diff --git a/DEPS b/DEPS index 5f96c95..4b582b4 100644 --- a/DEPS +++ b/DEPS
@@ -297,23 +297,23 @@ # 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': '0cc4b51ab9d5c72c052db4cf3782583df0dfa44e', + 'skia_revision': 'bfbc0c9abd675f4804b97000a93837b157b43754', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'f43f657e166b75db1f91f9f554ec8c474f87194b', + 'v8_revision': 'e5493ce937eafff2bf7c2091a89d5e64d04bf341', # 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': '8050079c116c993a5385737da12ad871c4f53fed', + 'angle_revision': '2c35135180285c722687620d9d7caa4e2bba6a03', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'ed9d5ae1e79c3ecc3bee8e07e852ba24868d27d5', + 'swiftshader_revision': 'aafa10869568fd3e84bfef1df2ed0e159b179dce', # 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': '962e4bff2fa741f0a8efc06fb84d55111bcc11b7', + 'pdfium_revision': '744e9f8b0f89481c4623b712f8ecdb02799ddcb5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -324,7 +324,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. - 'fuchsia_version': 'version:9.20220809.0.1', + 'fuchsia_version': 'version:9.20220809.1.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling google-toolbox-for-mac # and whatever else without interference from each other. @@ -376,7 +376,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': '50a84ca8e5b556e27bb285477f21a99f0ccb7050', + 'devtools_frontend_revision': '1f6ef0d58292665e06eded4059d8714a2e487e8a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -412,7 +412,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '1eed2c2e3638a0c2fbb9494549798cea096d15e5', + 'dawn_revision': '76eeb828c8fff6e83c099b6202d2585fa18fddfa', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -440,7 +440,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling nearby # and whatever else without interference from each other. - 'nearby_revision': '0a8f1f1c39af06dff550d8ca96c6e087994155b7', + 'nearby_revision': '55f20775d209871190580f0ef461ae1c816b1870', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling securemessage # and whatever else without interference from each other. @@ -1693,7 +1693,7 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@30403bafe4dc5b14e820ae922278455053e8f7e2', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c728ed48ce2fb0d011918eea7013f94d03c75bfe', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907', @@ -1805,7 +1805,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fe63cb6846edc3c548739c989e6a85a6fa568bde', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b77bd0ccfd4c6f0e69dbd465bd4032555d2447e6', 'condition': 'checkout_src_internal', },
diff --git a/ash/app_list/model/search/search_box_model.cc b/ash/app_list/model/search/search_box_model.cc index 9911203..e99b7daa 100644 --- a/ash/app_list/model/search/search_box_model.cc +++ b/ash/app_list/model/search/search_box_model.cc
@@ -32,26 +32,6 @@ observer.SearchEngineChanged(); } -void SearchBoxModel::Update(const std::u16string& text, - bool initiated_by_user) { - if (text_ == text) - return; - - if (initiated_by_user) { - if (text_.empty() && !text.empty()) { - UMA_HISTOGRAM_ENUMERATION("Apps.AppListSearchCommenced", 1, 2); - base::RecordAction(base::UserMetricsAction("AppList_EnterSearch")); - } else if (!text_.empty() && text.empty()) { - // The user ended a search interaction. Reset search start time. - base::RecordAction(base::UserMetricsAction("AppList_LeaveSearch")); - } - } - - text_ = text; - for (auto& observer : observers_) - observer.Update(); -} - void SearchBoxModel::AddObserver(SearchBoxModelObserver* observer) { observers_.AddObserver(observer); }
diff --git a/ash/app_list/model/search/search_box_model.h b/ash/app_list/model/search/search_box_model.h index 32e56b0..78d0cced4 100644 --- a/ash/app_list/model/search/search_box_model.h +++ b/ash/app_list/model/search/search_box_model.h
@@ -5,12 +5,8 @@ #ifndef ASH_APP_LIST_MODEL_SEARCH_SEARCH_BOX_MODEL_H_ #define ASH_APP_LIST_MODEL_SEARCH_SEARCH_BOX_MODEL_H_ -#include <memory> -#include <string> - #include "ash/app_list/model/app_list_model_export.h" #include "base/observer_list.h" -#include "ui/gfx/selection_model.h" namespace ash { @@ -31,16 +27,10 @@ void SetSearchEngineIsGoogle(bool is_google); bool search_engine_is_google() const { return search_engine_is_google_; } - // Sets/gets the text for the search box's Textfield and the voice search - // flag. - void Update(const std::u16string& text, bool initiated_by_user); - const std::u16string& text() const { return text_; } - void AddObserver(SearchBoxModelObserver* observer); void RemoveObserver(SearchBoxModelObserver* observer); private: - std::u16string text_; bool search_engine_is_google_ = false; bool show_assistant_button_ = false;
diff --git a/ash/app_list/model/search/search_box_model_observer.h b/ash/app_list/model/search/search_box_model_observer.h index 9434734..288d04c 100644 --- a/ash/app_list/model/search/search_box_model_observer.h +++ b/ash/app_list/model/search/search_box_model_observer.h
@@ -13,9 +13,6 @@ class APP_LIST_MODEL_EXPORT SearchBoxModelObserver : public base::CheckedObserver { public: - // Invoked when text or voice search flag is changed. - virtual void Update() = 0; - // Invoked when the search engine is changed. virtual void SearchEngineChanged() = 0;
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc index a0d0cac..43f4d40 100644 --- a/ash/app_list/views/app_list_bubble_view.cc +++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -614,6 +614,7 @@ void AppListBubbleView::QueryChanged(const std::u16string& trimmed_query, bool initiated_by_user) { if (current_page_ != AppListBubblePage::kNone) { + search_page_->search_view()->UpdateForNewSearch(!trimmed_query.empty()); if (!trimmed_query.empty()) ShowPage(AppListBubblePage::kSearch); else
diff --git a/ash/app_list/views/app_list_main_view.cc b/ash/app_list/views/app_list_main_view.cc index 87a1d40..778e189 100644 --- a/ash/app_list/views/app_list_main_view.cc +++ b/ash/app_list/views/app_list_main_view.cc
@@ -130,6 +130,7 @@ initiated_by_user); contents_view_->ShowSearchResults(search_box_view_->is_search_box_active() || !trimmed_query.empty()); + contents_view_->search_result_page_view()->UpdateForNewSearch(); } void AppListMainView::ActiveChanged(SearchBoxViewBase* sender) { @@ -140,17 +141,13 @@ if (search_box_view_->is_search_box_active()) { // Show zero state suggestions when search box is activated with an empty // query. - SearchModel* const search_model = - AppListModelProvider::Get()->search_model(); - const std::u16string raw_query = search_model->search_box()->text(); - std::u16string query; - base::TrimWhitespace(raw_query, base::TRIM_ALL, &query); + const bool is_query_empty = sender->IsSearchBoxTrimmedQueryEmpty(); if (features::IsProductivityLauncherEnabled()) { app_list_view_->SetStateFromSearchBoxView( - query.empty(), true /*triggered_by_contents_change*/); + is_query_empty, true /*triggered_by_contents_change*/); contents_view_->ShowSearchResults(true); } else { - if (query.empty()) + if (is_query_empty) search_box_view_->ShowZeroStateSuggestions(); } } else {
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc index c4b63963..8216c65 100644 --- a/ash/app_list/views/app_list_view_unittest.cc +++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -15,7 +15,6 @@ #include "ash/app_list/app_list_test_view_delegate.h" #include "ash/app_list/model/app_list_test_model.h" -#include "ash/app_list/model/search/search_box_model.h" #include "ash/app_list/model/search/test_search_result.h" #include "ash/app_list/views/app_list_folder_view.h" #include "ash/app_list/views/app_list_item_view.h" @@ -2571,8 +2570,8 @@ search_text, ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText); // Check that the current search is using |search_text|. - EXPECT_EQ(search_text, GetSearchModel()->search_box()->text()); EXPECT_EQ(search_text, main_view->search_box_view()->search_box()->GetText()); + EXPECT_EQ(search_text, main_view->search_box_view()->current_query()); contents_view->Layout(); EXPECT_TRUE( contents_view->IsStateActive(ash::AppListState::kStateSearchResults)); @@ -2592,9 +2591,9 @@ new_search_text, ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText); // Check that the current search is using |new_search_text|. - EXPECT_EQ(new_search_text, GetSearchModel()->search_box()->text()); EXPECT_EQ(new_search_text, main_view->search_box_view()->search_box()->GetText()); + EXPECT_EQ(search_text, main_view->search_box_view()->current_query()); contents_view->Layout(); EXPECT_TRUE(IsStateShown(ash::AppListState::kStateSearchResults)); EXPECT_TRUE(CheckSearchBoxWidget(contents_view->GetSearchBoxBounds(
diff --git a/ash/app_list/views/productivity_launcher_search_view.cc b/ash/app_list/views/productivity_launcher_search_view.cc index d24242d5..bc3abaa61 100644 --- a/ash/app_list/views/productivity_launcher_search_view.cc +++ b/ash/app_list/views/productivity_launcher_search_view.cc
@@ -145,8 +145,6 @@ AppListModelProvider* const model_provider = AppListModelProvider::Get(); model_provider->AddObserver(this); - search_box_model_observer_.Observe( - model_provider->search_model()->search_box()); } ProductivityLauncherSearchView::~ProductivityLauncherSearchView() { @@ -268,8 +266,7 @@ node_data->role = ax::mojom::Role::kListBox; std::u16string value; - std::u16string query = - AppListModelProvider::Get()->search_model()->search_box()->text(); + const std::u16string& query = search_box_view_->current_query(); if (!query.empty()) { if (last_search_result_count_ == 1) { value = l10n_util::GetStringFUTF16( @@ -295,26 +292,24 @@ SearchModel* search_model) { for (auto* container : result_container_views_) container->SetResults(search_model->results()); - search_box_model_observer_.Reset(); - search_box_model_observer_.Observe(search_model->search_box()); } -void ProductivityLauncherSearchView::Update() { +void ProductivityLauncherSearchView::UpdateForNewSearch(bool search_active) { if (app_list_features::IsDynamicSearchUpdateAnimationEnabled()) { - // Scan result_container_views_ to see if there are any in progress - // animations when the search model is updated. - for (SearchResultContainerView* view : result_container_views_) { - if (view->HasAnimatingChildView()) { - search_result_fast_update_time_ = base::TimeTicks::Now(); + if (search_active) { + // Scan result_container_views_ to see if there are any in progress + // animations when the search model is updated. + for (SearchResultContainerView* view : result_container_views_) { + if (view->HasAnimatingChildView()) { + search_result_fast_update_time_ = base::TimeTicks::Now(); + } } + } else { + search_result_fast_update_time_.reset(); } } } -void ProductivityLauncherSearchView::SearchEngineChanged() {} - -void ProductivityLauncherSearchView::ShowAssistantChanged() {} - void ProductivityLauncherSearchView::OnSelectedResultChanged() { if (!result_selection_controller_->selected_result()) { return;
diff --git a/ash/app_list/views/productivity_launcher_search_view.h b/ash/app_list/views/productivity_launcher_search_view.h index ecad3cb..a392464 100644 --- a/ash/app_list/views/productivity_launcher_search_view.h +++ b/ash/app_list/views/productivity_launcher_search_view.h
@@ -10,8 +10,6 @@ #include <vector> #include "ash/app_list/app_list_model_provider.h" -#include "ash/app_list/model/search/search_box_model.h" -#include "ash/app_list/model/search/search_box_model_observer.h" #include "ash/app_list/views/search_result_container_view.h" #include "ash/ash_export.h" #include "base/time/time.h" @@ -32,8 +30,7 @@ class ASH_EXPORT ProductivityLauncherSearchView : public views::View, public SearchResultContainerView::Delegate, - public AppListModelProvider::Observer, - public SearchBoxModelObserver { + public AppListModelProvider::Observer { public: METADATA_HEADER(ProductivityLauncherSearchView); @@ -58,10 +55,11 @@ void OnActiveAppListModelsChanged(AppListModel* model, SearchModel* search_model) override; - // Overridden from SearchBoxModelObserver: - void Update() override; - void SearchEngineChanged() override; - void ShowAssistantChanged() override; + // Called when the app list search query changes and new search is about to + // start or cleared. + // `search_active` - whether search update will result in a new search. This + // will be false when the search is about to be cleared using an empty query. + void UpdateForNewSearch(bool search_active); // Returns true if there are search results that can be keyboard selected. bool CanSelectSearchResults(); @@ -144,9 +142,6 @@ // The last reported number of search results shown by all containers. int last_search_result_count_ = 0; - - base::ScopedObservation<SearchBoxModel, SearchBoxModelObserver> - search_box_model_observer_{this}; }; } // namespace ash
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc index 8d71383..c4bedc8d 100644 --- a/ash/app_list/views/search_box_view.cc +++ b/ash/app_list/views/search_box_view.cc
@@ -368,6 +368,7 @@ // Set 'user_initiated_model_update_time_' when initiating a new query. user_initiated_model_update_time_ = current_time; } else if (!current_query_.empty() && query.empty()) { + base::RecordAction(base::UserMetricsAction("AppList_LeaveSearch")); // Reset 'user_initiated_model_update_time_' when clearing the search_box. user_initiated_model_update_time_ = base::TimeTicks(); } else if (query != current_query_ && @@ -395,15 +396,6 @@ if (query_empty_changed) SchedulePaint(); - // Temporarily remove from observer to ignore notifications caused by us. - search_box_model_observer_.Reset(); - - SearchBoxModel* const search_box_model = - AppListModelProvider::Get()->search_model()->search_box(); - - search_box_model->Update(query, initiated_by_user); - search_box_model_observer_.Observe(search_box_model); - delegate_->QueryChanged(trimmed_query, initiated_by_user); // Don't reinitiate zero state search if the previous query was already empty @@ -1251,16 +1243,6 @@ } } -void SearchBoxView::Update() { - const std::u16string text = - AppListModelProvider::Get()->search_model()->search_box()->text(); - search_box()->SetText(text); - UpdateButtonsVisibility(); - std::u16string trimmed_text; - base::TrimWhitespace(text, base::TrimPositions::TRIM_ALL, &trimmed_text); - delegate_->QueryChanged(trimmed_text, false); -} - void SearchBoxView::SearchEngineChanged() { UpdateSearchIcon(); }
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h index 41d56dce..836b270a 100644 --- a/ash/app_list/views/search_box_view.h +++ b/ash/app_list/views/search_box_view.h
@@ -165,6 +165,8 @@ highlight_range_ = range; } + const std::u16string& current_query() const { return current_query_; } + // Update search box view background when result container visibility changes. void OnResultContainerVisibilityChanged(bool visible); @@ -218,7 +220,6 @@ const ui::GestureEvent& gesture_event) override; // Overridden from SearchBoxModelObserver: - void Update() override; void SearchEngineChanged() override; void ShowAssistantChanged() override;
diff --git a/ash/app_list/views/search_box_view_delegate.h b/ash/app_list/views/search_box_view_delegate.h index f41da54..750a5fd 100644 --- a/ash/app_list/views/search_box_view_delegate.h +++ b/ash/app_list/views/search_box_view_delegate.h
@@ -17,7 +17,8 @@ class SearchBoxViewDelegate { public: - // Invoked when query text in the search box changes. + // Invoked when query text in the search box changes, just before initiating + // search request for the query. // `trimmed_query` - the search boxt textfiled contents with whitespace // trimmed (which will generally match the query sent to search providers). // `initiated_by_user` - whether the query changed as a result of user input
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc index c85d3fd..1f7ab64 100644 --- a/ash/app_list/views/search_box_view_unittest.cc +++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -1,4 +1,3 @@ - // Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,7 +21,9 @@ #include "ash/app_list/views/productivity_launcher_search_view.h" #include "ash/app_list/views/result_selection_controller.h" #include "ash/app_list/views/search_box_view_delegate.h" +#include "ash/app_list/views/search_result_list_view.h" #include "ash/app_list/views/search_result_page_view.h" +#include "ash/app_list/views/search_result_tile_item_list_view.h" #include "ash/constants/ash_features.h" #include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_features.h" @@ -139,32 +140,21 @@ // Initialize SearchBoxView like clamshell productivity launcher. view = std::make_unique<SearchBoxView>(this, &view_delegate_, /*app_list_view=*/nullptr); - } else { - // Initialize SearchBoxView like peeking launcher. - app_list_view_ = new AppListView(&view_delegate_); - app_list_view_->InitView(GetContext()); - view = std::make_unique<SearchBoxView>(this, &view_delegate_, - app_list_view_); - } - - if (IsProductivityLauncherEnabled()) view->InitializeForBubbleLauncher(); - else - view->InitializeForFullscreenLauncher(); - view_ = widget_->GetContentsView()->AddChildView(std::move(view)); + view_ = widget_->GetContentsView()->AddChildView(std::move(view)); - if (IsProductivityLauncherEnabled()) { productivity_launcher_search_view_ = widget_->GetContentsView()->AddChildView( std::make_unique<ProductivityLauncherSearchView>( &view_delegate_, /*dialog_controller=*/nullptr, view_)); + widget_->Show(); } else { - counter_view_ = widget_->GetContentsView()->AddChildView( - std::make_unique<KeyPressCounterView>(app_list_view_)); - counter_view_->Init(); - SetContentsView(counter_view_); + // Initialize SearchBoxView like peeking launcher. + app_list_view_ = new AppListView(&view_delegate_); + app_list_view_->InitView(GetContext()); + view_ = app_list_view_->search_box_view(); + app_list_view_->Show(AppListViewState::kPeeking, false); } - widget_->Show(); } void TearDown() override { @@ -238,16 +228,20 @@ SearchModel::SearchResults* results() { return GetSearchModel()->results(); } + SearchResultPageView* GetSearchResultPageView() { + DCHECK(!IsProductivityLauncherEnabled()); + return app_list_view() + ->app_list_main_view() + ->contents_view() + ->search_result_page_view(); + } SearchResultBaseView* GetFirstResultView() { if (IsProductivityLauncherEnabled()) { return productivity_launcher_search_view_ ->result_container_views_for_test()[kBestMatchIndex] ->GetFirstResultView(); } - return view() - ->contents_view() - ->search_result_page_view() - ->first_result_view(); + return GetSearchResultPageView()->first_result_view(); } ResultSelectionController* GetResultSelectionController() { @@ -255,32 +249,32 @@ return productivity_launcher_search_view_ ->result_selection_controller_for_test(); } - return view() - ->contents_view() - ->search_result_page_view() - ->result_selection_controller(); + return GetSearchResultPageView()->result_selection_controller(); } void OnSearchResultContainerResultsChanged() { if (IsProductivityLauncherEnabled()) { productivity_launcher_search_view_ ->OnSearchResultContainerResultsChanged(); + } else { + return GetSearchResultPageView()->OnSearchResultContainerResultsChanged(); } - view() - ->contents_view() - ->search_result_page_view() - ->OnSearchResultContainerResultsChanged(); } void SimulateQuery(const std::u16string& query) { view()->search_box()->InsertText( - u"test", + query, ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText); } // Overridden from SearchBoxViewDelegate: void QueryChanged(const std::u16string& trimmed_query, - bool initiated_by_user) override {} + bool initiated_by_user) override { + if (IsProductivityLauncherEnabled()) { + productivity_launcher_search_view_->UpdateForNewSearch( + !trimmed_query.empty()); + } + } void AssistantButtonPressed() override {} void CloseButtonPressed() override {} void ActiveChanged(SearchBoxViewBase* sender) override {} @@ -302,6 +296,18 @@ // Run search box view tests with and without productivity launcher enabled. INSTANTIATE_TEST_SUITE_P(All, SearchBoxViewTest, testing::Bool()); +class SearchBoxViewWithSuggestedContentTest : public SearchBoxViewTest { + public: + void SetUp() override { + view_delegate_.SetShouldShowSuggestedContentInfo(true); + SearchBoxViewTest::SetUp(); + } +}; + +INSTANTIATE_TEST_SUITE_P(All, + SearchBoxViewWithSuggestedContentTest, + testing::Values(false)); + TEST_P(SearchBoxViewTest, SearchBoxTextUsesAppListSearchBoxTextColor) { EXPECT_EQ(view()->search_box()->GetTextColor(), AppListColorProvider::Get()->GetSearchBoxTextColor( @@ -424,6 +430,7 @@ const SearchResultBaseView* selection = GetResultSelectionController()->selected_result(); + ASSERT_TRUE(selection); EXPECT_EQ(GetFirstResultView(), selection); ASSERT_TRUE(selection->result()); EXPECT_EQ(u"tester", selection->result()->title()); @@ -715,20 +722,9 @@ EXPECT_EQ(2, user_action_tester.GetActionCount("AppList_SearchQueryStarted")); } -TEST_P(SearchBoxViewTest, NavigateSuggestedContentInfo) { - // PrivacyContainerViews are deprecated for productivity launcher. - if (IsProductivityLauncherEnabled()) - return; - // Create a new contents view that contains a suggested content info. - view_delegate()->SetShouldShowSuggestedContentInfo(true); - auto* contents_view = widget()->GetContentsView()->AddChildView( - std::make_unique<KeyPressCounterView>(app_list_view())); - contents_view->Init(); - SetContentsView(contents_view); - +TEST_P(SearchBoxViewWithSuggestedContentTest, NavigateSuggestedContentInfo) { PrivacyContainerView* const privacy_container_view = - contents_view->search_result_page_view() - ->GetPrivacyContainerViewForTest(); + GetSearchResultPageView()->GetPrivacyContainerViewForTest(); ASSERT_TRUE(privacy_container_view); // Set up the search box. @@ -771,20 +767,10 @@ EXPECT_FALSE(selection); } -TEST_P(SearchBoxViewTest, KeyboardEventClosesSuggestedContentInfo) { - // PrivacyContainerViews are deprecated for productivity launcher. - if (IsProductivityLauncherEnabled()) - return; - // Create a new contents view that contains a suggested content info. - view_delegate()->SetShouldShowSuggestedContentInfo(true); - auto* contents_view = widget()->GetContentsView()->AddChildView( - std::make_unique<KeyPressCounterView>(app_list_view())); - contents_view->Init(); - SetContentsView(contents_view); - +TEST_P(SearchBoxViewWithSuggestedContentTest, + KeyboardEventClosesSuggestedContentInfo) { PrivacyContainerView* const privacy_container_view = - contents_view->search_result_page_view() - ->GetPrivacyContainerViewForTest(); + GetSearchResultPageView()->GetPrivacyContainerViewForTest(); ASSERT_TRUE(privacy_container_view); // Set up the search box. @@ -793,7 +779,7 @@ std::u16string()); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(contents_view->search_result_page_view() + EXPECT_EQ(GetSearchResultPageView() ->result_selection_controller() ->selected_result(), privacy_container_view->GetResultViewAt(0)); @@ -805,20 +791,10 @@ EXPECT_FALSE(view_delegate()->ShouldShowSuggestedContentInfo()); } -TEST_P(SearchBoxViewTest, SuggestedContentActionNotOverriddenByNewResults) { - // PrivacyContainerView is being deprecated for Productivity Launcher. - if (IsProductivityLauncherEnabled()) - return; - // Create a new contents view that contains a privacy notice. - view_delegate()->SetShouldShowSuggestedContentInfo(true); - auto* contents_view = widget()->GetContentsView()->AddChildView( - std::make_unique<KeyPressCounterView>(app_list_view())); - contents_view->Init(); - SetContentsView(contents_view); - +TEST_P(SearchBoxViewWithSuggestedContentTest, + SuggestedContentActionNotOverriddenByNewResults) { PrivacyContainerView* const privacy_container_view = - contents_view->search_result_page_view() - ->GetPrivacyContainerViewForTest(); + GetSearchResultPageView()->GetPrivacyContainerViewForTest(); ASSERT_TRUE(privacy_container_view); // Set up the search box. @@ -828,7 +804,7 @@ base::RunLoop().RunUntilIdle(); ResultSelectionController* const selection_controller = - contents_view->search_result_page_view()->result_selection_controller(); + GetResultSelectionController(); const SearchResultBaseView* selection = selection_controller->selected_result(); EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0)); @@ -851,20 +827,10 @@ EXPECT_EQ(selection->result()->title(), u"test"); } -TEST_P(SearchBoxViewTest, SuggestedContentSelectionDoesNotChangeSearchBoxText) { - // PrivacyContainerView is being deprecated for Productivity Launcher. - if (IsProductivityLauncherEnabled()) - return; - // Create a new contents view that contains a suggested content info. - view_delegate()->SetShouldShowSuggestedContentInfo(true); - auto* contents_view = widget()->GetContentsView()->AddChildView( - std::make_unique<KeyPressCounterView>(app_list_view())); - contents_view->Init(); - SetContentsView(contents_view); - +TEST_P(SearchBoxViewWithSuggestedContentTest, + SuggestedContentSelectionDoesNotChangeSearchBoxText) { PrivacyContainerView* const privacy_container_view = - contents_view->search_result_page_view() - ->GetPrivacyContainerViewForTest(); + GetSearchResultPageView()->GetPrivacyContainerViewForTest(); ASSERT_TRUE(privacy_container_view); // Set up the search box. @@ -874,7 +840,7 @@ base::RunLoop().RunUntilIdle(); ResultSelectionController* const selection_controller = - contents_view->search_result_page_view()->result_selection_controller(); + GetResultSelectionController(); EXPECT_EQ(selection_controller->selected_result(), privacy_container_view->GetResultViewAt(0)); EXPECT_TRUE(view()->search_box()->GetText().empty()); @@ -949,14 +915,13 @@ // Sets up the test by creating a SearchResult and displaying an autocomplete // suggestion. void SetupAutocompleteBehaviorTest() { + // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. + KeyPress(ui::VKEY_H); + KeyPress(ui::VKEY_E); // Add a search result with a non-empty title field. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello world!", std::u16string()); base::RunLoop().RunUntilIdle(); - - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); } }; @@ -972,6 +937,9 @@ // Search result tile tests are not relevant for productivity launcher. if (IsProductivityLauncherEnabled()) return; + + SimulateQuery(u"he"); + // Add two SearchResults, one tile and one list result. Initialize their title // field to a non-empty string. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello list", @@ -980,11 +948,7 @@ std::u16string()); base::RunLoop().RunUntilIdle(); - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); - EXPECT_EQ(view()->search_box()->GetText(), u"hello tile"); EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile"); } @@ -996,6 +960,9 @@ // Search result tile tests are not relevant for productivity launcher. if (IsProductivityLauncherEnabled()) return; + + SimulateQuery(u"he"); + // Add two SearchResults, one tile and one list result. Initialize their title // field to a non-empty string. CreateSearchResult(ash::SearchResultDisplayType::kTile, 1.0, u"hello tile", @@ -1004,9 +971,6 @@ std::u16string()); base::RunLoop().RunUntilIdle(); - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); EXPECT_EQ(view()->search_box()->GetText(), u"hello tile"); EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile"); @@ -1019,6 +983,9 @@ // Search result tile tests are not relevant for productivity launcher. if (IsProductivityLauncherEnabled()) return; + + SimulateQuery(u"he"); + // Add two SearchResults, one tile and one list result. The tile should // display first, despite having a lower score. Initialize their details field // to a non-empty string. @@ -1028,9 +995,6 @@ u"hello tile"); base::RunLoop().RunUntilIdle(); - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); EXPECT_EQ(view()->search_box()->GetText(), u"hello tile"); EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile"); @@ -1043,6 +1007,9 @@ // Search result tile tests are not relevant for productivity launcher. if (IsProductivityLauncherEnabled()) return; + + SimulateQuery(u"he"); + // Add two SearchResults, one tile and one list result. Initialize their // details field to a non-empty string. CreateSearchResult(ash::SearchResultDisplayType::kTile, 1.0, std::u16string(), @@ -1051,9 +1018,6 @@ u"hello list"); base::RunLoop().RunUntilIdle(); - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); EXPECT_EQ(view()->search_box()->GetText(), u"hello tile"); EXPECT_EQ(view()->search_box()->GetSelectedText(), u"llo tile"); @@ -1063,13 +1027,12 @@ // result title or details do not have a matching prefix. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxDoesNotAutocompleteWrongCharacter) { + // Send Z to the SearchBoxView textfield, then trigger an autocomplete. + KeyPress(ui::VKEY_Z); // Add a search result with non-empty details and title fields. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"title", u"details"); base::RunLoop().RunUntilIdle(); - - // Send Z to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_Z); ProcessAutocomplete(); // The text should not be autocompleted. EXPECT_EQ(view()->search_box()->GetText(), u"z"); @@ -1078,14 +1041,11 @@ // Tests that autocomplete suggestion will remain if next key in the suggestion // is typed. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesAcceptsNextChar) { + SimulateQuery(u"he"); // Add a search result with a non-empty title field. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello world!", std::u16string()); base::RunLoop().RunUntilIdle(); - - // Send H, E to the SearchBoxView textfield, then trigger an autocomplete. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); ProcessAutocomplete(); // After typing L, the highlighted text will be replaced by L. @@ -1118,7 +1078,7 @@ // Search box autocomplete suggestion is accepted, but it should not // trigger another query, thus it is not reflected in Search Model. EXPECT_EQ(u"hello world!", view()->search_box()->GetText()); - EXPECT_EQ(u"he", GetSearchModel()->search_box()->text()); + EXPECT_EQ(u"he", view()->current_query()); } TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAcceptsAutocompleteForTap) { @@ -1137,20 +1097,20 @@ // Search box autocomplete suggestion is accepted, but it should not // trigger another query, thus it is not reflected in Search Model. EXPECT_EQ(u"hello world!", view()->search_box()->GetText()); - EXPECT_EQ(u"he", GetSearchModel()->search_box()->text()); + EXPECT_EQ(u"he", view()->current_query()); } // Tests that autocomplete is not handled if IME is using composition text. TEST_P(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesNotHandledForIME) { + // Simulate uncomposited text. The autocomplete should be handled. + KeyPress(ui::VKEY_H); + KeyPress(ui::VKEY_E); + view()->set_highlight_range_for_test(gfx::Range(2, 2)); // Add a search result with a non-empty title field. CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0, u"hello world!", std::u16string()); base::RunLoop().RunUntilIdle(); - // Simulate uncomposited text. The autocomplete should be handled. - KeyPress(ui::VKEY_H); - KeyPress(ui::VKEY_E); - view()->set_highlight_range_for_test(gfx::Range(2, 2)); ProcessAutocomplete(); std::u16string selected_text = view()->search_box()->GetSelectedText();
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc index 47deac9..1535e9b 100644 --- a/ash/app_list/views/search_result_page_view.cc +++ b/ash/app_list/views/search_result_page_view.cc
@@ -251,7 +251,6 @@ AppListModelProvider* const model_provider = AppListModelProvider::Get(); model_provider->AddObserver(this); - search_box_observation_.Observe(model_provider->search_model()->search_box()); } SearchResultPageView::~SearchResultPageView() { @@ -369,8 +368,8 @@ node_data->role = ax::mojom::Role::kListBox; std::u16string value; - std::u16string query = - AppListModelProvider::Get()->search_model()->search_box()->text(); + SearchBoxView* search_box = AppListPage::contents_view()->GetSearchBoxView(); + const std::u16string& query = search_box->current_query(); if (!query.empty()) { if (last_search_result_count_ == 1) { value = l10n_util::GetStringFUTF16( @@ -402,6 +401,14 @@ AppListPage::OnThemeChanged(); } +void SearchResultPageView::UpdateForNewSearch() { + notify_a11y_results_changed_timer_.Stop(); + if (productivity_launcher_search_view_) { + productivity_launcher_search_view_->UpdateForNewSearch( + ShouldShowSearchResultView()); + } +} + void SearchResultPageView::UpdateResultContainersVisibility() { bool should_show_page_view = false; if (features::IsProductivityLauncherEnabled()) { @@ -683,8 +690,6 @@ void SearchResultPageView::OnActiveAppListModelsChanged( AppListModel* model, SearchModel* search_model) { - search_box_observation_.Reset(); - search_box_observation_.Observe(search_model->search_box()); for (auto* container : result_container_views_) container->SetResults(search_model->results()); } @@ -742,14 +747,6 @@ first_result_view_); } -void SearchResultPageView::Update() { - notify_a11y_results_changed_timer_.Stop(); -} - -void SearchResultPageView::SearchEngineChanged() {} - -void SearchResultPageView::ShowAssistantChanged() {} - bool SearchResultPageView::CanSelectSearchResults() const { if (!GetVisible()) return false; @@ -781,11 +778,9 @@ } bool SearchResultPageView::ShouldShowSearchResultView() const { - SearchModel* search_model = AppListModelProvider::Get()->search_model(); + SearchBoxView* search_box = AppListPage::contents_view()->GetSearchBoxView(); return (!features::IsProductivityLauncherEnabled() || - !base::TrimWhitespace(search_model->search_box()->text(), - base::TrimPositions::TRIM_ALL) - .empty()); + search_box->HasValidQuery()); } void SearchResultPageView::OnHidden() {
diff --git a/ash/app_list/views/search_result_page_view.h b/ash/app_list/views/search_result_page_view.h index 16abd7df..3d93bba 100644 --- a/ash/app_list/views/search_result_page_view.h +++ b/ash/app_list/views/search_result_page_view.h
@@ -11,8 +11,6 @@ #include "ash/app_list/app_list_model_provider.h" #include "ash/app_list/model/app_list_model.h" -#include "ash/app_list/model/search/search_box_model.h" -#include "ash/app_list/model/search/search_box_model_observer.h" #include "ash/app_list/views/app_list_page.h" #include "ash/app_list/views/result_selection_controller.h" #include "ash/app_list/views/search_result_container_view.h" @@ -37,8 +35,7 @@ class ASH_EXPORT SearchResultPageView : public AppListPage, public AppListModelProvider::Observer, - public SearchResultContainerView::Delegate, - public SearchBoxModelObserver { + public SearchResultContainerView::Delegate { public: SearchResultPageView(); @@ -96,11 +93,6 @@ void OnSearchResultContainerResultsChanging() override; void OnSearchResultContainerResultsChanged() override; - // Overridden from SearchBoxModelObserver: - void Update() override; - void SearchEngineChanged() override; - void ShowAssistantChanged() override; - // Whether any results are available for selection within the search result // UI. bool CanSelectSearchResults() const; @@ -126,6 +118,10 @@ // Hide zero state search result view when ProductivityLauncher is enabled. bool ShouldShowSearchResultView() const; + // Called when the app list search query changes and new search is about to + // start or cleared. + void UpdateForNewSearch(); + // Sets visibility of result container and separator views so only containers // that contain some results are shown. void UpdateResultContainersVisibility(); @@ -248,9 +244,6 @@ // The controller that manages dialogs modal to the search results page. std::unique_ptr<SearchResultPageDialogController> dialog_controller_; - - base::ScopedObservation<SearchBoxModel, SearchBoxModelObserver> - search_box_observation_{this}; }; } // namespace ash
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc index bb86572a..51b37511 100644 --- a/ash/ash_prefs.cc +++ b/ash/ash_prefs.cc
@@ -60,6 +60,7 @@ #include "ash/wm/window_cycle/window_cycle_controller.h" #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h" #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h" +#include "chromeos/ui/wm/fullscreen/pref_names.h" #include "components/language/core/browser/pref_names.h" #include "components/live_caption/pref_names.h" #include "components/soda/constants.h" @@ -128,6 +129,8 @@ registry->RegisterBooleanPref(chromeos::prefs::kSuggestedContentEnabled, true); registry->RegisterBooleanPref(::prefs::kLiveCaptionEnabled, false); + registry->RegisterListPref( + chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList); registry->RegisterStringPref(::prefs::kLiveCaptionLanguageCode, speech::kUsEnglishLocale); registry->RegisterStringPref(language::prefs::kApplicationLocale,
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc index 098ebf7..f6aee65 100644 --- a/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc +++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -123,32 +123,30 @@ UpdateCreationDate(base::Time::Now()); } -ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo(const base::Value* value, - bool is_last) +ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo( + const base::Value::Dict& dict, + bool is_last) : is_last_(is_last) { - const base::DictionaryValue* dict; - if (!value || !value->GetAsDictionary(&dict) || !dict) - return; { - auto* found = dict->FindStringPath(kOsVersion); + auto* found = dict.FindString(kOsVersion); if (found) os_version_ = *found; } { - auto* found = dict->FindPath(kCreationDate); + auto* found = dict.Find(kCreationDate); if (found && base::ValueToTime(found).has_value()) { auto parsed_time = base::ValueToTime(found).value(); UpdateCreationDate(parsed_time); } } { - auto found = dict->FindBoolPath(kVerified); + auto found = dict.FindBool(kVerified); if (found.has_value()) verified_ = found.value(); } { - auto found = dict->FindBoolPath(kUpdated); + auto found = dict.FindBool(kUpdated); if (found.has_value()) updated_ = found.value(); } @@ -168,17 +166,14 @@ os_version, creation_date, verified, updated, is_last)); } -void ArcDataSnapshotdManager::SnapshotInfo::Sync(base::Value* dict) { - if (!dict) - return; +void ArcDataSnapshotdManager::SnapshotInfo::Sync(base::Value::Dict& dict) { + base::Value::Dict value; + value.Set(kOsVersion, os_version_); + value.Set(kCreationDate, base::TimeToValue(creation_date_)); + value.Set(kVerified, verified_); + value.Set(kUpdated, updated_); - base::DictionaryValue value; - value.SetStringKey(kOsVersion, os_version_); - value.SetKey(kCreationDate, base::TimeToValue(creation_date_)); - value.SetBoolKey(kVerified, verified_); - value.SetBoolKey(kUpdated, updated_); - - dict->SetKey(GetDictPath(), std::move(value)); + dict.Set(GetDictPath(), std::move(value)); } bool ArcDataSnapshotdManager::SnapshotInfo::IsExpired() const { @@ -257,41 +252,39 @@ } void ArcDataSnapshotdManager::Snapshot::Parse() { - const base::Value* dict = - local_state_->GetDictionary(arc::prefs::kArcSnapshotInfo); - if (!dict) - return; + const base::Value::Dict& dict = + local_state_->GetValueDict(arc::prefs::kArcSnapshotInfo); { - const auto* found = dict->FindDictPath(kPrevious); + const auto* found = dict.FindDict(kPrevious); if (found) - previous_snapshot_ = std::make_unique<SnapshotInfo>(found, false); + previous_snapshot_ = std::make_unique<SnapshotInfo>(*found, false); } { - const auto* found = dict->FindDictPath(kLast); + const auto* found = dict.FindDict(kLast); if (found) - last_snapshot_ = std::make_unique<SnapshotInfo>(found, true); + last_snapshot_ = std::make_unique<SnapshotInfo>(*found, true); } { - auto found = dict->FindBoolPath(kBlockedUiReboot); + auto found = dict.FindBool(kBlockedUiReboot); if (found.has_value()) blocked_ui_mode_ = found.value(); } { - auto found = dict->FindBoolPath(kStarted); + auto found = dict.FindBool(kStarted); if (found.has_value()) started_ = found.value(); } } void ArcDataSnapshotdManager::Snapshot::Sync() { - base::DictionaryValue dict; + base::Value::Dict dict; if (previous_snapshot_) - previous_snapshot_->Sync(&dict); + previous_snapshot_->Sync(dict); if (last_snapshot_) - last_snapshot_->Sync(&dict); - dict.SetBoolKey(kBlockedUiReboot, blocked_ui_mode_); - dict.SetBoolKey(kStarted, started_); - local_state_->Set(arc::prefs::kArcSnapshotInfo, std::move(dict)); + last_snapshot_->Sync(dict); + dict.Set(kBlockedUiReboot, blocked_ui_mode_); + dict.Set(kStarted, started_); + local_state_->SetDict(arc::prefs::kArcSnapshotInfo, std::move(dict)); } void ArcDataSnapshotdManager::Snapshot::Sync(base::OnceClosure callback) {
diff --git a/ash/components/arc/enterprise/arc_data_snapshotd_manager.h b/ash/components/arc/enterprise/arc_data_snapshotd_manager.h index c13fee8..8db2c04 100644 --- a/ash/components/arc/enterprise/arc_data_snapshotd_manager.h +++ b/ash/components/arc/enterprise/arc_data_snapshotd_manager.h
@@ -104,7 +104,7 @@ public: // Creates new snapshot with current parameters. explicit SnapshotInfo(bool is_last); - SnapshotInfo(const base::Value* value, bool is_last); + SnapshotInfo(const base::Value::Dict& value, bool is_last); SnapshotInfo(const SnapshotInfo&) = delete; SnapshotInfo& operator=(const SnapshotInfo&) = delete; ~SnapshotInfo(); @@ -119,7 +119,7 @@ bool is_last); // Syncs stored snapshot info to dictionaty |value|. - void Sync(base::Value* value); + void Sync(base::Value::Dict& value); // Returns true if snapshot is expired. bool IsExpired() const;
diff --git a/ash/components/arc/metrics/stability_metrics_manager.cc b/ash/components/arc/metrics/stability_metrics_manager.cc index 39d63df3..eb2bddc4 100644 --- a/ash/components/arc/metrics/stability_metrics_manager.cc +++ b/ash/components/arc/metrics/stability_metrics_manager.cc
@@ -47,11 +47,12 @@ void StabilityMetricsManager::RecordMetricsToUMA() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // GetDictionary() should never return null, but since this may be called - // early on browser startup, be paranoid here to prevent going into a crash - // loop. - if (!local_state_->GetDictionary(prefs::kStabilityMetrics)) { - NOTREACHED() << "Local state unavailable, not recording stabiltiy metrics."; + // FindPreference(prefs::kStabilityMetrics) should never return null, but + // since this may be called early on browser startup, be paranoid here to + // prevent going into a crash loop. + if (const auto* pref = local_state_->FindPreference(prefs::kStabilityMetrics); + !pref || pref->GetType() != base::Value::Type::DICT) { + NOTREACHED() << "Local state unavailable, not recording stability metrics."; return; }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index afeb36b..c2fc8e5 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -839,6 +839,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorUpdateIndexableTextEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsProjectorUseOAuthForGetVideoInfoEnabled(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQsRevampEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickDimEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickSettingsNetworkRevampEnabled();
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc index 68c49b07..29d9f53 100644 --- a/ash/constants/ash_pref_names.cc +++ b/ash/constants/ash_pref_names.cc
@@ -447,11 +447,6 @@ // A boolean pref that enable fullscreen alert bubble. // TODO(zxdan): Change to an allowlist in M89. const char kFullscreenAlertEnabled[] = "ash.fullscreen_alert_enabled"; -// A list of URLs that are allowed to continue full screen mode after session -// unlock without a notification. To prevent fake login screens, the device -// normally exits full screen mode before locking a session. -const char kKeepFullscreenWithoutNotificationUrlAllowList[] = - "ash.keep_fullscreen_without_notification_url_allow_list"; // A boolean pref storing whether the gesture education notification has ever // been shown to the user, which we use to stop showing it again.
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h index b3b2d4d..e336523 100644 --- a/ash/constants/ash_pref_names.h +++ b/ash/constants/ash_pref_names.h
@@ -214,8 +214,6 @@ extern const char kAllowMGSToStoreDisplayProperties[]; COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kFullscreenAlertEnabled[]; -COMPONENT_EXPORT(ASH_CONSTANTS) -extern const char kKeepFullscreenWithoutNotificationUrlAllowList[]; COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kGestureEducationNotificationShown[];
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc index 245fdce..b723907d 100644 --- a/ash/display/display_prefs.cc +++ b/ash/display/display_prefs.cc
@@ -72,13 +72,13 @@ // This kind of boilerplates should be done by base::JSONValueConverter but it // doesn't support classes like gfx::Insets for now. // TODO(mukai): fix base::JSONValueConverter and use it here. -bool ValueToInsets(const base::DictionaryValue& value, gfx::Insets* insets) { +bool ValueToInsets(const base::Value::Dict& dict, gfx::Insets* insets) { DCHECK(insets); - absl::optional<int> top = value.FindIntKey(kInsetsTopKey); - absl::optional<int> left = value.FindIntKey(kInsetsLeftKey); - absl::optional<int> bottom = value.FindIntKey(kInsetsBottomKey); - absl::optional<int> right = value.FindIntKey(kInsetsRightKey); + absl::optional<int> top = dict.FindInt(kInsetsTopKey); + absl::optional<int> left = dict.FindInt(kInsetsLeftKey); + absl::optional<int> bottom = dict.FindInt(kInsetsBottomKey); + absl::optional<int> right = dict.FindInt(kInsetsRightKey); if (top && left && bottom && right) { *insets = gfx::Insets::TLBR(*top, *left, *bottom, *right); return true; @@ -86,12 +86,11 @@ return false; } -void InsetsToValue(const gfx::Insets& insets, base::Value& value) { - DCHECK(value.is_dict()); - value.SetIntKey(kInsetsTopKey, insets.top()); - value.SetIntKey(kInsetsLeftKey, insets.left()); - value.SetIntKey(kInsetsBottomKey, insets.bottom()); - value.SetIntKey(kInsetsRightKey, insets.right()); +void InsetsToValue(const gfx::Insets& insets, base::Value::Dict& dict) { + dict.Set(kInsetsTopKey, insets.top()); + dict.Set(kInsetsLeftKey, insets.left()); + dict.Set(kInsetsBottomKey, insets.bottom()); + dict.Set(kInsetsRightKey, insets.right()); } // Unmarshalls the string containing CalibrationPointPairQuad and populates @@ -125,20 +124,20 @@ // Retrieves touch calibration associated data from the dictionary and stores // it in an instance of TouchCalibrationData struct. -bool ValueToTouchData(const base::DictionaryValue& value, +bool ValueToTouchData(const base::Value::Dict& dict, display::TouchCalibrationData* touch_calibration_data) { display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad = &(touch_calibration_data->point_pairs); - const std::string* str = value.FindStringKey(kTouchCalibrationPointPairs); + const std::string* str = dict.FindString(kTouchCalibrationPointPairs); if (!str) return false; if (!ParseTouchCalibrationStringValue(*str, point_pair_quad)) return false; - absl::optional<int> width = value.FindIntKey(kTouchCalibrationWidth); - absl::optional<int> height = value.FindIntKey(kTouchCalibrationHeight); + absl::optional<int> width = dict.FindInt(kTouchCalibrationWidth); + absl::optional<int> height = dict.FindInt(kTouchCalibrationHeight); if (!width || !height) { return false; } @@ -149,8 +148,7 @@ // Stores the touch calibration data into the dictionary. void TouchDataToValue( const display::TouchCalibrationData& touch_calibration_data, - base::Value& value) { - DCHECK(value.is_dict()); + base::Value::Dict& dict) { std::string str; for (std::size_t row = 0; row < touch_calibration_data.point_pairs.size(); row++) { @@ -168,11 +166,9 @@ if (row != touch_calibration_data.point_pairs.size() - 1) str += " "; } - value.SetStringKey(kTouchCalibrationPointPairs, str); - value.SetIntKey(kTouchCalibrationWidth, - touch_calibration_data.bounds.width()); - value.SetIntKey(kTouchCalibrationHeight, - touch_calibration_data.bounds.height()); + dict.Set(kTouchCalibrationPointPairs, str); + dict.Set(kTouchCalibrationWidth, touch_calibration_data.bounds.width()); + dict.Set(kTouchCalibrationHeight, touch_calibration_data.bounds.height()); } display::DisplayManager* GetDisplayManager() { @@ -225,8 +221,8 @@ void LoadDisplayProperties(PrefService* local_state) { for (const auto it : local_state->GetValueDict(prefs::kDisplayProperties)) { - const base::DictionaryValue* dict_value = nullptr; - if (!it.second.GetAsDictionary(&dict_value) || dict_value == nullptr) + const base::Value::Dict* dict_value = it.second.GetIfDict(); + if (!dict_value) continue; int64_t id = display::kInvalidDisplayId; if (!base::StringToInt64(it.first, &id) || @@ -236,18 +232,17 @@ const gfx::Insets* insets_to_set = nullptr; display::Display::Rotation rotation = display::Display::ROTATE_0; - if (absl::optional<int> rotation_value = - dict_value->FindIntKey("rotation")) { + if (absl::optional<int> rotation_value = dict_value->FindInt("rotation")) { rotation = static_cast<display::Display::Rotation>(*rotation_value); } - int width = dict_value->FindIntKey("width").value_or(0); - int height = dict_value->FindIntKey("height").value_or(0); + int width = dict_value->FindInt("width").value_or(0); + int height = dict_value->FindInt("height").value_or(0); gfx::Size resolution_in_pixels(width, height); float device_scale_factor = 1.0; if (absl::optional<int> dsf_value = - dict_value->FindIntKey("device-scale-factor")) { + dict_value->FindInt("device-scale-factor")) { device_scale_factor = static_cast<float>(*dsf_value) / 1000.0f; } @@ -258,9 +253,9 @@ bool is_interlaced = false; if (display::features::IsListAllDisplayModesEnabled()) { refresh_rate = - dict_value->FindDoubleKey("refresh-rate").value_or(refresh_rate); + dict_value->FindDouble("refresh-rate").value_or(refresh_rate); absl::optional<bool> is_interlaced_opt = - dict_value->FindBoolKey("interlaced"); + dict_value->FindBool("interlaced"); is_interlaced = is_interlaced_opt.value_or(false); } @@ -268,7 +263,7 @@ if (ValueToInsets(*dict_value, &insets)) insets_to_set = &insets; - double display_zoom = dict_value->FindDoubleKey(kDisplayZoom).value_or(1.0); + double display_zoom = dict_value->FindDouble(kDisplayZoom).value_or(1.0); GetDisplayManager()->RegisterDisplayProperty( id, rotation, insets_to_set, resolution_in_pixels, device_scale_factor, @@ -303,23 +298,22 @@ identifier, display::TouchDeviceManager::AssociationInfoMap()); if (!item.second.is_dict()) continue; - for (const auto association_info_item : item.second.DictItems()) { + for (const auto association_info_item : item.second.GetDict()) { display::TouchDeviceManager::TouchAssociationInfo info; int64_t display_id; if (!base::StringToInt64(association_info_item.first, &display_id)) continue; - auto* value = - association_info_item.second.FindKey(kTouchAssociationTimestamp); - if (!value->is_double()) + absl::optional<double> value = + association_info_item.second.GetDict().FindDouble( + kTouchAssociationTimestamp); + if (!value) continue; - info.timestamp = base::Time().FromDoubleT(value->GetDouble()); + info.timestamp = base::Time().FromDoubleT(*value); - value = association_info_item.second.FindKey( - kTouchAssociationCalibrationData); - if (!value->is_dict()) - continue; - const base::DictionaryValue* calibration_data_dict = nullptr; - if (!value->GetAsDictionary(&calibration_data_dict)) + const base::Value::Dict* calibration_data_dict = + association_info_item.second.GetDict().FindDict( + kTouchAssociationCalibrationData); + if (!calibration_data_dict) continue; ValueToTouchData(*calibration_data_dict, &info.calibration_data); touch_associations.at(identifier).emplace(display_id, info); @@ -331,8 +325,8 @@ const display::TouchDeviceIdentifier& fallback_identifier = display::TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier(); for (const auto it : local_state->GetValueDict(prefs::kDisplayProperties)) { - const base::DictionaryValue* dict_value = nullptr; - if (!it.second.GetAsDictionary(&dict_value) || dict_value == nullptr) + const base::Value::Dict* dict_value = it.second.GetIfDict(); + if (!dict_value) continue; int64_t id = display::kInvalidDisplayId; if (!base::StringToInt64(it.first, &id) || @@ -369,20 +363,21 @@ continue; // Retrieve the touch device identifier that identifies the touch device. - auto* value = item.second.FindKey(kTouchDeviceIdentifier); - if (!value->is_string()) + const std::string* value = + item.second.GetDict().FindString(kTouchDeviceIdentifier); + if (!value) continue; uint32_t identifier_raw; - if (!base::StringToUint(value->GetString(), &identifier_raw)) + if (!base::StringToUint(*value, &identifier_raw)) continue; // Retrieve the display that the touch device identified by |identifier_raw| // was associated with. - value = item.second.FindKey(kPortAssociationDisplayId); - if (!value->is_string()) + value = item.second.GetDict().FindString(kPortAssociationDisplayId); + if (!value) continue; int64_t display_id; - if (!base::StringToInt64(value->GetString(), &display_id)) + if (!base::StringToInt64(*value, &display_id)) continue; port_associations.emplace( @@ -464,13 +459,10 @@ std::string name = display::DisplayIdListToString(list); DictionaryPrefUpdate update(pref_service, prefs::kSecondaryDisplays); - base::Value* pref_data = update.Get(); - base::Value layout_value(base::Value::Type::DICTIONARY); - if (base::Value* value = pref_data->FindKey(name)) { - layout_value = value->Clone(); - } - if (display::DisplayLayoutToJson(display_layout, &layout_value)) - pref_data->SetPath(name, std::move(layout_value)); + base::Value::Dict& pref_data = update->GetDict(); + base::Value::Dict* layout_dict = pref_data.EnsureDict(name); + // This call modifies `layout_dict` in place. + display::DisplayLayoutToJson(display_layout, *layout_dict); } void StoreCurrentDisplayLayoutPrefs(PrefService* pref_service) { @@ -499,7 +491,7 @@ display::DisplayManager* display_manager = GetDisplayManager(); DictionaryPrefUpdate update(pref_service, prefs::kDisplayProperties); - base::Value* pref_data = update.Get(); + base::Value::Dict& pref_data = update->GetDict(); // Pre-process data related to legacy touch calibration to opitmize lookup. const display::TouchDeviceIdentifier& fallback_identifier = @@ -519,7 +511,7 @@ int64_t id = display.id(); display::ManagedDisplayInfo info = display_manager->GetDisplayInfo(id); - base::Value property_value(base::Value::Type::DICTIONARY); + base::Value::Dict property_value; // Don't save the display preference in unified mode because its // size and modes can change depending on the combination of displays. if (display_manager->IsInUnifiedMode()) @@ -530,34 +522,33 @@ // But we should keep any original value so that it can be restored when // exiting tablet mode. if (Shell::Get()->tablet_mode_controller()->InTabletMode()) { - const base::Value* original_property = - pref_data->FindDictKey(base::NumberToString(id)); + const base::Value::Dict* original_property = + pref_data.FindDict(base::NumberToString(id)); if (original_property) { absl::optional<int> original_rotation = - original_property->FindIntKey("rotation"); + original_property->FindInt("rotation"); if (original_rotation) { - property_value.SetIntKey("rotation", *original_rotation); + property_value.Set("rotation", *original_rotation); } } } else { - property_value.SetIntKey("rotation", - static_cast<int>(info.GetRotation( - display::Display::RotationSource::USER))); + property_value.Set("rotation", + static_cast<int>(info.GetRotation( + display::Display::RotationSource::USER))); } display::ManagedDisplayMode mode; if (!display.IsInternal() && display_manager->GetSelectedModeForDisplayId(id, &mode) && !mode.native()) { - property_value.SetIntKey("width", mode.size().width()); - property_value.SetIntKey("height", mode.size().height()); - property_value.SetIntKey( - "device-scale-factor", - static_cast<int>(mode.device_scale_factor() * 1000)); + property_value.Set("width", mode.size().width()); + property_value.Set("height", mode.size().height()); + property_value.Set("device-scale-factor", + static_cast<int>(mode.device_scale_factor() * 1000)); if (display::features::IsListAllDisplayModesEnabled()) { - property_value.SetBoolKey("interlaced", mode.is_interlaced()); - property_value.SetDoubleKey("refresh-rate", mode.refresh_rate()); + property_value.Set("interlaced", mode.is_interlaced()); + property_value.Set("refresh-rate", mode.refresh_rate()); } } if (!info.overscan_insets_in_dip().IsEmpty()) @@ -569,9 +560,9 @@ TouchDataToValue(legacy_data_map.at(id).calibration_data, property_value); } - property_value.SetDoubleKey(kDisplayZoom, info.zoom_factor()); + property_value.Set(kDisplayZoom, info.zoom_factor()); - pref_data->SetKey(base::NumberToString(id), std::move(property_value)); + pref_data.Set(base::NumberToString(id), std::move(property_value)); } } @@ -621,9 +612,9 @@ display::Display::Rotation rotation, bool rotation_lock) { DictionaryPrefUpdate update(pref_service, prefs::kDisplayRotationLock); - base::Value* pref_data = update.Get(); - pref_data->SetBoolKey("lock", rotation_lock); - pref_data->SetIntKey("orientation", static_cast<int>(rotation)); + base::Value::Dict& pref_data = update->GetDict(); + pref_data.Set("lock", rotation_lock); + pref_data.Set("orientation", static_cast<int>(rotation)); } void StoreCurrentDisplayRotationLockPrefs(PrefService* pref_service) { @@ -644,58 +635,57 @@ GetDisplayManager()->touch_device_manager(); DictionaryPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations); - base::Value* pref_data = update.Get(); - pref_data->DictClear(); + base::Value::Dict& pref_data = update->GetDict(); + pref_data.clear(); const display::TouchDeviceManager::TouchAssociationMap& touch_associations = touch_device_manager->touch_associations(); for (const auto& association : touch_associations) { - base::Value association_info_map_value(base::Value::Type::DICTIONARY); + base::Value::Dict association_info_map_value; for (const auto& association_info : association.second) { // Iteration for each pair of <Display ID, TouchAssociationInfo>. - base::Value association_info_value(base::Value::Type::DICTIONARY); + base::Value::Dict association_info_value; // Parsing each member of TouchAssociationInfo and storing them in // |association_info_value|. // Serialize timestamp. - association_info_value.SetDoubleKey( - kTouchAssociationTimestamp, - association_info.second.timestamp.ToDoubleT()); + association_info_value.Set(kTouchAssociationTimestamp, + association_info.second.timestamp.ToDoubleT()); // Serialize TouchCalibrationData. - base::Value calibration_data_value(base::Value::Type::DICTIONARY); + base::Value::Dict calibration_data_value; TouchDataToValue(association_info.second.calibration_data, calibration_data_value); - association_info_value.SetKey(kTouchAssociationCalibrationData, - std::move(calibration_data_value)); + association_info_value.Set(kTouchAssociationCalibrationData, + std::move(calibration_data_value)); // Move the searialzed TouchAssociationInfo stored in // |association_info_value| to |association_info_map_value| against the // display id as key. This is a 1 to 1 mapping of a single entry from // AssociationInfoMap to its serialized form. - association_info_map_value.SetKey( + association_info_map_value.Set( base::NumberToString(association_info.first), std::move(association_info_value)); } - if (association_info_map_value.DictEmpty()) + if (association_info_map_value.empty()) continue; // Move the already serialized entry of AssociationInfoMap from // |association_info_map_value| to |pref_data| against the // TouchDeviceIdentifier as key. This is a 1 to 1 mapping of a single entry // from TouchAssociationMap to its serialized form. - pref_data->SetKey(association.first.ToString(), - std::move(association_info_map_value)); + pref_data.Set(association.first.ToString(), + std::move(association_info_map_value)); } // Store the port mappings. What display a touch device connected to a // particular port is associated with. DictionaryPrefUpdate update_port(pref_service, prefs::kDisplayTouchPortAssociations); - pref_data = update_port.Get(); - update_port->DictClear(); + base::Value::Dict& port_pref_data = update_port->GetDict(); + port_pref_data.clear(); const display::TouchDeviceManager::PortAssociationMap& port_associations = touch_device_manager->port_associations(); @@ -703,26 +693,26 @@ // For each port identified by the secondary id of TouchDeviceIdentifier, // we store the touch device and the display associated with it. for (const auto& association : port_associations) { - base::Value association_info_value(base::Value::Type::DICTIONARY); - association_info_value.SetStringKey(kTouchDeviceIdentifier, - association.first.ToString()); - association_info_value.SetStringKey( - kPortAssociationDisplayId, base::NumberToString(association.second)); + base::Value::Dict association_info_value; + association_info_value.Set(kTouchDeviceIdentifier, + association.first.ToString()); + association_info_value.Set(kPortAssociationDisplayId, + base::NumberToString(association.second)); - pref_data->SetKey(association.first.SecondaryIdToString(), - std::move(association_info_value)); + port_pref_data.Set(association.first.SecondaryIdToString(), + std::move(association_info_value)); } } // Stores mirror info for each external display. void StoreExternalDisplayMirrorInfo(PrefService* pref_service) { ListPrefUpdate update(pref_service, prefs::kExternalDisplayMirrorInfo); - base::Value* pref_data = update.Get(); - pref_data->ClearList(); + base::Value::List& pref_data = update->GetList(); + pref_data.clear(); const std::set<int64_t>& external_display_mirror_info = GetDisplayManager()->external_display_mirror_info(); for (const auto& id : external_display_mirror_info) - pref_data->Append(base::NumberToString(id)); + pref_data.Append(base::NumberToString(id)); } // Stores mixed mirror mode parameters. Clear the preferences if @@ -732,21 +722,21 @@ const absl::optional<display::MixedMirrorModeParams>& mixed_params) { DictionaryPrefUpdate update(pref_service, prefs::kDisplayMixedMirrorModeParams); - base::Value* pref_data = update.Get(); - pref_data->DictClear(); + base::Value::Dict& pref_data = update->GetDict(); + pref_data.clear(); if (!mixed_params) return; - pref_data->SetStringKey(kMirroringSourceId, - base::NumberToString(mixed_params->source_id)); + pref_data.Set(kMirroringSourceId, + base::NumberToString(mixed_params->source_id)); - base::Value mirroring_destination_ids_value(base::Value::Type::LIST); + base::Value::List mirroring_destination_ids_list; for (const auto& id : mixed_params->destination_ids) { - mirroring_destination_ids_value.Append(base::NumberToString(id)); + mirroring_destination_ids_list.Append(base::NumberToString(id)); } - pref_data->SetKey(kMirroringDestinationIds, - std::move(mirroring_destination_ids_value)); + pref_data.Set(kMirroringDestinationIds, + std::move(mirroring_destination_ids_list)); } void StoreCurrentDisplayMixedMirrorModeParams(PrefService* pref_service) { @@ -888,11 +878,10 @@ int64_t display_id, const display::TouchCalibrationData& data) { DictionaryPrefUpdate update(local_state_, prefs::kDisplayProperties); - base::Value* pref_data = update.Get(); - base::Value property_value(base::Value::Type::DICTIONARY); + base::Value::Dict& pref_data = update->GetDict(); + base::Value::Dict property_value; TouchDataToValue(data, property_value); - pref_data->SetKey(base::NumberToString(display_id), - std::move(property_value)); + pref_data.Set(base::NumberToString(display_id), std::move(property_value)); } bool DisplayPrefs::ParseTouchCalibrationStringForTest(
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc index d829387..38503d1 100644 --- a/ash/display/display_prefs_unittest.cc +++ b/ash/display/display_prefs_unittest.cc
@@ -162,12 +162,9 @@ DCHECK(!name.empty()); - base::Value* pref_data = update.Get(); - base::Value layout_value(base::Value::Type::DICTIONARY); - if (const base::Value* value = pref_data->FindKey(name)) - layout_value = value->Clone(); - if (display::DisplayLayoutToJson(display_layout, &layout_value)) - pref_data->SetPath(name, std::move(layout_value)); + base::Value::Dict& pref_data = update->GetDict(); + base::Value::Dict* layout_dict = pref_data.EnsureDict(name); + display::DisplayLayoutToJson(display_layout, *layout_dict); } void StoreDisplayPropertyForList(const display::DisplayIdList& list, @@ -176,14 +173,13 @@ std::string name = display::DisplayIdListToString(list); DictionaryPrefUpdate update(local_state(), prefs::kSecondaryDisplays); - base::Value* pref_data = update.Get(); - - if (base::Value* existing_layout_value = pref_data->FindKey(name)) { - existing_layout_value->SetKey(key, std::move(value)); + base::Value::Dict& pref_data = update->GetDict(); + if (base::Value::Dict* existing_layout_value = pref_data.FindDict(name)) { + existing_layout_value->Set(key, std::move(value)); } else { - base::Value layout_value(base::Value::Type::DICTIONARY); - layout_value.SetBoolKey(key, true); - pref_data->SetPath(name, std::move(layout_value)); + base::Value::Dict layout_dict; + layout_dict.Set(key, true); + pref_data.SetByDottedPath(name, std::move(layout_dict)); } }
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_item_impl.cc b/ash/public/cpp/external_arc/message_center/arc_notification_item_impl.cc index 06100db..f34f52e4 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_item_impl.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_item_impl.cc
@@ -11,6 +11,7 @@ #include "ash/public/cpp/external_arc/message_center/arc_notification_content_view.h" #include "ash/public/cpp/external_arc/message_center/arc_notification_delegate.h" #include "ash/public/cpp/external_arc/message_center/arc_notification_view.h" +#include "ash/public/cpp/external_arc/message_center/metrics_utils.h" #include "ash/public/cpp/message_center/arc_notification_constants.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" @@ -131,9 +132,13 @@ if (expand_state_ != ArcNotificationExpandState::FIXED_SIZE && data->expand_state != ArcNotificationExpandState::FIXED_SIZE && expand_state_ != data->expand_state) { - // Assuming changing the expand status on Android-side is manually tiggered + // Assuming changing the expand status on Android-side is manually triggered // by user. manually_expanded_or_collapsed_ = true; + metrics_utils::LogArcNotificationExpandState( + data->expand_state == ArcNotificationExpandState::EXPANDED + ? metrics_utils::ArcNotificationExpandState::kExpanded + : metrics_utils::ArcNotificationExpandState::kCollapsed); } type_ = data->type;
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc index 5445a64d..6c1dd50 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc
@@ -33,6 +33,7 @@ using arc::mojom::ArcNotificationData; using arc::mojom::ArcNotificationDataPtr; using arc::mojom::ArcNotificationEvent; +using arc::mojom::ArcNotificationExpandState; using arc::mojom::ArcNotificationPriority; using arc::mojom::MessageCenterVisibility; using arc::mojom::NotificationConfiguration; @@ -227,6 +228,10 @@ metrics_utils::LogArcNotificationActionEnabled(data->is_action_enabled); metrics_utils::LogArcNotificationInlineReplyEnabled( data->is_inline_reply_enabled); + metrics_utils::LogArcNotificationExpandState( + data->expand_state == ArcNotificationExpandState::FIXED_SIZE + ? metrics_utils::ArcNotificationExpandState::kFixedSize + : metrics_utils::ArcNotificationExpandState::kExpandable); } std::string app_id =
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_manager_unittest.cc b/ash/public/cpp/external_arc/message_center/arc_notification_manager_unittest.cc index 75b9a70..b0938b3 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_manager_unittest.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_manager_unittest.cc
@@ -30,6 +30,7 @@ constexpr char kDummyNotificationKey[] = "DUMMY_NOTIFICATION_KEY"; constexpr char kHistogramNameActionEnabled[] = "Arc.Notifications.ActionEnabled"; +constexpr char kHistogramNameExpandState[] = "Arc.Notifications.ExpandState"; constexpr char kHistogramNameStyle[] = "Arc.Notifications.Style"; constexpr char kHistogramNameInlineReplyEnabled[] = "Arc.Notifications.InlineReplyEnabled"; @@ -334,18 +335,21 @@ UmaMeticsPublishedOnlyWhenNotificationCreated) { base::HistogramTester histogram_tester; histogram_tester.ExpectTotalCount(kHistogramNameActionEnabled, 0); + histogram_tester.ExpectTotalCount(kHistogramNameExpandState, 0); histogram_tester.ExpectTotalCount(kHistogramNameStyle, 0); histogram_tester.ExpectTotalCount(kHistogramNameInlineReplyEnabled, 0); // Create notification std::string key = CreateNotification(); histogram_tester.ExpectTotalCount(kHistogramNameActionEnabled, 1); + histogram_tester.ExpectTotalCount(kHistogramNameExpandState, 1); histogram_tester.ExpectTotalCount(kHistogramNameStyle, 1); histogram_tester.ExpectTotalCount(kHistogramNameInlineReplyEnabled, 1); // Update notification CreateNotificationWithKey(key); histogram_tester.ExpectTotalCount(kHistogramNameActionEnabled, 1); + histogram_tester.ExpectTotalCount(kHistogramNameExpandState, 1); histogram_tester.ExpectTotalCount(kHistogramNameStyle, 1); histogram_tester.ExpectTotalCount(kHistogramNameInlineReplyEnabled, 1); }
diff --git a/ash/public/cpp/external_arc/message_center/metrics_utils.cc b/ash/public/cpp/external_arc/message_center/metrics_utils.cc index b8584b3..dfb0d14c 100644 --- a/ash/public/cpp/external_arc/message_center/metrics_utils.cc +++ b/ash/public/cpp/external_arc/message_center/metrics_utils.cc
@@ -12,6 +12,10 @@ base::UmaHistogramBoolean("Arc.Notifications.ActionEnabled", action_enabled); } +void LogArcNotificationExpandState(ArcNotificationExpandState state) { + base::UmaHistogramEnumeration("Arc.Notifications.ExpandState", state); +} + void LogArcNotificationInlineReplyEnabled(bool inline_reply_enabled) { base::UmaHistogramBoolean("Arc.Notifications.InlineReplyEnabled", inline_reply_enabled);
diff --git a/ash/public/cpp/external_arc/message_center/metrics_utils.h b/ash/public/cpp/external_arc/message_center/metrics_utils.h index e6aa819..f90d2a3f 100644 --- a/ash/public/cpp/external_arc/message_center/metrics_utils.h +++ b/ash/public/cpp/external_arc/message_center/metrics_utils.h
@@ -9,9 +9,25 @@ namespace ash::metrics_utils { +// Note to keep in sync with enum in tools/metrics/histograms/enums.xml. +enum class ArcNotificationExpandState { + // No expand button is available, the size of the notification is fixed + kFixedSize = 0, + // Expand button is available to expand the notification + kExpandable = 1, + // The state after the user expands the notification + kExpanded = 2, + // The state after the user collapses the notification + kCollapsed = 3, + kMaxValue = kCollapsed, +}; + // Logs if action button is enabled for Arc notification. void LogArcNotificationActionEnabled(bool action_enabled); +// Logs the expand state for Arc notification. +void LogArcNotificationExpandState(ArcNotificationExpandState state); + // Logs if inline reply is enabled for Arc notification. void LogArcNotificationInlineReplyEnabled(bool inline_reply_enabled);
diff --git a/ash/session/fullscreen_controller.cc b/ash/session/fullscreen_controller.cc index 1640d45..d394eff 100644 --- a/ash/session/fullscreen_controller.cc +++ b/ash/session/fullscreen_controller.cc
@@ -6,6 +6,7 @@ #include <limits> +#include "ash/constants/app_types.h" #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/session/fullscreen_notification_bubble.h" @@ -18,13 +19,48 @@ #include "base/check.h" #include "chromeos/dbus/power_manager/backlight.pb.h" #include "chromeos/dbus/power_manager/idle.pb.h" +#include "chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" -#include "components/url_matcher/url_matcher.h" -#include "components/url_matcher/url_util.h" +#include "ui/aura/client/aura_constants.h" +#include "url/gurl.h" namespace ash { +namespace { + +// Exits full screen to avoid the web page or app mimicking the lock screen if +// the active window is in full screen mode and the shelf is not visible. Do not +// exit fullscreen if the shelf is visible while in fullscreen because the shelf +// makes it harder for a web page or app to mimic the lock screen. +void ExitFullscreenIfActive() { + WindowState* active_window_state = WindowState::ForActiveWindow(); + if (!active_window_state || !active_window_state->IsFullscreen()) + return; + + Shelf* shelf = Shelf::ForWindow(active_window_state->window()); + const bool shelf_visible = + shelf->GetVisibilityState() == ShelfVisibilityState::SHELF_VISIBLE; + + if (shelf_visible && !active_window_state->GetHideShelfWhenFullscreen()) + return; + + const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN); + active_window_state->OnWMEvent(&event); +} + +// Receives the result from the request to Lacros and exits full screen, if +// required. |callback| will be invoked to signal readiness for session lock. +void OnShouldExitFullscreenResult(base::OnceClosure callback, + bool should_exit_fullscreen) { + if (should_exit_fullscreen) + ExitFullscreenIfActive(); + + std::move(callback).Run(); +} + +} // namespace + FullscreenController::FullscreenController( SessionControllerImpl* session_controller) : session_controller_(session_controller) { @@ -43,24 +79,63 @@ } // static -void FullscreenController::MaybeExitFullscreen() { - // If the active window is fullscreen, exit fullscreen to avoid the web page - // or app mimicking the lock screen. Do not exit fullscreen if the shelf is - // visible while in fullscreen because the shelf makes it harder for a web - // page or app to mimic the lock screen. +void FullscreenController::RegisterProfilePrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(prefs::kFullscreenAlertEnabled, true, + PrefRegistry::PUBLIC); +} + +void FullscreenController::MaybeExitFullscreenBeforeLock( + base::OnceClosure callback) { + // Check whether it is allowed to keep full screen on unlock. + if (!features::IsFullscreenAfterUnlockAllowed()) { + ExitFullscreenIfActive(); + std::move(callback).Run(); + return; + } + + // Nothing to do if the active window is not in full screen mode. WindowState* active_window_state = WindowState::ForActiveWindow(); - if (!active_window_state || !active_window_state->IsFullscreen()) + if (!active_window_state || !active_window_state->IsFullscreen()) { + std::move(callback).Run(); return; + } - Shelf* shelf = Shelf::ForWindow(active_window_state->window()); - const bool shelf_visible = - shelf->GetVisibilityState() == ShelfVisibilityState::SHELF_VISIBLE; + if (!keep_fullscreen_checker_) { + keep_fullscreen_checker_ = + std::make_unique<chromeos::KeepFullscreenForUrlChecker>( + Shell::Get()->session_controller()->GetPrimaryUserPrefService()); + } - if (shelf_visible && !active_window_state->GetHideShelfWhenFullscreen()) + // Always exit full screen if the allowlist policy is unset. + if (!keep_fullscreen_checker_ + ->IsKeepFullscreenWithoutNotificationPolicySet()) { + ExitFullscreenIfActive(); + std::move(callback).Run(); return; + } - const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN); - active_window_state->OnWMEvent(&event); + // Try to get the URL of the active window from the shell delegate. + const GURL& url = + Shell::Get()->shell_delegate()->GetLastCommittedURLForWindowIfAny( + active_window_state->window()); + + // If the chrome shell delegate did not return a URL for the active window, it + // could be a Lacros window and it should check with Lacros whether the + // FullscreenController should exit full screen mode. + if (url.is_empty() && + active_window_state->window()->GetProperty(aura::client::kAppType) == + static_cast<int>(AppType::LACROS)) { + auto should_exit_fullscreen_callback = + base::BindOnce(&OnShouldExitFullscreenResult, std::move(callback)); + Shell::Get()->shell_delegate()->ShouldExitFullscreenBeforeLock( + std::move(should_exit_fullscreen_callback)); + return; + } + + // Check if it is allowed by user pref to keep full screen for the window URL. + if (keep_fullscreen_checker_->ShouldExitFullscreenForUrl(url)) + ExitFullscreenIfActive(); + std::move(callback).Run(); } void FullscreenController::MaybeShowNotification() { @@ -99,21 +174,12 @@ bubble_->ShowForWindowState(active_window_state); } -// static -void FullscreenController::RegisterProfilePrefs(PrefRegistrySimple* registry) { - registry->RegisterBooleanPref(prefs::kFullscreenAlertEnabled, true, - PrefRegistry::PUBLIC); - registry->RegisterListPref( - prefs::kKeepFullscreenWithoutNotificationUrlAllowList, - PrefRegistry::PUBLIC); -} - void FullscreenController::SuspendImminent( power_manager::SuspendImminent::Reason reason) { if (session_controller_->login_status() != LoginStatus::GUEST) return; - MaybeExitFullscreen(); + ExitFullscreenIfActive(); } void FullscreenController::ScreenIdleStateChanged( @@ -122,7 +188,7 @@ return; if (proto.off() || proto.dimmed()) - MaybeExitFullscreen(); + ExitFullscreenIfActive(); } void FullscreenController::ScreenBrightnessChanged( @@ -151,33 +217,4 @@ MaybeShowNotification(); } -// static -bool FullscreenController::ShouldExitFullscreenBeforeLock() { - // Check whether it is allowed to keep full screen on unlock. - if (!features::IsFullscreenAfterUnlockAllowed()) - return true; - - // Nothing to do if the active window is not in full screen mode. - WindowState* active_window_state = WindowState::ForActiveWindow(); - if (!active_window_state || !active_window_state->IsFullscreen()) - return false; - - // Always exit full screen if the allowlist policy is unset. - auto* prefs = Shell::Get()->session_controller()->GetPrimaryUserPrefService(); - const auto& url_allow_list = prefs->GetValueList( - prefs::kKeepFullscreenWithoutNotificationUrlAllowList); - if (url_allow_list.size() == 0) - return true; - - // Get the URL of the active window from the shell delegate. - const GURL& url = - Shell::Get()->shell_delegate()->GetLastCommittedURLForWindowIfAny( - active_window_state->window()); - - // Check if it is allowed by user pref to keep full screen for the active URL. - url_matcher::URLMatcher url_matcher; - url_matcher::util::AddAllowFilters(&url_matcher, url_allow_list); - return url_matcher.MatchURL(url).empty(); -} - } // namespace ash
diff --git a/ash/session/fullscreen_controller.h b/ash/session/fullscreen_controller.h index 6c63761..d918194 100644 --- a/ash/session/fullscreen_controller.h +++ b/ash/session/fullscreen_controller.h
@@ -12,6 +12,10 @@ class PrefRegistrySimple; +namespace chromeos { +class KeepFullscreenForUrlChecker; +} // namespace chromeos + namespace ash { class SessionControllerImpl; @@ -25,13 +29,17 @@ ~FullscreenController() override; - static void MaybeExitFullscreen(); - static void RegisterProfilePrefs(PrefRegistrySimple* registry); - static bool ShouldExitFullscreenBeforeLock(); + // Checks the window state, user pref and, if required, sends a request to + // Lacros to determine whether it should exit full screen mode before the + // session is locked. |callback| will be invoked to signal readiness for + // session lock. + void MaybeExitFullscreenBeforeLock(base::OnceClosure callback); private: + void MaybeShowNotification(); + // chromeos::PowerManagerClient::Observer: void SuspendImminent(power_manager::SuspendImminent::Reason reason) override; void ScreenIdleStateChanged( @@ -41,12 +49,13 @@ void LidEventReceived(chromeos::PowerManagerClient::LidState state, base::TimeTicks timestamp) override; - void MaybeShowNotification(); - const SessionControllerImpl* const session_controller_; std::unique_ptr<FullscreenNotificationBubble> bubble_; + std::unique_ptr<chromeos::KeepFullscreenForUrlChecker> + keep_fullscreen_checker_; + // Whether the screen brightness is low enough to make display dark. bool device_in_dark_ = false; };
diff --git a/ash/session/fullscreen_controller_unittest.cc b/ash/session/fullscreen_controller_unittest.cc index 4294924..4d9cf02261 100644 --- a/ash/session/fullscreen_controller_unittest.cc +++ b/ash/session/fullscreen_controller_unittest.cc
@@ -6,13 +6,14 @@ #include <memory> -#include "ash/constants/ash_pref_names.h" +#include "ash/constants/app_types.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/test_shell_delegate.h" #include "ash/wm/window_state.h" #include "base/run_loop.h" +#include "chromeos/ui/wm/fullscreen/pref_names.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" @@ -27,7 +28,8 @@ constexpr char kMatchingPattern[] = "test.com"; constexpr char kWildcardPattern[] = "*"; -class FullscreenControllerTest : public AshTestBase { +class FullscreenControllerTest : public AshTestBase, + public testing::WithParamInterface<bool> { public: FullscreenControllerTest() {} @@ -38,10 +40,9 @@ // AshTestBase: void SetUp() override { - // Create a shell delegate and set the active URL. + // Create a test shell delegate which can return fake responses. auto test_shell_delegate = std::make_unique<TestShellDelegate>(); test_shell_delegate_ = test_shell_delegate.get(); - test_shell_delegate->SetLastCommittedURLForWindow(kActiveUrl); AshTestBase::SetUp(std::move(test_shell_delegate)); @@ -57,6 +58,10 @@ window_ = CreateTestWindow(); window_->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); + if (is_lacros_window_) { + window_->SetProperty(aura::client::kAppType, + static_cast<int>(AppType::LACROS)); + } window_state_ = WindowState::Get(window_.get()); } @@ -65,10 +70,24 @@ base::Value list(base::Value::Type::LIST); list.Append(base::Value(pattern)); Shell::Get()->session_controller()->GetPrimaryUserPrefService()->Set( - prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); + chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); + } + + void SetUpShellDelegate(bool should_exit_fullscreen, GURL url = kActiveUrl) { + // The shell delegate will only retrieve the active URL for ash-chrome + // windows and return the empty URL for lacros-chrome windows. + if (is_lacros_window_) { + test_shell_delegate_->SetLastCommittedURLForWindow(kEmptyUrl); + test_shell_delegate_->SetShouldExitFullscreenBeforeLock( + should_exit_fullscreen); + } else { + test_shell_delegate_->SetLastCommittedURLForWindow(url); + } } protected: + bool is_lacros_window_ = GetParam(); + std::unique_ptr<aura::Window> window_; WindowState* window_state_ = nullptr; @@ -78,7 +97,7 @@ // Test that full screen is exited after session unlock if the allow list pref // is unset. -TEST_F(FullscreenControllerTest, ExitFullscreenIfUnsetPref) { +TEST_P(FullscreenControllerTest, ExitFullscreenIfUnsetPref) { EXPECT_TRUE(window_state_->IsFullscreen()); base::RunLoop run_loop; @@ -92,7 +111,10 @@ // Test that full screen is exited after session unlock if the URL of the active // window does not match any patterns from the allow list. -TEST_F(FullscreenControllerTest, ExitFullscreenIfNonMatchingPref) { +TEST_P(FullscreenControllerTest, ExitFullscreenIfNonMatchingPref) { + bool should_exit_fullscreen = true; + SetUpShellDelegate(should_exit_fullscreen); + SetKeepFullscreenWithoutNotificationAllowList(kNonMatchingPattern); EXPECT_TRUE(window_state_->IsFullscreen()); @@ -103,18 +125,21 @@ GetSessionControllerClient()->UnlockScreen(); run_loop.Run(); - EXPECT_FALSE(window_state_->IsFullscreen()); + EXPECT_EQ(window_state_->IsFullscreen(), !should_exit_fullscreen); } // Test that full screen is not exited after session unlock if the URL of the // active window matches a pattern from the allow list. -TEST_F(FullscreenControllerTest, KeepFullscreenIfMatchingPref) { +TEST_P(FullscreenControllerTest, KeepFullscreenIfMatchingPref) { + bool should_exit_fullscreen = false; + SetUpShellDelegate(should_exit_fullscreen); + // Set up the URL exempt list with one matching and one non-matching pattern. base::Value list(base::Value::Type::LIST); list.Append(base::Value(kNonMatchingPattern)); list.Append(base::Value(kMatchingPattern)); Shell::Get()->session_controller()->GetPrimaryUserPrefService()->Set( - prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); + chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); EXPECT_TRUE(window_state_->IsFullscreen()); @@ -124,12 +149,15 @@ GetSessionControllerClient()->UnlockScreen(); run_loop.Run(); - EXPECT_TRUE(window_state_->IsFullscreen()); + EXPECT_EQ(window_state_->IsFullscreen(), !should_exit_fullscreen); } // Test that full screen is not exited after session unlock if the allow list // includes the wildcard character. -TEST_F(FullscreenControllerTest, KeepFullscreenIfWildcardPref) { +TEST_P(FullscreenControllerTest, KeepFullscreenIfWildcardPref) { + bool should_exit_fullscreen = false; + SetUpShellDelegate(should_exit_fullscreen); + SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern); EXPECT_TRUE(window_state_->IsFullscreen()); @@ -140,13 +168,14 @@ GetSessionControllerClient()->UnlockScreen(); run_loop.Run(); - EXPECT_TRUE(window_state_->IsFullscreen()); + EXPECT_EQ(window_state_->IsFullscreen(), !should_exit_fullscreen); } // Test that full screen is exited after session unlock if the URL is not // available. -TEST_F(FullscreenControllerTest, ExitFullscreenIfUnsetUrlUnsetPref) { - test_shell_delegate_->SetLastCommittedURLForWindow(kEmptyUrl); +TEST_P(FullscreenControllerTest, ExitFullscreenIfUnsetUrlUnsetPref) { + bool should_exit_fullscreen = true; + SetUpShellDelegate(should_exit_fullscreen, kEmptyUrl); EXPECT_TRUE(window_state_->IsFullscreen()); @@ -156,13 +185,14 @@ GetSessionControllerClient()->UnlockScreen(); run_loop.Run(); - EXPECT_FALSE(window_state_->IsFullscreen()); + EXPECT_EQ(window_state_->IsFullscreen(), !should_exit_fullscreen); } // Test that full screen is not exited after session unlock if the allow list // includes the wildcard character and the URL is not available. -TEST_F(FullscreenControllerTest, KeepFullscreenIfUnsetUrlWildcardPref) { - test_shell_delegate_->SetLastCommittedURLForWindow(kEmptyUrl); +TEST_P(FullscreenControllerTest, KeepFullscreenIfUnsetUrlWildcardPref) { + bool should_exit_fullscreen = false; + SetUpShellDelegate(should_exit_fullscreen, kEmptyUrl); SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern); @@ -174,8 +204,10 @@ GetSessionControllerClient()->UnlockScreen(); run_loop.Run(); - EXPECT_TRUE(window_state_->IsFullscreen()); + EXPECT_EQ(window_state_->IsFullscreen(), !should_exit_fullscreen); } +INSTANTIATE_TEST_SUITE_P(All, FullscreenControllerTest, testing::Bool()); + } // namespace } // namespace ash
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc index f173000ca..be9fb4c 100644 --- a/ash/session/session_controller_impl.cc +++ b/ash/session/session_controller_impl.cc
@@ -369,10 +369,7 @@ } void SessionControllerImpl::PrepareForLock(PrepareForLockCallback callback) { - if (FullscreenController::ShouldExitFullscreenBeforeLock()) - FullscreenController::MaybeExitFullscreen(); - - std::move(callback).Run(); + fullscreen_controller_->MaybeExitFullscreenBeforeLock(std::move(callback)); } void SessionControllerImpl::StartLock(StartLockCallback callback) {
diff --git a/ash/shell_delegate.cc b/ash/shell_delegate.cc index 48cd1a31..193f496 100644 --- a/ash/shell_delegate.cc +++ b/ash/shell_delegate.cc
@@ -35,4 +35,9 @@ return GURL::EmptyGURL(); } +void ShellDelegate::ShouldExitFullscreenBeforeLock( + ShellDelegate::ShouldExitFullscreenCallback callback) { + std::move(callback).Run(false); +} + } // namespace ash
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h index f383d2d70..bd2024d7 100644 --- a/ash/shell_delegate.h +++ b/ash/shell_delegate.h
@@ -152,6 +152,12 @@ // Retrieves the official Chrome version string e.g. 105.0.5178.0. virtual std::string GetVersionString() = 0; + + // Forwards the ShouldExitFullscreenBeforeLock() call to the crosapi browser + // manager. + using ShouldExitFullscreenCallback = base::OnceCallback<void(bool)>; + virtual void ShouldExitFullscreenBeforeLock( + ShouldExitFullscreenCallback callback); }; } // namespace ash
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc index 0cd0493..0f834ba 100644 --- a/ash/test_shell_delegate.cc +++ b/ash/test_shell_delegate.cc
@@ -67,6 +67,11 @@ tab_scrubber_enabled_ = enabled; } +void TestShellDelegate::ShouldExitFullscreenBeforeLock( + ShouldExitFullscreenCallback callback) { + std::move(callback).Run(should_exit_fullscreen_before_lock_); +} + bool TestShellDelegate::ShouldWaitForTouchPressAck(gfx::NativeWindow window) { return should_wait_for_touch_ack_; } @@ -86,6 +91,11 @@ can_go_back_ = can_go_back; } +void TestShellDelegate::SetShouldExitFullscreenBeforeLock( + bool should_exit_fullscreen_before_lock) { + should_exit_fullscreen_before_lock_ = should_exit_fullscreen_before_lock; +} + void TestShellDelegate::SetShouldWaitForTouchAck( bool should_wait_for_touch_ack) { should_wait_for_touch_ack_ = should_wait_for_touch_ack;
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h index db9404ea..d3af456 100644 --- a/ash/test_shell_delegate.h +++ b/ash/test_shell_delegate.h
@@ -49,6 +49,8 @@ GetGeolocationUrlLoaderFactory() const override; bool CanGoBack(gfx::NativeWindow window) const override; void SetTabScrubberChromeOSEnabled(bool enabled) override; + void ShouldExitFullscreenBeforeLock( + ShouldExitFullscreenCallback callback) override; bool ShouldWaitForTouchPressAck(gfx::NativeWindow window) override; int GetBrowserWebUITabStripHeight() override; void BindMultiDeviceSetup( @@ -61,6 +63,8 @@ const std::vector<aura::Window*>& windows) override {} void SetCanGoBack(bool can_go_back); + void SetShouldExitFullscreenBeforeLock( + bool should_exit_fullscreen_before_lock); void SetShouldWaitForTouchAck(bool should_wait_for_touch_ack); void SetSessionRestoreInProgress(bool in_progress); bool IsLoggingRedirectDisabled() const override; @@ -83,6 +87,9 @@ // True if the tab scrubber is enabled. bool tab_scrubber_enabled_ = true; + // False if it is allowed by policy to keep fullscreen after unlock. + bool should_exit_fullscreen_before_lock_ = true; + // True if when performing back gesture on the top window, we should handle // the event after the touch ack is received. Please refer to // |BackGestureEventHandler::should_wait_for_touch_ack_| for detailed
diff --git a/ash/webui/media_app_ui/media_app_page_handler.cc b/ash/webui/media_app_ui/media_app_page_handler.cc index 4814f96e..34ab1684 100644 --- a/ash/webui/media_app_ui/media_app_page_handler.cc +++ b/ash/webui/media_app_ui/media_app_page_handler.cc
@@ -53,6 +53,12 @@ std::move(callback).Run(); } +void MediaAppPageHandler::MaybeTriggerPdfHats( + MaybeTriggerPdfHatsCallback callback) { + media_app_ui_->delegate()->MaybeTriggerPdfHats(); + std::move(callback).Run(); +} + void MediaAppPageHandler::IsFileArcWritable( mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken> token, IsFileArcWritableCallback callback) {
diff --git a/ash/webui/media_app_ui/media_app_page_handler.h b/ash/webui/media_app_ui/media_app_page_handler.h index 512a1b2..16bf7d2 100644 --- a/ash/webui/media_app_ui/media_app_page_handler.h +++ b/ash/webui/media_app_ui/media_app_page_handler.h
@@ -31,6 +31,7 @@ void OpenFeedbackDialog(OpenFeedbackDialogCallback callback) override; void ToggleBrowserFullscreenMode( ToggleBrowserFullscreenModeCallback callback) override; + void MaybeTriggerPdfHats(MaybeTriggerPdfHatsCallback callback) override; void IsFileArcWritable( mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken> token, IsFileArcWritableCallback callback) override;
diff --git a/ash/webui/media_app_ui/media_app_ui.mojom b/ash/webui/media_app_ui/media_app_ui.mojom index 9ddcffa..21e2ef5 100644 --- a/ash/webui/media_app_ui/media_app_ui.mojom +++ b/ash/webui/media_app_ui/media_app_ui.mojom
@@ -20,6 +20,8 @@ OpenFeedbackDialog() => (string? error_message); // Toggles "browser" fullscreen mode for the window. ToggleBrowserFullscreenMode() => (); + // Indicate that a trigger for displaying the PDF HaTS survey has occurred. + MaybeTriggerPdfHats() => (); // Checks if the file path for the file represented by the provided transfer // token is in a filesystem that ARC is able to write to. Returns false if // |token| can't be resolved to a path.
diff --git a/ash/webui/media_app_ui/media_app_ui_delegate.h b/ash/webui/media_app_ui/media_app_ui_delegate.h index cdb40b0..b8e1d1b 100644 --- a/ash/webui/media_app_ui/media_app_ui_delegate.h +++ b/ash/webui/media_app_ui/media_app_ui_delegate.h
@@ -28,6 +28,9 @@ // Toggles fullscreen mode on the Browser* hosting this MediaApp instance. virtual void ToggleBrowserFullscreenMode() = 0; + // Indicate that a trigger for displaying the PDF HaTS survey has occurred. + virtual void MaybeTriggerPdfHats() = 0; + // Checks whether file represented by the provided transfer token is within a // filesystem that ARC is able to write to. virtual void IsFileArcWritable(
diff --git a/ash/webui/media_app_ui/resources/js/launch.js b/ash/webui/media_app_ui/resources/js/launch.js index 76c4929..19ba4510 100644 --- a/ash/webui/media_app_ui/resources/js/launch.js +++ b/ash/webui/media_app_ui/resources/js/launch.js
@@ -243,6 +243,10 @@ window.location.reload(); }); +guestMessagePipe.registerHandler(Message.MAYBE_TRIGGER_PDF_HATS, () => { + mediaAppPageHandler.maybeTriggerPdfHats(); +}); + guestMessagePipe.registerHandler(Message.EDIT_IN_PHOTOS, message => { const editInPhotosMsg = /** @type {!EditInPhotosMessage} */ (message); const fileHandle = fileHandleForToken(editInPhotosMsg.token);
diff --git a/ash/webui/media_app_ui/resources/js/media_app.externs.js b/ash/webui/media_app_ui/resources/js/media_app.externs.js index 0794dd0..21c885d 100644 --- a/ash/webui/media_app_ui/resources/js/media_app.externs.js +++ b/ash/webui/media_app_ui/resources/js/media_app.externs.js
@@ -255,6 +255,12 @@ * @type {function()|undefined} */ mediaApp.ClientApiDelegate.prototype.reloadMainFrame = function() {}; +/** + * Indicates to the WebUI Controller that a trigger for displaying the PDF HaTS + * survey has occurred. + * @type {function()|undefined} + */ +mediaApp.ClientApiDelegate.prototype.maybeTriggerPdfHats = function() {}; /** * The client Api for interacting with the media app instance.
diff --git a/ash/webui/media_app_ui/resources/js/message_types.js b/ash/webui/media_app_ui/resources/js/message_types.js index 13b7c66..a2c673b 100644 --- a/ash/webui/media_app_ui/resources/js/message_types.js +++ b/ash/webui/media_app_ui/resources/js/message_types.js
@@ -19,6 +19,7 @@ IS_FILE_BROWSER_WRITABLE: 'is-file-browser-writable', LOAD_EXTRA_FILES: 'load-extra-files', LOAD_FILES: 'load-files', + MAYBE_TRIGGER_PDF_HATS: 'maybe-trigger-pdf-hats', NAVIGATE: 'navigate', NOTIFY_CURRENT_FILE: 'notify-current-file', OPEN_ALLOWED_FILE: 'open-allowed-file',
diff --git a/ash/webui/media_app_ui/resources/js/receiver.js b/ash/webui/media_app_ui/resources/js/receiver.js index 785b454..3da033d 100644 --- a/ash/webui/media_app_ui/resources/js/receiver.js +++ b/ash/webui/media_app_ui/resources/js/receiver.js
@@ -381,6 +381,9 @@ reloadMainFrame() { parentMessagePipe.sendMessage(Message.RELOAD_MAIN_FRAME); }, + maybeTriggerPdfHats() { + parentMessagePipe.sendMessage(Message.MAYBE_TRIGGER_PDF_HATS); + }, // TODO(b/219631600): Implement openUrlInBrowserTab() for LacrOS if needed. };
diff --git a/ash/webui/projector_app/projector_message_handler.h b/ash/webui/projector_app/projector_message_handler.h index d136a8d2..1bb1ad72 100644 --- a/ash/webui/projector_app/projector_message_handler.h +++ b/ash/webui/projector_app/projector_message_handler.h
@@ -45,6 +45,7 @@ base::WeakPtr<ProjectorMessageHandler> GetWeakPtr(); // content::WebUIMessageHandler: + // TODO(b/237337607): chrome.send() is banned on ash. Migrate to Mojo instead. void RegisterMessages() override; // ProjectorAppClient:Observer:
diff --git a/ash/webui/shortcut_customization_ui/BUILD.gn b/ash/webui/shortcut_customization_ui/BUILD.gn index 609df0a..39a1d0e 100644 --- a/ash/webui/shortcut_customization_ui/BUILD.gn +++ b/ash/webui/shortcut_customization_ui/BUILD.gn
@@ -18,6 +18,7 @@ "//ash/webui/resources:shortcut_customization_app_resources", "//ash/webui/shortcut_customization_ui/backend", "//ash/webui/shortcut_customization_ui/mojom", + "//ash/webui/system_apps/public:system_web_app_config", "//content/public/browser", "//ui/resources:webui_generated_resources_grd_grit", "//ui/webui",
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h index 9414f51..c7dca02 100644 --- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h +++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h
@@ -9,6 +9,8 @@ #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" +#include "ash/webui/shortcut_customization_ui/url_constants.h" +#include "ash/webui/system_apps/public/system_web_app_ui_config.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "ui/webui/mojo_web_ui_controller.h" @@ -18,6 +20,17 @@ namespace ash { +class ShortcutCustomizationAppUI; + +// The WebUIConfig for chrome://shortcut-customization. +class ShortcutCustomizationAppUIConfig + : public SystemWebAppUIConfig<ShortcutCustomizationAppUI> { + public: + ShortcutCustomizationAppUIConfig() + : SystemWebAppUIConfig(kChromeUIShortcutCustomizationAppHost, + SystemWebAppType::SHORTCUT_CUSTOMIZATION) {} +}; + class ShortcutCustomizationAppUI : public ui::MojoWebUIController { public: explicit ShortcutCustomizationAppUI(content::WebUI* web_ui); @@ -36,4 +49,4 @@ } // namespace ash -#endif // ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_SHORTCUT_CUSTOMIZATION_APP_UI_H_ \ No newline at end of file +#endif // ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_SHORTCUT_CUSTOMIZATION_APP_UI_H_
diff --git a/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom b/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom index 5af20556..bf57d33 100644 --- a/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom +++ b/ash/webui/system_extensions_internals_ui/mojom/system_extensions_internals_ui.mojom
@@ -12,4 +12,12 @@ // a folder located at the top level of the default Downloads directory. InstallSystemExtensionFromDownloadsDir( mojo_base.mojom.SafeBaseName system_extension_dir_name) => (bool success); + + // Returns whether a system extension is installed or not. Note that we + // currently only support one system extension being installed at a time, so + // a boolean is enough. + IsSystemExtensionInstalled() => (bool is_installed); + + // Uninstalls the currently installed System Extension. + UninstallSystemExtension() => (); };
diff --git a/ash/webui/system_extensions_internals_ui/resources/index.html b/ash/webui/system_extensions_internals_ui/resources/index.html index ad99177c..db875df 100644 --- a/ash/webui/system_extensions_internals_ui/resources/index.html +++ b/ash/webui/system_extensions_internals_ui/resources/index.html
@@ -8,9 +8,11 @@ </head> <p>Choose a directory with a System Extension to sideload it. The System Extension directory must be in Downloads for ChromeOS to be able to find it.</p> <button id="choose-directory">Choose directory</button> +<button id="uninstall" >Uninstall System Extension</button> <dialog id="result-dialog"> <p>System Extension installed</p> </dialog> +<p id="installed-status"></p> <!-- Needed for js browser tests --> <script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script> <script type="module" src="index.js"></script>
diff --git a/ash/webui/system_extensions_internals_ui/resources/index.js b/ash/webui/system_extensions_internals_ui/resources/index.js index b1dc57b..9182204 100644 --- a/ash/webui/system_extensions_internals_ui/resources/index.js +++ b/ash/webui/system_extensions_internals_ui/resources/index.js
@@ -4,13 +4,26 @@ import {pageHandler} from './page_handler.js'; +const installedStatus = document.querySelector('#installed-status'); +async function updateIsInstallStatus() { + const {isInstalled} = await pageHandler.isSystemExtensionInstalled(); + if (isInstalled) { + installedStatus.textContent = 'System Extension is installed'; + } else { + installedStatus.textContent = 'System Extension is not installed'; + } +} + +updateIsInstallStatus(); + const chooseDirButton = document.querySelector('#choose-directory'); const resultDialog = document.querySelector('#result-dialog'); - chooseDirButton.addEventListener('click', async event => { const directory = await window.showDirectoryPicker({startIn: 'downloads'}); const {success} = await pageHandler.installSystemExtensionFromDownloadsDir( {path: {path: directory.name}}); + updateIsInstallStatus(); + if (success) { resultDialog.textContent = `System Extension in '${directory.name}' was successfully installed.`; @@ -20,3 +33,9 @@ } resultDialog.showModal(); }); + +const uninstallButton = document.querySelector('#uninstall'); +uninstallButton.addEventListener('click', async event => { + await pageHandler.uninstallSystemExtension(); + updateIsInstallStatus(); +});
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc index a324e8c..c9f912e 100644 --- a/base/allocator/partition_allocator/partition_alloc_unittest.cc +++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -2795,6 +2795,58 @@ allocator.root()->Alloc(requested_size - kExtraAllocSize, type_name); memset(ptr1, 'A', requested_size - kExtraAllocSize); memset(ptr2, 'A', requested_size - kExtraAllocSize); + allocator.root()->Free(ptr1); + allocator.root()->Free(ptr2); + { + MockPartitionStatsDumper dumper; + allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, + &dumper); + EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); + + const PartitionBucketMemoryStats* stats = + dumper.GetBucketStats(requested_size); + EXPECT_TRUE(stats); + EXPECT_TRUE(stats->is_valid); + EXPECT_EQ(0u, stats->decommittable_bytes); +#if BUILDFLAG(IS_WIN) + EXPECT_EQ(3 * SystemPageSize(), stats->discardable_bytes); +#else + EXPECT_EQ(4 * SystemPageSize(), stats->discardable_bytes); +#endif + EXPECT_EQ(requested_size * 2, stats->active_bytes); + EXPECT_EQ(10 * SystemPageSize(), stats->resident_bytes); + } + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), true); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), true); + allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); + // Except for Windows, the first page is discardable because the freelist + // pointer on this page is nullptr. Note that CHECK_PAGE_IN_CORE only executes + // checks for Linux and ChromeOS, not for Windows. + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, false); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), false); + + allocator.root()->Free(ptr3); + allocator.root()->Free(ptr4); +} + +TEST_P(PartitionAllocTest, PurgeDiscardableNonPageSizedAllocOnSlotBoundary) { + const size_t requested_size = 2.5 * SystemPageSize(); + char* ptr1 = static_cast<char*>( + allocator.root()->Alloc(requested_size - kExtraAllocSize, type_name)); + void* ptr2 = + allocator.root()->Alloc(requested_size - kExtraAllocSize, type_name); + void* ptr3 = + allocator.root()->Alloc(requested_size - kExtraAllocSize, type_name); + void* ptr4 = + allocator.root()->Alloc(requested_size - kExtraAllocSize, type_name); + memset(ptr1, 'A', requested_size - kExtraAllocSize); + memset(ptr2, 'A', requested_size - kExtraAllocSize); allocator.root()->Free(ptr2); allocator.root()->Free(ptr1); { @@ -2808,7 +2860,11 @@ EXPECT_TRUE(stats); EXPECT_TRUE(stats->is_valid); EXPECT_EQ(0u, stats->decommittable_bytes); +#if BUILDFLAG(IS_WIN) EXPECT_EQ(3 * SystemPageSize(), stats->discardable_bytes); +#else + EXPECT_EQ(4 * SystemPageSize(), stats->discardable_bytes); +#endif EXPECT_EQ(requested_size * 2, stats->active_bytes); EXPECT_EQ(10 * SystemPageSize(), stats->resident_bytes); } @@ -2820,7 +2876,10 @@ allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); + // Except for Windows, the third page is discardable because the freelist + // pointer on this page is nullptr. Note that CHECK_PAGE_IN_CORE only executes + // checks for Linux and ChromeOS, not for Windows. + CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), false); CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), false);
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h index 26b0f6f..157b7a8 100644 --- a/base/allocator/partition_allocator/partition_page.h +++ b/base/allocator/partition_allocator/partition_page.h
@@ -25,6 +25,7 @@ #include "base/allocator/partition_allocator/partition_bucket.h" #include "base/allocator/partition_allocator/partition_freelist_entry.h" #include "base/allocator/partition_allocator/partition_tag_bitmap.h" +#include "base/allocator/partition_allocator/partition_tag_types.h" #include "base/allocator/partition_allocator/reservation_offset_table.h" #include "base/allocator/partition_allocator/starscan/state_bitmap.h" #include "base/allocator/partition_allocator/tagging.h" @@ -221,6 +222,10 @@ PA_ALWAYS_INLINE void SetRawSize(size_t raw_size); PA_ALWAYS_INLINE size_t GetRawSize() const; + // Only meaningful when `this` refers to a slot span in a direct map + // bucket. + PA_ALWAYS_INLINE PartitionTag* DirectMapMTETag(); + PA_ALWAYS_INLINE PartitionFreelistEntry* get_freelist_head() const { return freelist_head; } @@ -334,6 +339,13 @@ // the first one is used to store slot information, but the second one is // available for extra information) size_t raw_size; + + // Specific to when `this` is used in a direct map bucket. Since direct + // maps don't have as many tags as the typical normal bucket slot span, + // we can get away with just hiding the sole tag in here. + // + // See `//base/memory/mtecheckedptr.md` for details. + PartitionTag direct_map_tag; }; // Each partition page has metadata associated with it. The metadata of the @@ -679,6 +691,14 @@ } template <bool thread_safe> +PA_ALWAYS_INLINE PartitionTag* +SlotSpanMetadata<thread_safe>::DirectMapMTETag() { + PA_DCHECK(bucket->is_direct_mapped()); + auto* the_next_page = reinterpret_cast<PartitionPage<thread_safe>*>(this) + 1; + return &the_next_page->subsequent_page_metadata.direct_map_tag; +} + +template <bool thread_safe> PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetFreelistHead( PartitionFreelistEntry* new_head) { #if BUILDFLAG(PA_DCHECK_IS_ON)
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc index 8585f02c..bc5bc128f 100644 --- a/base/allocator/partition_allocator/partition_root.cc +++ b/base/allocator/partition_allocator/partition_root.cc
@@ -337,10 +337,10 @@ // slots are not in use. for (PartitionFreelistEntry* entry = slot_span->get_freelist_head(); entry; /**/) { - size_t slot_index = - (SlotStartPtr2Addr(entry) - slot_span_start) / slot_size; - PA_DCHECK(slot_index < num_slots); - slot_usage[slot_index] = 0; + size_t slot_number = + bucket->GetSlotNumber(SlotStartPtr2Addr(entry) - slot_span_start); + PA_DCHECK(slot_number < num_slots); + slot_usage[slot_number] = 0; #if !BUILDFLAG(IS_WIN) // If we have a slot where the encoded next pointer is 0, we can actually // discard that entry because touching a discarded page is guaranteed to @@ -348,7 +348,7 @@ // effective on big-endian machines because the masking function is // negation.) if (entry->IsEncodedNextPtrZero()) - last_slot = slot_index; + last_slot = slot_number; #endif entry = entry->GetNext(slot_size); } @@ -415,25 +415,51 @@ } } - // Next, walk the slots and for any not in use, consider where the system page - // boundaries occur. We can release any system pages back to the system as - // long as we don't interfere with a freelist pointer or an adjacent slot. + // Next, walk the slots and for any not in use, consider which system pages + // are no longer needed. We can release any system pages back to the system as + // long as we don't interfere with a freelist pointer or an adjacent used + // slot. for (size_t i = 0; i < num_slots; ++i) { - if (slot_usage[i]) + if (slot_usage[i]) { continue; + } + // The first address we can safely discard is just after the freelist // pointer. There's one quirk: if the freelist pointer is actually nullptr, // we can discard that pointer value too. uintptr_t begin_addr = slot_span_start + (i * slot_size); uintptr_t end_addr = begin_addr + slot_size; + + bool can_discard_free_list_pointer = false; #if !BUILDFLAG(IS_WIN) - if (i != last_slot) + if (i != last_slot) { begin_addr += sizeof(internal::PartitionFreelistEntry); + } else { + can_discard_free_list_pointer = true; + } #else begin_addr += sizeof(internal::PartitionFreelistEntry); #endif - begin_addr = RoundUpToSystemPage(begin_addr); + + uintptr_t rounded_up_begin_addr = RoundUpToSystemPage(begin_addr); + uintptr_t rounded_down_begin_addr = RoundDownToSystemPage(begin_addr); end_addr = RoundDownToSystemPage(end_addr); + + // |rounded_up_begin_addr| could be greater than |end_addr| only if slot + // size was less than system page size, or if free list pointer crossed the + // page boundary. Neither is possible here. + PA_DCHECK(rounded_up_begin_addr <= end_addr); + + if (rounded_down_begin_addr < rounded_up_begin_addr && i != 0 && + !slot_usage[i - 1] && can_discard_free_list_pointer) { + // This slot contains a partial page in the beginning. The rest of that + // page is contained in the slot[i-1], which is also discardable. + // Therefore we can discard this page. + begin_addr = rounded_down_begin_addr; + } else { + begin_addr = rounded_up_begin_addr; + } + if (begin_addr < end_addr) { size_t partial_slot_bytes = end_addr - begin_addr; discardable_bytes += partial_slot_bytes; @@ -443,6 +469,7 @@ } } } + return discardable_bytes; }
diff --git a/base/allocator/partition_allocator/partition_tag_types.h b/base/allocator/partition_allocator/partition_tag_types.h index e67de3b..b7f42da 100644 --- a/base/allocator/partition_allocator/partition_tag_types.h +++ b/base/allocator/partition_allocator/partition_tag_types.h
@@ -8,7 +8,7 @@ #include <cstdint> // This header defines the types for MTECheckedPtr. Canonical -// documentation available at `//base/memory/mtecheckedptr.md`. +// documentation available at `//base/memory/raw_ptr_mtecheckedptr.md`. namespace partition_alloc {
diff --git a/base/memory/mtecheckedptr.md b/base/memory/raw_ptr_mtecheckedptr.md similarity index 71% rename from base/memory/mtecheckedptr.md rename to base/memory/raw_ptr_mtecheckedptr.md index 5420900..8c9634da 100644 --- a/base/memory/mtecheckedptr.md +++ b/base/memory/raw_ptr_mtecheckedptr.md
@@ -20,6 +20,14 @@ reading. *** +*** aside +MTECheckedPtr is one particular incarnation of `raw_ptr`, and so the +primary documentation is kept here in `//base/memory/`. However, the +implementation is woven deeply into PartitionAlloc, and inevitably +some dirty PA-internal details may bubble up here when discussing +how MTECheckedPtr works. +*** + MTECheckedPtr is a Chromium-specific implementation of ARM's [MTE concept][arm-mte]. When MTECheckedPtr is enabled, @@ -84,4 +92,22 @@ both of the above degrade the `raw_ptr<T, D>` into the no-op version of `raw_ptr`. +## Appendix: PA-Internal Tag Locations + +[The top-level PartitionAlloc documentation][pa-readme] +mentions the space in which +MTECheckedPtr's tags reside - in the space labeled "Bitmaps(?)" in the +super page diagram, before the first usable slot span. This diagram +only applies to *normal* buckets and not to *direct map* buckets. + +While direct map super pages also cordon off the first partition page +and offer access to the core metadata within, reservations are always +permissible immediately after, and there are no bitmaps (whether +from *Scan or MTECheckedPtr) following that first partition page. +In implementing MTECheckedPtr support for direct maps, we decided +not to add this extra headroom for bitmaps; instead, the tag is +placed directly in `SubsequentPageMetadata`, colocated with the core +metadata in the first partition page. + [arm-mte]: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/enhancing-memory-safety +[pa-readme]: ../allocator/partition_allocator/PartitionAlloc.md#layout-in-memory
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc index 8529e31..605cbf5 100644 --- a/base/task/thread_pool/job_task_source.cc +++ b/base/task/thread_pool/job_task_source.cc
@@ -358,6 +358,15 @@ GetMaxConcurrency(state_before_sub.worker_count() - 1); } +// This is a no-op and should always return true. +bool JobTaskSource::WillReEnqueue(TimeTicks now, + TaskSource::Transaction* /*transaction*/) { + return true; +} + +// This is a no-op. +void JobTaskSource::OnBecomeReady() {} + TaskSourceSortKey JobTaskSource::GetSortKey( bool disable_fair_scheduling) const { if (disable_fair_scheduling) {
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h index 9cb03b6fc..cb06210 100644 --- a/base/task/thread_pool/job_task_source.h +++ b/base/task/thread_pool/job_task_source.h
@@ -193,6 +193,9 @@ Task TakeTask(TaskSource::Transaction* transaction) override; Task Clear(TaskSource::Transaction* transaction) override; bool DidProcessTask(TaskSource::Transaction* transaction) override; + bool WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction) override; + void OnBecomeReady() override; // Synchronizes access to workers state. mutable CheckedLock worker_lock_{UniversalSuccessor()};
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc index 12f46cec..ed562938 100644 --- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc +++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
@@ -131,8 +131,12 @@ void DidProcessTask(RegisteredTaskSource task_source) override { if (task_source) { - EnqueueTaskSource(TransactionWithRegisteredTaskSource::FromTaskSource( - std::move(task_source))); + auto task_source_with_transaction = + TransactionWithRegisteredTaskSource::FromTaskSource( + std::move(task_source)); + task_source_with_transaction.task_source.WillReEnqueue( + TimeTicks::Now(), &task_source_with_transaction.transaction); + EnqueueTaskSource(std::move(task_source_with_transaction)); } } @@ -143,7 +147,7 @@ // |task| will be pushed to |sequence|, and |sequence| will be queued // to |priority_queue_| iff |sequence_should_be_queued| is true. - const bool sequence_should_be_queued = transaction.WillPushTask(); + const bool sequence_should_be_queued = transaction.ShouldBeQueued(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(sequence); @@ -153,7 +157,7 @@ } if (!task_tracker_->WillPostTaskNow(task, transaction.traits().priority())) return false; - transaction.PushTask(std::move(task)); + transaction.PushImmediateTask(std::move(task)); if (task_source) { bool should_wakeup = EnqueueTaskSource({std::move(task_source), std::move(transaction)}); @@ -355,7 +359,7 @@ if (task_tracker()->WillPostTask( &pump_message_task, TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) { auto transaction = message_pump_sequence_->BeginTransaction(); - const bool sequence_should_be_queued = transaction.WillPushTask(); + const bool sequence_should_be_queued = transaction.ShouldBeQueued(); DCHECK(sequence_should_be_queued) << "GetWorkFromWindowsMessageQueue() does not expect " "queueing of pump tasks."; @@ -363,7 +367,7 @@ std::move(message_pump_sequence_)); if (!registered_task_source) return nullptr; - transaction.PushTask(std::move(pump_message_task)); + transaction.PushImmediateTask(std::move(pump_message_task)); return registered_task_source; } }
diff --git a/base/task/thread_pool/priority_queue_unittest.cc b/base/task/thread_pool/priority_queue_unittest.cc index fe8ab50..a976d37 100644 --- a/base/task/thread_pool/priority_queue_unittest.cc +++ b/base/task/thread_pool/priority_queue_unittest.cc
@@ -43,7 +43,7 @@ task_environment.FastForwardBy(Microseconds(1)); scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( traits, nullptr, TaskSourceExecutionMode::kParallel); - sequence->BeginTransaction().PushTask( + sequence->BeginTransaction().PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); return sequence; }
diff --git a/base/task/thread_pool/sequence.cc b/base/task/thread_pool/sequence.cc index 6ebde82..b4892bf 100644 --- a/base/task/thread_pool/sequence.cc +++ b/base/task/thread_pool/sequence.cc
@@ -24,39 +24,50 @@ Sequence::Transaction::~Transaction() = default; -bool Sequence::Transaction::WillPushTask() const { - // A sequence should be queued if it's not already in the queue and the pool - // is not running any task from it. Otherwise, one of these must be true: - // - The Sequence is already queued, or, - // - A thread is running a Task from the Sequence. It is expected to reenqueue - // the Sequence once it's done running the Task. - // Access to |current_location_| can get racy between calls to WillRunTask() - // and WillPushTask(). WillRunTask() updates |current_location_| from - // kImmediateQueue to kInWorker, it can only be called on sequence when - // sequence is already in immediate queue so this behavior is always - // guaranteed. Hence, WillPushTask behavior won't be affected no matter if - // WillRunTask runs before or after it's called since it returns false - // whether |current_location_| is set to kImmediateQueue or kInWorker. +bool Sequence::Transaction::ShouldBeQueued() const { + // A sequence should be queued to the immediate queue after receiving a new + // immediate Task, or queued to or updated in the delayed queue after + // receiving a new delayed Task, if it's not already in the immediate queue + // and the pool is not running any task from it. WillRunTask() can racily + // modify |current_location_|, but always from |kImmediateQueue| to + // |kInWorker|. In that case, ShouldBeQueued() returns false whether + // WillRunTask() runs immediately before or after. + // When pushing a delayed task, a sequence can become ready at any time, + // triggering OnBecomeReady() which racily modifies |current_location_| + // from kDelayedQueue to kImmediateQueue. In that case this function may + // return true which immediately becomes incorrect. This race is resolved + // outside of this class. See my comment on ShouldBeQueued() in the header + // file. auto current_location = sequence()->current_location_.load(std::memory_order_relaxed); - if (current_location == Sequence::SequenceLocation::kImmediateQueue) { - return false; - } - - if (current_location == Sequence::SequenceLocation::kInWorker) { + if (current_location == Sequence::SequenceLocation::kImmediateQueue || + current_location == Sequence::SequenceLocation::kInWorker) { return false; } return true; } -void Sequence::Transaction::PushTask(Task task) { +bool Sequence::Transaction::TopDelayedTaskWillChange(Task& delayed_task) const { + if (sequence()->IsEmpty()) + return true; + return delayed_task.latest_delayed_run_time() < + sequence()->delayed_queue_.top().latest_delayed_run_time(); +} + +void Sequence::Transaction::PushImmediateTask(Task task) { // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 // for details. CHECK(task.task); DCHECK(!task.queue_time.is_null()); - bool should_be_queued = WillPushTask(); + auto current_location = + sequence()->current_location_.load(std::memory_order_relaxed); + bool was_unretained = + sequence()->IsEmpty() && + current_location != Sequence::SequenceLocation::kInWorker; + bool queue_was_empty = sequence()->queue_.empty(); + task.task = sequence()->traits_.shutdown_behavior() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? MakeCriticalClosure( @@ -64,56 +75,156 @@ /*is_immediate=*/task.delayed_run_time.is_null()) : std::move(task.task); - if (sequence()->queue_.empty()) { - sequence()->ready_time_.store(task.GetDesiredExecutionTime(), - std::memory_order_relaxed); - } sequence()->queue_.push(std::move(task)); - if (should_be_queued) + if (queue_was_empty) { + sequence()->ready_time_.store(sequence()->GetNextReadyTime(), + std::memory_order_relaxed); + } + + if (current_location == Sequence::SequenceLocation::kDelayedQueue || + current_location == Sequence::SequenceLocation::kNone) { sequence()->current_location_.store( Sequence::SequenceLocation::kImmediateQueue, std::memory_order_relaxed); + } // AddRef() matched by manual Release() when the sequence has no more tasks // to run (in DidProcessTask() or Clear()). - if (should_be_queued && sequence()->task_runner()) + if (was_unretained && sequence()->task_runner()) sequence()->task_runner()->AddRef(); } +void Sequence::Transaction::PushDelayedTask(Task task) { + // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 + // for details. + CHECK(task.task); + DCHECK(!task.queue_time.is_null()); + DCHECK(!task.delayed_run_time.is_null()); + + auto current_location = + sequence()->current_location_.load(std::memory_order_relaxed); + bool was_unretained = + sequence()->IsEmpty() && + current_location != Sequence::SequenceLocation::kInWorker; + + task.task = + sequence()->traits_.shutdown_behavior() == + TaskShutdownBehavior::BLOCK_SHUTDOWN + ? MakeCriticalClosure(task.posted_from, std::move(task.task), false) + : std::move(task.task); + + sequence()->delayed_queue_.insert(std::move(task)); + + if (sequence()->queue_.empty()) { + sequence()->ready_time_.store(sequence()->GetNextReadyTime(), + std::memory_order_relaxed); + } + + auto expected_location = Sequence::SequenceLocation::kNone; + sequence()->current_location_.compare_exchange_strong( + expected_location, Sequence::SequenceLocation::kDelayedQueue, + std::memory_order_relaxed); + + // AddRef() matched by manual Release() when the sequence has no more tasks + // to run (in DidProcessTask() or Clear()). + if (was_unretained && sequence()->task_runner()) + sequence()->task_runner()->AddRef(); +} + +// Delayed tasks are ordered by latest_delayed_run_time(). The top task may +// not be the first task eligible to run, but tasks will always become ripe +// before their latest_delayed_run_time(). +bool Sequence::DelayedTaskGreater::operator()(const Task& lhs, + const Task& rhs) const { + TimeTicks lhs_latest_delayed_run_time = lhs.latest_delayed_run_time(); + TimeTicks rhs_latest_delayed_run_time = rhs.latest_delayed_run_time(); + return std::tie(lhs_latest_delayed_run_time, lhs.sequence_num) > + std::tie(rhs_latest_delayed_run_time, rhs.sequence_num); +} + TaskSource::RunStatus Sequence::WillRunTask() { // There should never be a second call to WillRunTask() before DidProcessTask // since the RunStatus is always marked a saturated. - DCHECK(current_location_.load(std::memory_order_relaxed) != - Sequence::SequenceLocation::kInWorker); + + DCHECK_EQ(current_location_.load(std::memory_order_relaxed), + Sequence::SequenceLocation::kImmediateQueue); // It's ok to access |current_location_| outside of a Transaction since // WillRunTask() is externally synchronized, always called in sequence with - // TakeTask() and DidProcessTask() and only called if sequence is in immediate - // queue. Even though it can get racy with WillPushTask()/PushTask(), the - // behavior of each function is not affected as explained in WillPushTask(). + // OnBecomeReady(), TakeTask(), WillReEnqueue() and DidProcessTask() and only + // called if sequence is in immediate queue. Even though it can get racy with + // ShouldBeQueued()/PushImmediateTask()/PushDelayedTask(), the behavior of + // each function is not affected as explained in ShouldBeQueued(). current_location_.store(Sequence::SequenceLocation::kInWorker, std::memory_order_relaxed); return RunStatus::kAllowedSaturated; } +void Sequence::OnBecomeReady() { + // This should always be called from a worker thread at a time and it will be + // called only before WillRunTask(). + DCHECK(current_location_.load(std::memory_order_relaxed) == + Sequence::SequenceLocation::kDelayedQueue); + + // It's ok to access |current_location_| outside of a Transaction since + // OnBecomeReady() is externally synchronized and always called in sequence + // with WillRunTask(). This can get racy with + // ShouldBeQueued()/PushDelayedTask(). See comment in + // ShouldBeQueued() to see how races with this function are resolved. + current_location_.store(Sequence::SequenceLocation::kImmediateQueue, + std::memory_order_relaxed); +} + size_t Sequence::GetRemainingConcurrency() const { return 1; } +Task Sequence::TakeNextImmediateTask() { + Task next_task = std::move(queue_.front()); + queue_.pop(); + return next_task; +} + +Task Sequence::TakeEarliestTask() { + if (queue_.empty()) + return delayed_queue_.take_top(); + + if (delayed_queue_.empty()) + return TakeNextImmediateTask(); + + // Both queues contain at least a task. Decide from which one the task should + // be taken. + if (queue_.front().queue_time <= + delayed_queue_.top().latest_delayed_run_time()) + return TakeNextImmediateTask(); + + return delayed_queue_.take_top(); +} + +TimeTicks Sequence::GetNextReadyTime() { + if (queue_.empty()) + return delayed_queue_.top().latest_delayed_run_time(); + + if (delayed_queue_.empty()) + return queue_.front().queue_time; + + return std::min(queue_.front().queue_time, + delayed_queue_.top().latest_delayed_run_time()); +} + Task Sequence::TakeTask(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); DCHECK(current_location_.load(std::memory_order_relaxed) == Sequence::SequenceLocation::kInWorker); - DCHECK(!queue_.empty()); - DCHECK(queue_.front().task); + DCHECK(!queue_.empty() || !delayed_queue_.empty()); - auto next_task = std::move(queue_.front()); - queue_.pop(); - if (!queue_.empty()) { - ready_time_.store(queue_.front().queue_time, std::memory_order_relaxed); - } + auto next_task = TakeEarliestTask(); + + if (!IsEmpty()) + ready_time_.store(GetNextReadyTime(), std::memory_order_relaxed); + return next_task; } @@ -125,21 +236,57 @@ Sequence::SequenceLocation::kInWorker); // See comment on TaskSource::task_runner_ for lifetime management details. - if (queue_.empty()) { + if (IsEmpty()) { ReleaseTaskRunner(); current_location_.store(Sequence::SequenceLocation::kNone, std::memory_order_relaxed); return false; } - current_location_.store(Sequence::SequenceLocation::kImmediateQueue, - std::memory_order_relaxed); // Let the caller re-enqueue this non-empty Sequence regardless of // |run_result| so it can continue churning through this Sequence's tasks and // skip/delete them in the proper scope. return true; } +bool Sequence::WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction) { + CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + // This should always be called from a worker thread and it will be + // called after DidProcessTask(). + DCHECK(current_location_.load(std::memory_order_relaxed) == + Sequence::SequenceLocation::kInWorker); + + bool has_ready_tasks = HasReadyTasks(now); + if (has_ready_tasks) { + current_location_.store(Sequence::SequenceLocation::kImmediateQueue, + std::memory_order_relaxed); + } else { + current_location_.store(Sequence::SequenceLocation::kDelayedQueue, + std::memory_order_relaxed); + } + + return has_ready_tasks; +} + +bool Sequence::HasReadyTasks(TimeTicks now) const { + return HasRipeDelayedTasks(now) || HasImmediateTasks(); +} + +bool Sequence::HasRipeDelayedTasks(TimeTicks now) const { + if (delayed_queue_.empty()) + return false; + + if (!delayed_queue_.top().task.MaybeValid()) + return true; + + return delayed_queue_.top().earliest_delayed_run_time() <= now; +} + +bool Sequence::HasImmediateTasks() const { + return !queue_.empty(); +} + TaskSourceSortKey Sequence::GetSortKey( bool /* disable_fair_scheduling */) const { return TaskSourceSortKey(priority_racy(), @@ -149,19 +296,24 @@ Task Sequence::Clear(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); // See comment on TaskSource::task_runner_ for lifetime management details. - if (!queue_.empty() && current_location_.load(std::memory_order_relaxed) != - Sequence::SequenceLocation::kInWorker) { + if (!IsEmpty() && current_location_.load(std::memory_order_relaxed) != + Sequence::SequenceLocation::kInWorker) { ReleaseTaskRunner(); } - return Task(FROM_HERE, - base::BindOnce( - [](base::queue<Task> queue) { - while (!queue.empty()) - queue.pop(); - }, - std::move(queue_)), - TimeTicks(), TimeDelta()); + return Task( + FROM_HERE, + base::BindOnce( + [](base::queue<Task> queue, + base::IntrusiveHeap<Task, DelayedTaskGreater> delayed_queue) { + while (!queue.empty()) + queue.pop(); + + while (!delayed_queue.empty()) + delayed_queue.pop(); + }, + std::move(queue_), std::move(delayed_queue_)), + TimeTicks(), TimeDelta()); } void Sequence::ReleaseTaskRunner() { @@ -191,5 +343,9 @@ return current_location_.load(std::memory_order_relaxed); } +bool Sequence::IsEmpty() const { + return queue_.empty() && delayed_queue_.empty(); +} + } // namespace internal } // namespace base
diff --git a/base/task/thread_pool/sequence.h b/base/task/thread_pool/sequence.h index 8e65d695..c0694ca0 100644 --- a/base/task/thread_pool/sequence.h +++ b/base/task/thread_pool/sequence.h
@@ -8,6 +8,7 @@ #include <stddef.h> #include "base/base_export.h" +#include "base/containers/intrusive_heap.h" #include "base/containers/queue.h" #include "base/sequence_token.h" #include "base/task/task_traits.h" @@ -20,8 +21,15 @@ namespace base { namespace internal { -// A Sequence holds slots each containing up to a single Task that must be -// executed in posting order. +// A Sequence is intended to hold delayed tasks and immediate tasks. +// Delayed tasks are held in a prority_queue until they are ripe and +// immediate tasks in a simple fifo queue. +// Sequence::PushTask is responsible for putting a task into the right +// queue depending on its nature. +// When Sequence::TakeTask is called, we select the next appropriate task +// from both queues and return it. +// Each queue holds slots each containing up to a single Task that must be +// executed in posting/runtime order. // // In comments below, an "empty Sequence" is a Sequence with no slot. // @@ -34,10 +42,9 @@ // dangling reference cycle would only occur should they release their reference // to it while it's not empty. In other words, it is only correct for them to // release it after PopTask() returns false to indicate it was made empty by -// that call (in which case the next PushTask() will return true to indicate to -// the caller that the Sequence should be re-enqueued for execution). -// -// This class is thread-safe. +// that call (in which case the next PushImmediateTask() will return true to +// indicate to the caller that the Sequence should be re-enqueued for +// execution). This class is thread-safe. class BASE_EXPORT Sequence : public TaskSource { public: // A Transaction can perform multiple operations atomically on a @@ -51,13 +58,26 @@ Transaction& operator=(const Transaction&) = delete; ~Transaction(); - // Returns true if the sequence would need to be queued after receiving a - // new Task. - [[nodiscard]] bool WillPushTask() const; + // Returns true if the sequence would need to be queued in the + // immediate/delayed queue after receiving a new immediate/delayed Task. + // Thread-safe but the returned value may immediately be obsolete when + // pushing a delayed task since a sequence can become ready at any time; + // therefore it must be externally synchronized to prevent races against + // OnBecomeReady(). + [[nodiscard]] bool ShouldBeQueued() const; - // Adds |task| in a new slot at the end of the Sequence. This must only be - // called after invoking WillPushTask(). - void PushTask(Task task); + // Returns true if the task to be posted will change the sequence + // delayed_queue top. + bool TopDelayedTaskWillChange(Task& delayed_task) const; + + // Adds immediate |task| to the end of this sequence. This must only + // be called after invoking ShouldBeQueued(). + void PushImmediateTask(Task task); + + // Adds a delayed |task| in this sequence to be prioritized based on it's + // delayed run time. This must only be called after invoking + // TopDelayedTaskWillChange()/ShouldBeQueued(). + void PushDelayedTask(Task task); Sequence* sequence() const { return static_cast<Sequence*>(task_source()); } @@ -110,22 +130,54 @@ SequenceLocation GetCurrentLocationForTesting(); + void OnBecomeReady() override; + private: ~Sequence() override; + struct DelayedTaskGreater { + bool operator()(const Task& lhs, const Task& rhs) const; + }; + // TaskSource: RunStatus WillRunTask() override; Task TakeTask(TaskSource::Transaction* transaction) override; Task Clear(TaskSource::Transaction* transaction) override; bool DidProcessTask(TaskSource::Transaction* transaction) override; + bool WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction) override; + + // Selects the earliest task to run, either from immediate or + // delayed queue and return it. + // Expects this sequence to have at least one task that can run + // immediately. + Task TakeEarliestTask(); + + // Get and return next task from immediate queue + Task TakeNextImmediateTask(); + + // Determine next ready time and set ready time to it + TimeTicks GetNextReadyTime(); + + // Returns true if there are immediate tasks + bool HasImmediateTasks() const; + + // Returns true if there are tasks ripe for execution in the delayed queue + bool HasRipeDelayedTasks(TimeTicks now) const; + + // Returns true if tasks ready to be executed + bool HasReadyTasks(TimeTicks now) const; + + bool IsEmpty() const; // Releases reference to TaskRunner. void ReleaseTaskRunner(); const SequenceToken token_ = SequenceToken::Create(); - // Queue of tasks to execute. + // Queues of tasks to execute. base::queue<Task> queue_; + base::IntrusiveHeap<Task, DelayedTaskGreater> delayed_queue_; std::atomic<TimeTicks> ready_time_{TimeTicks()};
diff --git a/base/task/thread_pool/sequence_unittest.cc b/base/task/thread_pool/sequence_unittest.cc index bddc48b0..8772fcd 100644 --- a/base/task/thread_pool/sequence_unittest.cc +++ b/base/task/thread_pool/sequence_unittest.cc
@@ -25,9 +25,16 @@ MOCK_METHOD0(Run, void()); }; -Task CreateTask(MockTask* mock_task) { - return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), - TimeTicks::Now(), TimeDelta()); +Task CreateTask(MockTask* mock_task, TimeTicks now = TimeTicks::Now()) { + return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), now, + TimeDelta()); +} + +Task CreateDelayedTask(MockTask* mock_task, + TimeDelta delay, + TimeTicks now = TimeTicks::Now()) { + return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), now, + delay); } void ExpectMockTask(MockTask* mock_task, Task* task) { @@ -50,19 +57,19 @@ TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - // Push task A in the sequence. PushTask() should return true since it's the - // first task-> - EXPECT_TRUE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_a)); + // Push task A in the sequence. PushImmediateTask() should return true since + // it's the first task-> + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); - // Push task B, C and D in the sequence. PushTask() should return false - // since there is already a task in a sequence. - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_b)); - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_c)); - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_d)); + // Push task B, C and D in the sequence. PushImmediateTask() should return + // false since there is already a task in a sequence. + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_c)); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_d)); // Take the task in front of the sequence. It should be task A. auto registered_task_source = @@ -75,8 +82,10 @@ // Remove the empty slot. Task B should now be in front. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); - EXPECT_FALSE(sequence_transaction.WillPushTask()); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_b, &task.value()); @@ -84,8 +93,10 @@ // Remove the empty slot. Task C should now be in front. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); - EXPECT_FALSE(sequence_transaction.WillPushTask()); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_c, &task.value()); @@ -93,10 +104,12 @@ // Remove the empty slot. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); // Push task E in the sequence. - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_e)); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_e)); // Task D should be in front. registered_task_source.WillRunTask(); @@ -106,7 +119,9 @@ // Remove the empty slot. Task E should now be in front. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); - EXPECT_FALSE(sequence_transaction.WillPushTask()); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_e, &task.value()); @@ -114,7 +129,7 @@ // Remove the empty slot. The sequence should now be empty. EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - EXPECT_TRUE(sequence_transaction.WillPushTask()); + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); } // Verifies the sort key of a BEST_EFFORT sequence that contains one task. @@ -126,7 +141,8 @@ TaskSourceExecutionMode::kParallel); Sequence::Transaction best_effort_sequence_transaction( best_effort_sequence->BeginTransaction()); - best_effort_sequence_transaction.PushTask(std::move(best_effort_task)); + best_effort_sequence_transaction.PushImmediateTask( + std::move(best_effort_task)); // Get the sort key. const TaskSourceSortKey best_effort_sort_key = @@ -160,7 +176,7 @@ TaskSourceExecutionMode::kParallel); Sequence::Transaction foreground_sequence_transaction( foreground_sequence->BeginTransaction()); - foreground_sequence_transaction.PushTask(std::move(foreground_task)); + foreground_sequence_transaction.PushImmediateTask(std::move(foreground_task)); // Get the sort key. const TaskSourceSortKey foreground_sort_key = @@ -189,7 +205,7 @@ scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - sequence_transaction.PushTask( + sequence_transaction.PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); auto registered_task_source = @@ -205,7 +221,7 @@ scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - sequence_transaction.PushTask( + sequence_transaction.PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); auto registered_task_source = @@ -249,23 +265,24 @@ Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - // Push task A in the sequence. WillPushTask() should return true - // since sequence is empty. - EXPECT_TRUE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_a)); + // Push task A in the sequence. ShouldBeQueued() should return + // true since sequence is empty. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); - // WillPushTask is called when a new task is about to be pushed and sequence - // will be put in the priority queue or is already in it. + // ShouldBeQueued()is called when a new task is about to be + // pushed and sequence will be put in the priority queue or is already in it. EXPECT_EQ(sequence->GetCurrentLocationForTesting(), Sequence::SequenceLocation::kImmediateQueue); - // Push task B into the sequence. WillPushTask() should return false. - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_b)); + // Push task B into the sequence. ShouldBeQueued() should + // return false. + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); - // WillPushTask is called when a new task is about to be pushed and sequence - // will be put in the priority queue or is already in it. Sequence location - // should be kImmediateQueue. + // ShouldBeQueued()is called when a new task is about to be + // pushed and sequence will be put in the priority queue or is already in it. + // Sequence location should be kImmediateQueue. EXPECT_EQ(sequence->GetCurrentLocationForTesting(), Sequence::SequenceLocation::kImmediateQueue); @@ -286,6 +303,9 @@ // Remove the empty slot. Sequence still has task B. This should return true. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + // Sequence can run immediately. + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); // Sequence is not empty so it will be returned to the priority queue and its // location should be updated to kImmediateQueue. @@ -323,10 +343,10 @@ Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - // Push task A in the sequence. WillPushTask() should return true - // since sequence is empty. - EXPECT_TRUE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_a)); + // Push task A in the sequence. ShouldBeQueued() should return + // true since sequence is empty. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); @@ -343,9 +363,10 @@ EXPECT_EQ(sequence->GetCurrentLocationForTesting(), Sequence::SequenceLocation::kInWorker); - // Push task B into the sequence. WillPushTask() should return false. - EXPECT_FALSE(sequence_transaction.WillPushTask()); - sequence_transaction.PushTask(CreateTask(&mock_task_b)); + // Push task B into the sequence. ShouldBeQueued() should + // return false. + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); // Sequence is still being processed by a worker so pushing a new task // shouldn't change its location. We should expect it to still be in worker. @@ -354,6 +375,9 @@ // Remove the empty slot. Sequence still has task B. This should return true. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + // Sequence can run immediately. + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), + &sequence_transaction)); // Sequence is not empty so it will be returned to the priority queue and its // location should be updated to kImmediateQueue. @@ -375,5 +399,345 @@ Sequence::SequenceLocation::kNone); } +// Verify that the sequence handle delayed tasks and sets locations +// appropriately +TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { + TimeTicks now = TimeTicks::Now(); + + testing::StrictMock<MockTask> mock_task_a; + testing::StrictMock<MockTask> mock_task_b; + testing::StrictMock<MockTask> mock_task_c; + testing::StrictMock<MockTask> mock_task_d; + + scoped_refptr<Sequence> sequence = + MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr, + TaskSourceExecutionMode::kParallel); + + Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + + // Push task A in the sequence. + auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now); + // TopDelayedTaskWillChange(delayed_task_a) should return + // true since sequence is empty. + EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_a)); + // ShouldBeQueued() should return true since sequence is empty. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + // PushImmediateTask(...) should be used for immediate tasks only + // EXPECT_DCHECK_DEATH({ + // sequence_transaction.PushImmediateTask(delayed_task_a); + // }); + sequence_transaction.PushDelayedTask(std::move(delayed_task_a)); + + // Sequence doesn't have immediate tasks so its location should be the delayed + // queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kDelayedQueue); + + // Push task B into the sequence. + auto delayed_task_b = CreateDelayedTask(&mock_task_b, Milliseconds(10), now); + // TopDelayedTaskWillChange(...) should return true since task b runtime is + // earlier than task a's. + EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_b)); + // ShouldBeQueued() should return true since task B is earlier + // than task A. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushDelayedTask(std::move(delayed_task_b)); + + // Sequence doesn't have immediate tasks so its location should be the delayed + // queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kDelayedQueue); + + // Time advances by 15s. + now += Milliseconds(15); + + // Set sequence to ready + sequence->OnBecomeReady(); + + // Sequence is about to be run so its location should change to immediate + // queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + auto registered_task_source = + RegisteredTaskSource::CreateForTesting(sequence); + registered_task_source.WillRunTask(); + + // WillRunTask() has been called so sequence location should be kInWorker. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kInWorker); + + // Take the task in front of the sequence. It should be task B. + absl::optional<Task> task = + registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_b, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task A should now be in front. Sequence is not empty + // so this should return true. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Task A is still not ready so this should return false and location + // should be set to delayed queue + EXPECT_FALSE( + registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kDelayedQueue); + + // Push task C into the sequence. + auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(1), now); + // TopDelayedTaskWillChange(...) should return true since task c runtime is + // earlier than task a's. + EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_c)); + // ShouldBeQueued() should return true since task C is earlier + // than task A. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushDelayedTask(std::move(delayed_task_c)); + + // Push task D into the sequence. + auto delayed_task_d = CreateDelayedTask(&mock_task_d, Milliseconds(1), now); + // TopDelayedTaskWillChange(...) should return false since task d queue time + // is later than task c's. + EXPECT_FALSE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_d)); + sequence_transaction.PushDelayedTask(std::move(delayed_task_d)); + + // Time advances by 2ms. + now += Milliseconds(2); + // Set sequence to ready + registered_task_source.OnBecomeReady(); + + registered_task_source.WillRunTask(); + + // This should return task C + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_c, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task D should now be in front. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Task D is ready so this should return true and location + // should be set to immediate queue + EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + registered_task_source.WillRunTask(); + + // This should return task D + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_d, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task A should now be in front. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Time advances by 10ms. + now += Milliseconds(10); + + // Task A is ready so this should return true and location + // should be set to immediate queue + EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + registered_task_source.WillRunTask(); + + // This should return task A since it's ripe + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_a, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Sequence should be empty now. + EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kNone); + + // Sequence is empty so there should be no task to execute. + // This should return true + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); +} + +// Verify that the sequence handle delayed and immediate tasks and sets +// locations appropriately +TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { + TimeTicks now = TimeTicks::Now(); + + testing::StrictMock<MockTask> mock_task_a; + testing::StrictMock<MockTask> mock_task_b; + testing::StrictMock<MockTask> mock_task_c; + testing::StrictMock<MockTask> mock_task_d; + + scoped_refptr<Sequence> sequence = + MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr, + TaskSourceExecutionMode::kParallel); + + Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + + // Starting with a delayed task + // Push task A in the sequence. + auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now); + // TopDelayedTaskWillChange(delayed_task_a) should return + // true since sequence is empty. + EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_a)); + // ShouldBeQueued() should return true since sequence is empty. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushDelayedTask(std::move(delayed_task_a)); + + // Sequence doesn't have immediate tasks so its location should be the delayed + // queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kDelayedQueue); + + // Push an immediate task while a delayed task is already sitting in the + // delayed queue. This should prompt a move to the immediate queue. + // Push task B in the sequence. + auto task_b = CreateTask(&mock_task_b, now); + // ShouldBeQueued() should return true since sequence is in delayed queue. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(std::move(task_b)); + // Sequence doesn't have immediate tasks so its location should will change + // to immediate queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + auto registered_task_source = + RegisteredTaskSource::CreateForTesting(sequence); + + // Prepare to run a task. + registered_task_source.WillRunTask(); + + // WillRunTask() has been called so sequence location should be kInWorker. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kInWorker); + + // Take the task in front of the sequence. It should be task B. + absl::optional<Task> task = + registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_b, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task A should now be in front. Sequence is not empty + // so this should return true. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Time advances by 21ms. + now += Milliseconds(21); + + // Task A is ready so this should return true and location + // should be set to immediate queue. + EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + registered_task_source.WillRunTask(); + + // WillRunTask() has been called so sequence location should be kInWorker. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kInWorker); + + // Push a delayed task while sequence is being run by a worker. + // Push task C in the sequence. + auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(5), now); + // TopDelayedTaskWillChange(delayed_task_c) should return + // false since task A is ripe and earlier than task C. + EXPECT_FALSE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_c)); + // ShouldBeQueued() should return false since sequence is in worker. + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushDelayedTask(std::move(delayed_task_c)); + + // Sequence is in worker. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kInWorker); + + // This should return task A + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_a, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task C should now be in front. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Time advances by 2ms. + now += Milliseconds(2); + + // Task C is not ready so this should return false and location should be set + // to delayed queue. + EXPECT_FALSE( + registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kDelayedQueue); + + // Time advances by 4ms. Task C becomes ready. + now += Milliseconds(4); + + // Set sequence to ready + registered_task_source.OnBecomeReady(); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + // Push task D in the sequence while sequence is ready. + auto task_d = CreateTask(&mock_task_d, now); + // ShouldBeQueued() should return false since sequence is already in immediate + // queue. + EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + sequence_transaction.PushImmediateTask(std::move(task_d)); + + // Sequence should be in immediate queue. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + registered_task_source.WillRunTask(); + + // WillRunTask() has been called so sequence location should be kInWorker. + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kInWorker); + + // This should return task C since was ready before Task D was posted. + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_c, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Task D should now be in front. + EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); + + // Task D should be run so this should return true and location should be set + // to immediate queue. + EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kImmediateQueue); + + registered_task_source.WillRunTask(); + + // This should return task D since it's immediate. + task = registered_task_source.TakeTask(&sequence_transaction); + ExpectMockTask(&mock_task_d, &task.value()); + EXPECT_FALSE(task->queue_time.is_null()); + + // Remove the empty slot. Sequence should be empty. + EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); + EXPECT_EQ(sequence->GetCurrentLocationForTesting(), + Sequence::SequenceLocation::kNone); +} + +// Test that PushDelayedTask method is used only for delayed tasks +TEST(ThreadPoolSequenceTest, TestPushDelayedTaskMethodUsage) { + testing::StrictMock<MockTask> mock_task_a; + + scoped_refptr<Sequence> sequence = + MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr, + TaskSourceExecutionMode::kParallel); + + Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + + // Push task B in the sequence. + auto task_a = CreateTask(&mock_task_a); + // ShouldBeQueued() should return true since sequence is empty. + EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + // PushDelayedTask(...) should be used for delayed tasks only. + EXPECT_DCHECK_DEATH( + { sequence_transaction.PushDelayedTask(std::move(task_a)); }); +} + } // namespace internal } // namespace base
diff --git a/base/task/thread_pool/task.h b/base/task/thread_pool/task.h index 0920049..0489e27 100644 --- a/base/task/thread_pool/task.h +++ b/base/task/thread_pool/task.h
@@ -50,8 +50,6 @@ Task& operator=(Task&& other); - void SetScheduled(); - // Required by IntrusiveHeap. void SetHeapHandle(const HeapHandle& handle) {}
diff --git a/base/task/thread_pool/task_source.cc b/base/task/thread_pool/task_source.cc index bdc49ad..41dd50a 100644 --- a/base/task/thread_pool/task_source.cc +++ b/base/task/thread_pool/task_source.cc
@@ -110,6 +110,13 @@ return *this; } +void RegisteredTaskSource::OnBecomeReady() { +#if DCHECK_IS_ON() + DCHECK_EQ(run_step_, State::kInitial); +#endif // DCHECK_IS_ON() + task_source_->OnBecomeReady(); +} + TaskSource::RunStatus RegisteredTaskSource::WillRunTask() { TaskSource::RunStatus run_status = task_source_->WillRunTask(); #if DCHECK_IS_ON() @@ -143,6 +150,15 @@ return task_source_->DidProcessTask(transaction); } +bool RegisteredTaskSource::WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction) { + DCHECK(!transaction || transaction->task_source() == get()); +#if DCHECK_IS_ON() + DCHECK_EQ(State::kInitial, run_step_); +#endif // DCHECK_IS_ON() + return task_source_->WillReEnqueue(now, transaction); +} + RegisteredTaskSource::RegisteredTaskSource( scoped_refptr<TaskSource> task_source, TaskTracker* task_tracker)
diff --git a/base/task/thread_pool/task_source.h b/base/task/thread_pool/task_source.h index a5f653ec..d811365 100644 --- a/base/task/thread_pool/task_source.h +++ b/base/task/thread_pool/task_source.h
@@ -18,6 +18,7 @@ #include "base/task/thread_pool/task.h" #include "base/task/thread_pool/task_source_sort_key.h" #include "base/threading/sequence_local_storage_map.h" +#include "base/time/time.h" namespace base { namespace internal { @@ -45,8 +46,8 @@ // 1- It has new tasks that can run concurrently as a result of external // operations, e.g. posting a new task to an empty Sequence or increasing // max concurrency of a JobTaskSource; -// 2- A worker finished running a task from it and DidProcessTask() returned -// true; or +// 2- A worker finished running a task from it and both DidProcessTask() and +// WillReEnqueue() returned true; or // 3- A worker is about to run a task from it and WillRunTask() returned // kAllowedNotSaturated. // @@ -58,7 +59,8 @@ // with TakeTask(). // 3- (optional) Execute the task. // 4- Inform the task source that a task was processed with DidProcessTask(), -// and re-enqueue the task source iff requested. +// and re-enqueue the task source iff requested. The task source is ready to +// run immediately iff WillReEnqueue() returns true. // When a task source is registered multiple times, many overlapping chains of // operations may run concurrently, as permitted by WillRunTask(). This allows // tasks from the same task source to run in parallel. @@ -169,10 +171,10 @@ // Transaction because it is never mutated. ThreadPolicy thread_policy() const { return traits_.thread_policy(); } - // A reference to TaskRunner is only retained between PushTask() and when - // DidProcessTask() returns false, guaranteeing it is safe to dereference this - // pointer. Otherwise, the caller should guarantee such TaskRunner still - // exists before dereferencing. + // A reference to TaskRunner is only retained between + // PushImmediateTask()/PushDelayedTask() and when DidProcessTask() returns + // false, guaranteeing it is safe to dereference this pointer. Otherwise, the + // caller should guarantee such TaskRunner still exists before dereferencing. TaskRunner* task_runner() const { return task_runner_; } TaskSourceExecutionMode execution_mode() const { return execution_mode_; } @@ -182,10 +184,12 @@ virtual RunStatus WillRunTask() = 0; - // Implementations of TakeTask(), DidProcessTask() and Clear() must ensure - // proper synchronization iff |transaction| is nullptr. + // Implementations of TakeTask(), DidProcessTask(), WillReEnqueue(), and + // Clear() must ensure proper synchronization iff |transaction| is nullptr. virtual Task TakeTask(TaskSource::Transaction* transaction) = 0; virtual bool DidProcessTask(TaskSource::Transaction* transaction) = 0; + virtual bool WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction) = 0; // This may be called for each outstanding RegisteredTaskSource that's ready. // The implementation needs to support this being called multiple times; @@ -206,6 +210,7 @@ mutable CheckedLock lock_{UniversalPredecessor()}; private: + virtual void OnBecomeReady() = 0; friend class RefCountedThreadSafe<TaskSource>; friend class RegisteredTaskSource; @@ -230,7 +235,7 @@ // used by a single worker at a time. However, the same task source may be // registered several times, spawning multiple RegisteredTaskSources. A // RegisteredTaskSource resets to its initial state when WillRunTask() fails -// or after DidProcessTask(), so it can be used again. +// or after DidProcessTask() and WillReEnqueue(), so it can be used again. class BASE_EXPORT RegisteredTaskSource { public: RegisteredTaskSource(); @@ -261,6 +266,10 @@ // that indicates if the operation is allowed (TakeTask() can be called). TaskSource::RunStatus WillRunTask(); + // Informs this TaskSource that it has become ready to run and is being moved + // from delayed to immediate queue. + void OnBecomeReady(); + // Returns the next task to run from this TaskSource. This should be called // only after WillRunTask() returned RunStatus::kAllowed*. |transaction| is // optional and should only be provided if this operation is already part of @@ -274,6 +283,13 @@ // Returns true if the TaskSource should be queued after this operation. bool DidProcessTask(TaskSource::Transaction* transaction = nullptr); + // Must be called iff DidProcessTask() previously returns true . + // |transaction| is optional and should only be provided if this + // operation is already part of a transaction. Returns true if the + // TaskSource is ready to run immediately. + bool WillReEnqueue(TimeTicks now, + TaskSource::Transaction* transaction = nullptr); + // Returns a task that clears this TaskSource to make it empty. |transaction| // is optional and should only be provided if this operation is already part // of a transaction.
diff --git a/base/task/thread_pool/task_tracker_unittest.cc b/base/task/thread_pool/task_tracker_unittest.cc index cde9825..9b14bb6e 100644 --- a/base/task/thread_pool/task_tracker_unittest.cc +++ b/base/task/thread_pool/task_tracker_unittest.cc
@@ -122,7 +122,7 @@ post_and_queue_succeeded = tracker_->WillPostTask(&task_, sequence_->shutdown_behavior()); - sequence_->BeginTransaction().PushTask(std::move(task_)); + sequence_->BeginTransaction().PushImmediateTask(std::move(task_)); task_source_ = tracker_->RegisterTaskSource(std::move(sequence_)); post_and_queue_succeeded &= !!task_source_; @@ -958,7 +958,7 @@ { Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - sequence_transaction.PushTask(std::move(task)); + sequence_transaction.PushImmediateTask(std::move(task)); EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid()); } @@ -1158,7 +1158,7 @@ scoped_refptr<Sequence> sequence = test::CreateSequenceWithTask(std::move(task_1), default_traits); - sequence->BeginTransaction().PushTask(std::move(task_2)); + sequence->BeginTransaction().PushImmediateTask(std::move(task_2)); EXPECT_EQ(sequence, test::QueueAndRunTaskSource(&tracker_, sequence).Unregister()); }
diff --git a/base/task/thread_pool/test_utils.cc b/base/task/thread_pool/test_utils.cc index 308cc94..ea75cec 100644 --- a/base/task/thread_pool/test_utils.cc +++ b/base/task/thread_pool/test_utils.cc
@@ -107,7 +107,7 @@ TaskSourceExecutionMode execution_mode) { scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(traits, task_runner.get(), execution_mode); - sequence->BeginTransaction().PushTask(std::move(task)); + sequence->BeginTransaction().PushImmediateTask(std::move(task)); return sequence; } @@ -190,7 +190,7 @@ Task task, scoped_refptr<Sequence> sequence) { auto transaction = sequence->BeginTransaction(); - const bool sequence_should_be_queued = transaction.WillPushTask(); + const bool sequence_should_be_queued = transaction.ShouldBeQueued(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(std::move(sequence)); @@ -198,7 +198,7 @@ if (!task_source) return; } - transaction.PushTask(std::move(task)); + transaction.PushImmediateTask(std::move(task)); if (task_source) { thread_group_->PushTaskSourceAndWakeUpWorkers( {std::move(task_source), std::move(transaction)});
diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc index 11dd18a..a16db32 100644 --- a/base/task/thread_pool/thread_group.cc +++ b/base/task/thread_pool/thread_group.cc
@@ -162,6 +162,10 @@ ThreadGroup* destination_thread_group = delegate_->GetThreadGroupForTraits( transaction_with_task_source.transaction.traits()); + bool push_to_immediate_queue = + transaction_with_task_source.task_source.WillReEnqueue( + TimeTicks::Now(), &transaction_with_task_source.transaction); + if (destination_thread_group == this) { // Another worker that was running a task from this task source may have // reenqueued it already, in which case its heap_handle will be valid. It @@ -174,8 +178,10 @@ // reenqueue it inside the scope of the lock. auto sort_key = transaction_with_task_source.task_source->GetSortKey( disable_fair_scheduling_); - priority_queue_.Push(std::move(transaction_with_task_source.task_source), - sort_key); + if (push_to_immediate_queue) { + priority_queue_.Push( + std::move(transaction_with_task_source.task_source), sort_key); + } } // This is called unconditionally to ensure there are always workers to run // task sources in the queue. Some ThreadGroup implementations only invoke
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc index 14abd275..205ed148 100644 --- a/base/task/thread_pool/thread_pool_impl.cc +++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -410,7 +410,7 @@ bool ThreadPoolImpl::PostTaskWithSequenceNow(Task task, scoped_refptr<Sequence> sequence) { auto transaction = sequence->BeginTransaction(); - const bool sequence_should_be_queued = transaction.WillPushTask(); + const bool sequence_should_be_queued = transaction.ShouldBeQueued(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(sequence); @@ -420,7 +420,7 @@ } if (!task_tracker_->WillPostTaskNow(task, transaction.traits().priority())) return false; - transaction.PushTask(std::move(task)); + transaction.PushImmediateTask(std::move(task)); if (task_source) { const TaskTraits traits = transaction.traits(); GetThreadGroupForTraits(traits)->PushTaskSourceAndWakeUpWorkers(
diff --git a/base/task/thread_pool/worker_thread_unittest.cc b/base/task/thread_pool/worker_thread_unittest.cc index 156c735..1e31dda 100644 --- a/base/task/thread_pool/worker_thread_unittest.cc +++ b/base/task/thread_pool/worker_thread_unittest.cc
@@ -203,7 +203,7 @@ TimeTicks::Now(), TimeDelta()); EXPECT_TRUE(outer_->task_tracker_.WillPostTask( &task, sequence->shutdown_behavior())); - sequence_transaction.PushTask(std::move(task)); + sequence_transaction.PushImmediateTask(std::move(task)); } auto registered_task_source = outer_->task_tracker_.RegisterTaskSource(sequence); @@ -239,15 +239,19 @@ EXPECT_FALSE(registered_task_source); } else { EXPECT_TRUE(registered_task_source); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now())); // Verify the number of Tasks in |registered_task_source|. for (int i = 0; i < outer_->TasksPerSequence() - 1; ++i) { registered_task_source.WillRunTask(); IgnoreResult(registered_task_source.TakeTask()); - EXPECT_EQ(i == outer_->TasksPerSequence() - 2, - !registered_task_source.DidProcessTask()); + if (i < outer_->TasksPerSequence() - 2) { + EXPECT_TRUE(registered_task_source.DidProcessTask()); + EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now())); + } else { + EXPECT_FALSE(registered_task_source.DidProcessTask()); + } } - scoped_refptr<TaskSource> task_source = registered_task_source.Unregister(); { @@ -485,7 +489,7 @@ TimeTicks::Now(), TimeDelta()); EXPECT_TRUE( task_tracker_->WillPostTask(&task, sequence->shutdown_behavior())); - sequence->BeginTransaction().PushTask(std::move(task)); + sequence->BeginTransaction().PushImmediateTask(std::move(task)); auto registered_task_source = task_tracker_->RegisterTaskSource(std::move(sequence)); EXPECT_TRUE(registered_task_source);
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto index 344cd8f..58007f42 100644 --- a/base/tracing/protos/chrome_track_event.proto +++ b/base/tracing/protos/chrome_track_event.proto
@@ -747,6 +747,7 @@ optional EventType event_type = 1; optional bool has_high_latency = 2; + repeated string high_latency_stage = 3; } message ProcessSingleton {
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1 index 187a554..dd7881b 100644 --- a/build/fuchsia/linux_internal.sdk.sha1 +++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@ -9.20220809.0.1 +9.20220809.1.1
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc index 8293311..3dfe719 100644 --- a/cc/metrics/compositor_frame_reporter.cc +++ b/cc/metrics/compositor_frame_reporter.cc
@@ -94,8 +94,9 @@ constexpr int kEventLatencyHistogramBucketCount = 100; constexpr base::TimeDelta kHighLatencyMin = base::Milliseconds(75); -// Number of stages of the current PipelineReporter -constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount); +// Number of breakdown stages of the current PipelineReporter +constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount) - 1; + // Number of dispatch stages of the current EventLatency constexpr int kNumDispatchStages = static_cast<int>(EventMetrics::DispatchStage::kMaxValue); @@ -104,6 +105,8 @@ // since TimeDelta calculate based on microseconds instead of nanoseconds, // therefore, decimals of stage durations in microseconds may be lost.) constexpr double kWeightOfCurStageInPercent = 25; +// Used for comparing doubles +constexpr double kEpsilon = 0.001; std::string GetCompositorLatencyHistogramName( FrameReportType report_type, @@ -128,12 +131,11 @@ const EventMetrics& event_metrics) { auto* scroll_metrics = event_metrics.AsScroll(); auto* pinch_metrics = event_metrics.AsPinch(); - return base::StrCat( - {"EventLatency.", event_metrics.GetTypeName(), - scroll_metrics || pinch_metrics ? "." : "", - scroll_metrics - ? scroll_metrics->GetScrollTypeName() - : pinch_metrics ? pinch_metrics->GetPinchTypeName() : ""}); + return base::StrCat({"EventLatency.", event_metrics.GetTypeName(), + scroll_metrics || pinch_metrics ? "." : "", + scroll_metrics ? scroll_metrics->GetScrollTypeName() + : pinch_metrics ? pinch_metrics->GetPinchTypeName() + : ""}); } constexpr char kTraceCategory[] = @@ -154,6 +156,29 @@ 100; } +// Calculate new prediction of latency based on old prediction and current +// latency +base::TimeDelta PredictLatency(base::TimeDelta previous_prediction, + base::TimeDelta current_latency) { + return (kWeightOfCurStageInPercent * current_latency + + (100 - kWeightOfCurStageInPercent) * previous_prediction) / + 100; +} + +double DetermineHighestContribution( + double contribution_change, + double highest_contribution_change, + const std::string& stage_name, + std::vector<std::string>& high_latency_stages) { + if (std::abs(contribution_change - highest_contribution_change) < kEpsilon) { + high_latency_stages.push_back(stage_name); + } else if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + high_latency_stages = {stage_name}; + } + return highest_contribution_change; +} + } // namespace // CompositorFrameReporter::ProcessedBlinkBreakdown::Iterator ================== @@ -341,6 +366,16 @@ buffer_ready_available_); } +// CompositorFrameReporter::CompositorLatencyInfo ============================== + +CompositorFrameReporter::CompositorLatencyInfo::CompositorLatencyInfo() = + default; +CompositorFrameReporter::CompositorLatencyInfo::CompositorLatencyInfo( + base::TimeDelta init_value) + : top_level_stages(kNumOfStages, init_value), total_latency(init_value) {} +CompositorFrameReporter::CompositorLatencyInfo::~CompositorLatencyInfo() = + default; + // CompositorFrameReporter ===================================================== CompositorFrameReporter::CompositorFrameReporter( @@ -560,7 +595,8 @@ : dispatch_durations(num_dispatch_stages, base::Microseconds(-1)), transition_duration(base::Microseconds(-1)), compositor_durations(num_compositor_stages, base::Microseconds(-1)), - total_duration(base::Microseconds(-1)) {} + total_duration(base::Microseconds(-1)), + transition_name("") {} CompositorFrameReporter::EventLatencyInfo::~EventLatencyInfo() = default; void CompositorFrameReporter::StartStage( @@ -925,9 +961,9 @@ const int stage_type_index = blink_breakdown ? kBlinkBreakdownInitialIndex + static_cast<int>(*blink_breakdown) - : viz_breakdown - ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown) - : static_cast<int>(stage_type); + : viz_breakdown + ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown) + : static_cast<int>(stage_type); const int histogram_index = (stage_type_index == static_cast<int>(StageType::kTotalLatency) ? kStagesWithBreakdownCount + frame_sequence_tracker_type_index @@ -1119,6 +1155,10 @@ reporter->set_frame_type(ChromeFrameReporter::BACKFILL); } + for (auto stage : high_latency_substages_) { + reporter->add_high_latency_contribution_stage(stage); + } + // TODO(crbug.com/1086974): Set 'drop reason' if applicable. }); @@ -1248,20 +1288,19 @@ DiscardOldPartialUpdateReporters(); } -void CompositorFrameReporter::CalculateStageLatencyPrediction( - std::vector<base::TimeDelta>& previous_predictions) { +void CompositorFrameReporter::CalculateCompositorLatencyPrediction( + CompositorLatencyInfo& previous_predictions, + base::TimeDelta prediction_deviation_threshold) { // `stage_history_` should not be empty since we are calling this function // from DidPresentCompositorFrame(), which means there has to be some sort of // stage data. DCHECK(!stage_history_.empty()); - int index_of_total_latency = static_cast<int>(StageType::kTotalLatency); - - // The bad case of having `previous_predictions` being 0s should never happen - // since this function always only record the current PipelineReporter's - // duration if its duration is not 0s. Investigate if such rare case happens. - DCHECK(!previous_predictions[index_of_total_latency].is_zero()) - << "previous_predictions should theoretically never have duration of 0s "; + // If the bad case of having `total_latency` of `previous_predictions` happens + // then it would mess up the prediction calculation, therefore, we want to + // reset the prediction by setting everything back to -1 + if (previous_predictions.total_latency.is_zero()) + previous_predictions = CompositorLatencyInfo(base::Microseconds(-1)); base::TimeDelta total_pipeline_latency = stage_history_.back().end_time - stage_history_[0].start_time; @@ -1275,36 +1314,42 @@ // Note that `current_stage_durations` would always have the same length as // `previous_predictions`, since each index represent the breakdown stages of // the PipelineReporter listed at enum class, StageType. - std::vector<base::TimeDelta> current_stage_durations(kNumOfStages, - base::Microseconds(0)); + CompositorLatencyInfo current_stage_durations(base::Microseconds(0)); + current_stage_durations.total_latency = total_pipeline_latency; + for (auto stage : stage_history_) { + if (stage.stage_type == StageType::kTotalLatency) + continue; base::TimeDelta substageLatency = stage.end_time - stage.start_time; - current_stage_durations[static_cast<int>(stage.stage_type)] = - substageLatency; + current_stage_durations + .top_level_stages[static_cast<int>(stage.stage_type)] = substageLatency; } - current_stage_durations[index_of_total_latency] = total_pipeline_latency; // Do not record current pipeline details or update predictions if no frame // is submitted. if (current_stage_durations - [static_cast<int>( - StageType::kSubmitCompositorFrameToPresentationCompositorFrame)] - .is_zero()) + .top_level_stages[static_cast<int>( + StageType::kSubmitCompositorFrameToPresentationCompositorFrame)] + .is_zero()) return; // The previous prediction is initialized to be -1, so check if the current // PipelineReporter is the first reporter ever to be calculated. - if (previous_predictions[index_of_total_latency] == base::Microseconds(-1)) { + if (previous_predictions.total_latency == base::Microseconds(-1)) { previous_predictions = current_stage_durations; } else { - // TODO(crbug.com/1334823): Perform latency attribution. + if ((current_stage_durations.total_latency - + previous_predictions.total_latency) >= prediction_deviation_threshold) + FindHighLatencyAttribution(previous_predictions, current_stage_durations); for (int i = 0; i < kNumOfStages; i++) { - previous_predictions[i] = - (kWeightOfCurStageInPercent * current_stage_durations[i] + - (100 - kWeightOfCurStageInPercent) * previous_predictions[i]) / - 100; + previous_predictions.top_level_stages[i] = + PredictLatency(previous_predictions.top_level_stages[i], + current_stage_durations.top_level_stages[i]); } + previous_predictions.total_latency = + PredictLatency(previous_predictions.total_latency, + current_stage_durations.total_latency); } } @@ -1389,6 +1434,9 @@ base::TimeDelta stage_duration = stage_it->start_time - dispatch_end_time; actual_event_latency.transition_duration = stage_duration; actual_event_latency.total_duration += stage_duration; + actual_event_latency.transition_name = + EventLatencyTracingRecorder::GetDispatchToCompositorBreakdownName( + last_valid_stage, stage_it->stage_type); } } @@ -1404,8 +1452,14 @@ } } - // TODO(crbug.com/1334827): Implement attribution for the substage with the - // highest latency. + // High latency attribution. + if (predicted_event_latency.total_duration.is_positive() && + actual_event_latency.total_duration - + predicted_event_latency.total_duration >= + prediction_deviation_threshold) { + FindEventLatencyAttribution(event_metrics.get(), predicted_event_latency, + actual_event_latency); + } // Calculate new dispatch stage predictions. base::TimeDelta predicted_total_duration = base::Microseconds(0); @@ -1588,4 +1642,109 @@ return info; } +void CompositorFrameReporter::FindHighLatencyAttribution( + CompositorLatencyInfo& previous_predictions, + CompositorLatencyInfo& current_stage_durations) { + double contribution_change = -1; + double highest_contribution_change = -1; + std::vector<int> highest_contribution_change_index; + + for (int i = 0; i < kNumOfStages; i++) { + contribution_change = (current_stage_durations.top_level_stages[i] / + current_stage_durations.total_latency) - + (previous_predictions.top_level_stages[i] / + previous_predictions.total_latency); + + if (contribution_change > highest_contribution_change) { + highest_contribution_change = contribution_change; + highest_contribution_change_index = {i}; + } else if (std::abs(contribution_change - highest_contribution_change) < + kEpsilon) { + highest_contribution_change_index.push_back(i); + } + } + + // It is not expensive to go through vector of indexes again since it is + // usually very small (possibilities of breakdown stages having the same + // change contribution is small). + for (auto index : highest_contribution_change_index) { + high_latency_substages_.push_back( + GetStageName(static_cast<StageType>(index))); + } +} + +void CompositorFrameReporter::FindEventLatencyAttribution( + EventMetrics* event_metrics, + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + CompositorFrameReporter::EventLatencyInfo& actual_event_latency) { + if (!event_metrics) + return; + + std::vector<std::string> high_latency_stages; + double contribution_change = -1; + double highest_contribution_change = -1; + + // Check dispatch stage change + EventMetrics::DispatchStage dispatch_stage = + EventMetrics::DispatchStage::kGenerated; + base::TimeTicks dispatch_timestamp = + event_metrics->GetDispatchStageTimestamp(dispatch_stage); + while (dispatch_stage != EventMetrics::DispatchStage::kMaxValue) { + DCHECK(!dispatch_timestamp.is_null()); + auto end_stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(dispatch_stage) + 1); + base::TimeTicks end_timestamp = + event_metrics->GetDispatchStageTimestamp(end_stage); + while (end_timestamp.is_null() && + end_stage != EventMetrics::DispatchStage::kMaxValue) { + end_stage = static_cast<EventMetrics::DispatchStage>( + static_cast<int>(end_stage) + 1); + end_timestamp = event_metrics->GetDispatchStageTimestamp(end_stage); + } + if (end_timestamp.is_null()) + break; + + contribution_change = + (actual_event_latency + .dispatch_durations[static_cast<int>(end_stage) - 1] / + actual_event_latency.total_duration) - + (predicted_event_latency + .dispatch_durations[static_cast<int>(end_stage) - 1] / + predicted_event_latency.total_duration); + std::string dispatch_stage_name = + EventLatencyTracingRecorder::GetDispatchBreakdownName(dispatch_stage, + end_stage); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, dispatch_stage_name, + high_latency_stages); + + dispatch_stage = end_stage; + dispatch_timestamp = end_timestamp; + } + + // Check dispatch-to-compositor stage change + contribution_change = (actual_event_latency.transition_duration / + actual_event_latency.total_duration) - + (predicted_event_latency.transition_duration / + predicted_event_latency.total_duration); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, + actual_event_latency.transition_name, high_latency_stages); + + // Check compositor stage change + for (int i = 0; i < kNumOfStages; i++) { + contribution_change = (actual_event_latency.compositor_durations[i] / + actual_event_latency.total_duration) - + (predicted_event_latency.compositor_durations[i] / + predicted_event_latency.total_duration); + highest_contribution_change = DetermineHighestContribution( + contribution_change, highest_contribution_change, + GetStageName(static_cast<StageType>(i)), high_latency_stages); + } + + for (auto stage : high_latency_stages) { + event_metrics->SetHighLatencyStage(stage); + } +} + } // namespace cc
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h index 8743e4b..68116860 100644 --- a/cc/metrics/compositor_frame_reporter.h +++ b/cc/metrics/compositor_frame_reporter.h
@@ -153,6 +153,7 @@ base::TimeDelta transition_duration; std::vector<base::TimeDelta> compositor_durations; base::TimeDelta total_duration; + std::string transition_name; EventLatencyInfo(const int num_dispatch_stages, const int num_compositor_stages); EventLatencyInfo(const EventLatencyInfo&); @@ -243,6 +244,17 @@ base::TimeTicks swap_start_; }; + // Wrapper for all level of breakdown stages' prediction + struct CC_EXPORT CompositorLatencyInfo { + CompositorLatencyInfo(); + explicit CompositorLatencyInfo(base::TimeDelta init_value); + ~CompositorLatencyInfo(); + + std::vector<base::TimeDelta> top_level_stages; + // TODO(crbug.com/1334823): add viz and blink breakdown + base::TimeDelta total_latency; + }; + CompositorFrameReporter(const ActiveTrackers& active_trackers, const viz::BeginFrameArgs& args, bool should_report_histograms, @@ -344,12 +356,8 @@ is_accompanied_by_main_thread_update; } - void set_is_forked(bool is_forked) { - is_forked_ = is_forked; - } - void set_is_backfill(bool is_backfill) { - is_backfill_ = is_backfill; - } + void set_is_forked(bool is_forked) { is_forked_ = is_forked; } + void set_is_backfill(bool is_backfill) { is_backfill_ = is_backfill; } const viz::BeginFrameId& frame_id() const { return args_.frame_id; } @@ -370,8 +378,9 @@ // This function is called to calculate breakdown stage duration's prediction // based on the `previous_predictions` and update the `previous_predictions` // to the new prediction calculated. - void CalculateStageLatencyPrediction( - std::vector<base::TimeDelta>& previous_predictions); + void CalculateCompositorLatencyPrediction( + CompositorLatencyInfo& previous_predictions, + base::TimeDelta prediction_deviation_threshold); // Sets EventLatency stage duration predictions based on previous trace // durations using exponentially weighted averages. @@ -382,9 +391,20 @@ ReporterType get_reporter_type() { return reporter_type_; } void set_reporter_type_to_impl() { reporter_type_ = ReporterType::kImpl; } - void set_reporter_type_to_main() { reporter_type_ = ReporterType::kMain; } + const std::vector<std::string>& high_latency_substages_for_testing() { + return high_latency_substages_; + } + + void ClearHighLatencySubstagesForTesting() { + high_latency_substages_.clear(); + } + + std::vector<std::unique_ptr<EventMetrics>>& events_metrics_for_testing() { + return events_metrics_; + } + protected: void set_has_partial_update(bool has_partial_update) { has_partial_update_ = has_partial_update; @@ -437,6 +457,15 @@ // updates (see comments on EventMetrics::requires_main_thread_update_). EventMetrics::List TakeMainBlockedEventsMetrics(); + void FindHighLatencyAttribution( + CompositorLatencyInfo& previous_predictions, + CompositorLatencyInfo& current_stage_durations); + + void FindEventLatencyAttribution( + EventMetrics* event_metrics, + CompositorFrameReporter::EventLatencyInfo& predicted_event_latency, + CompositorFrameReporter::EventLatencyInfo& actual_event_latency); + // Whether UMA histograms should be reported or not. const bool should_report_histograms_; @@ -526,6 +555,8 @@ const GlobalMetricsTrackers global_trackers_; + std::vector<std::string> high_latency_substages_; + ReporterType reporter_type_; base::WeakPtrFactory<CompositorFrameReporter> weak_factory_{this};
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc index c39286d6..3969aa3 100644 --- a/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -202,8 +202,9 @@ TotalFrameCounter total_frame_counter_; std::unique_ptr<CompositorFrameReporter> pipeline_reporter_; + // Number of breakdown stages of the current PipelineReporter const int kNumOfStages = - static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount); + static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount) - 1; const int kNumDispatchStages = static_cast<int>(EventMetrics::DispatchStage::kMaxValue); const base::TimeDelta kLatencyPredictionDeviationThreshold = @@ -813,34 +814,54 @@ pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - int kNumOfStages = - static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount); - // predictions when this is the very first prediction - std::vector<base::TimeDelta> expected_latency_predictions1( - kNumOfStages - 1, base::Microseconds(3)); - expected_latency_predictions1.push_back(base::Microseconds(21)); + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1; + expected_latency_predictions1.top_level_stages = + std::vector<base::TimeDelta>(kNumOfStages, base::Microseconds(3)); + expected_latency_predictions1.total_latency = base::Microseconds(21); // predictions when there exists a previous prediction - std::vector<base::TimeDelta> expected_latency_predictions2 = { + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2; + expected_latency_predictions2.top_level_stages = { base::Microseconds(1), base::Microseconds(0), base::Microseconds(3), base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), - base::Microseconds(0), base::Microseconds(12)}; + base::Microseconds(0)}; + expected_latency_predictions2.total_latency = base::Microseconds(12); - std::vector<base::TimeDelta> actual_latency_predictions1( - kNumOfStages, base::Microseconds(-1)); - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions1); + // expected attribution for all 3 cases above + std::vector<std::string> expected_latency_attributions = {}; - std::vector<base::TimeDelta> actual_latency_predictions2 = { + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2; + actual_latency_predictions2.top_level_stages = { base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), - base::Microseconds(0), base::Microseconds(10)}; - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions2); + base::Microseconds(0)}; + actual_latency_predictions2.total_latency = base::Microseconds(10); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); - EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1); - EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2); + EXPECT_EQ(expected_latency_predictions1.top_level_stages, + actual_latency_predictions1.top_level_stages); + EXPECT_EQ(expected_latency_predictions1.total_latency, + actual_latency_predictions1.total_latency); + EXPECT_EQ(expected_latency_predictions2.top_level_stages, + actual_latency_predictions2.top_level_stages); + EXPECT_EQ(expected_latency_predictions2.total_latency, + actual_latency_predictions2.total_latency); + + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions2); pipeline_reporter_ = nullptr; } @@ -879,35 +900,55 @@ AdvanceNowByUs(0); pipeline_reporter_->TerminateFrame( - CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - - int kNumOfStages = - static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount); + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); // predictions when this is the very first prediction - std::vector<base::TimeDelta> expected_latency_predictions1( - kNumOfStages, base::Microseconds(-1)); + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1( + base::Microseconds(-1)); // predictions when there exists a previous prediction - std::vector<base::TimeDelta> expected_latency_predictions2 = { + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2; + expected_latency_predictions2.top_level_stages = { base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), - base::Microseconds(0), base::Microseconds(10)}; + base::Microseconds(0)}; + expected_latency_predictions2.total_latency = base::Microseconds(10); - std::vector<base::TimeDelta> actual_latency_predictions1( - kNumOfStages, base::Microseconds(-1)); - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions1); + // expected attribution for all 3 cases above + std::vector<std::string> expected_latency_attributions = {}; - std::vector<base::TimeDelta> actual_latency_predictions2 = { + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2; + actual_latency_predictions2.top_level_stages = { base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), - base::Microseconds(0), base::Microseconds(10)}; - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions2); + base::Microseconds(0)}; + actual_latency_predictions2.total_latency = base::Microseconds(10); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); - EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1); - EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2); + EXPECT_EQ(expected_latency_predictions1.top_level_stages, + actual_latency_predictions1.top_level_stages); + EXPECT_EQ(expected_latency_predictions1.total_latency, + actual_latency_predictions1.total_latency); + EXPECT_EQ(expected_latency_predictions2.top_level_stages, + actual_latency_predictions2.top_level_stages); + EXPECT_EQ(expected_latency_predictions2.total_latency, + actual_latency_predictions2.total_latency); + + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions2); pipeline_reporter_ = nullptr; } @@ -929,7 +970,7 @@ pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); - AdvanceNowByUs(1000000); + AdvanceNowByUs(10000000); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kActivation, Now()); @@ -948,37 +989,272 @@ pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - int kNumOfStages = - static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount); - // predictions when this is the very first prediction - std::vector<base::TimeDelta> expected_latency_predictions1 = { + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions1; + expected_latency_predictions1.top_level_stages = { base::Microseconds(10000000), base::Microseconds(5000000), - base::Microseconds(6000000), base::Microseconds(1000000), + base::Microseconds(6000000), base::Microseconds(10000000), base::Microseconds(0), base::Microseconds(2000000), - base::Microseconds(10000000), base::Microseconds(34000000)}; + base::Microseconds(10000000)}; + expected_latency_predictions1.total_latency = base::Microseconds(43000000); // predictions when there exists a previous prediction - std::vector<base::TimeDelta> expected_latency_predictions2 = { + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions2; + expected_latency_predictions2.top_level_stages = { base::Microseconds(2500000), base::Microseconds(1250000), - base::Microseconds(1500003), base::Microseconds(250000), + base::Microseconds(1500003), base::Microseconds(2500000), base::Microseconds(1), base::Microseconds(500002), - base::Microseconds(2500000), base::Microseconds(8500007)}; + base::Microseconds(2500000)}; + expected_latency_predictions2.total_latency = base::Microseconds(10750007); - std::vector<base::TimeDelta> actual_latency_predictions1( - kNumOfStages, base::Microseconds(-1)); - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions1); + // expected attribution for cases 1 above + std::vector<std::string> expected_latency_attributions1 = {}; - std::vector<base::TimeDelta> actual_latency_predictions2 = { + // expected attribution for case 2 above + std::vector<std::string> expected_latency_attributions2 = { + "EndCommitToActivation", + "SubmitCompositorFrameToPresentationCompositorFrame"}; + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions1( + base::Microseconds(-1)); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions1, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions1 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); + + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions2; + actual_latency_predictions2.top_level_stages = { base::Microseconds(1), base::Microseconds(0), base::Microseconds(4), base::Microseconds(0), base::Microseconds(2), base::Microseconds(3), - base::Microseconds(0), base::Microseconds(10)}; - pipeline_reporter_->CalculateStageLatencyPrediction( - actual_latency_predictions2); + base::Microseconds(0)}; + actual_latency_predictions2.total_latency = base::Microseconds(10); + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions2, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions2 = + pipeline_reporter_->high_latency_substages_for_testing(); + pipeline_reporter_->ClearHighLatencySubstagesForTesting(); - EXPECT_EQ(expected_latency_predictions1, actual_latency_predictions1); - EXPECT_EQ(expected_latency_predictions2, actual_latency_predictions2); + EXPECT_EQ(expected_latency_predictions1.top_level_stages, + actual_latency_predictions1.top_level_stages); + EXPECT_EQ(expected_latency_predictions1.total_latency, + actual_latency_predictions1.total_latency); + EXPECT_EQ(expected_latency_predictions2.top_level_stages, + actual_latency_predictions2.top_level_stages); + EXPECT_EQ(expected_latency_predictions2.total_latency, + actual_latency_predictions2.total_latency); + + EXPECT_EQ(expected_latency_attributions1, actual_latency_attributions1); + EXPECT_EQ(expected_latency_attributions2, actual_latency_attributions2); + + pipeline_reporter_ = nullptr; +} + +TEST_F(CompositorFrameReporterTest, StageLatencyMultiplePrediction) { + CompositorFrameReporter::CompositorLatencyInfo actual_latency_predictions( + base::Microseconds(-1)); + CompositorFrameReporter::CompositorLatencyInfo expected_latency_predictions; + + // First compositor reporter (general) + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(1500); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(833000); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1500), + base::Microseconds(833000)}; + expected_latency_predictions.total_latency = base::Microseconds(850500); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + EXPECT_EQ(expected_latency_predictions.top_level_stages, + actual_latency_predictions.top_level_stages); + EXPECT_EQ(expected_latency_predictions.total_latency, + actual_latency_predictions.total_latency); + + // Second compositor reporter (without subtmit stage) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16000), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1500), + base::Microseconds(833000)}; + expected_latency_predictions.total_latency = base::Microseconds(850500); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + EXPECT_EQ(expected_latency_predictions.top_level_stages, + actual_latency_predictions.top_level_stages); + EXPECT_EQ(expected_latency_predictions.total_latency, + actual_latency_predictions.total_latency); + + // Third compositor reporter (prediction and actual latency does not differ + // by 8) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16500); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(2000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(833000); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16125), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1625), + base::Microseconds(833000)}; + expected_latency_predictions.total_latency = base::Microseconds(850750); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + EXPECT_EQ(expected_latency_predictions.top_level_stages, + actual_latency_predictions.top_level_stages); + EXPECT_EQ(expected_latency_predictions.total_latency, + actual_latency_predictions.total_latency); + + // Fourth compositor reporter (total duration is 0) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(0); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16125), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(0), + base::Microseconds(0), base::Microseconds(1625), + base::Microseconds(833000)}; + expected_latency_predictions.total_latency = base::Microseconds(850750); + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + + EXPECT_EQ(expected_latency_predictions.top_level_stages, + actual_latency_predictions.top_level_stages); + EXPECT_EQ(expected_latency_predictions.total_latency, + actual_latency_predictions.total_latency); + + // Fifth compositor reporter (prediction and actual latency differ by a lot) + pipeline_reporter_ = CreatePipelineReporter(); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + AdvanceNowByUs(16000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); + + AdvanceNowByUs(60000); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now()); + + AdvanceNowByUs(6000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); + + AdvanceNowByUs(3000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now()); + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(39000); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + AdvanceNowByUs(833000); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + expected_latency_predictions.top_level_stages = { + base::Microseconds(16093), base::Microseconds(15000), + base::Microseconds(1500), base::Microseconds(750), + base::Microseconds(75), base::Microseconds(10968), + base::Microseconds(833000)}; + expected_latency_predictions.total_latency = base::Microseconds(877387); + + std::vector<std::string> expected_latency_attributions = { + "SendBeginMainFrameToCommit"}; + + pipeline_reporter_->CalculateCompositorLatencyPrediction( + actual_latency_predictions, kLatencyPredictionDeviationThreshold); + std::vector<std::string> actual_latency_attributions = + pipeline_reporter_->high_latency_substages_for_testing(); + + EXPECT_EQ(expected_latency_predictions.top_level_stages, + actual_latency_predictions.top_level_stages); + EXPECT_EQ(expected_latency_predictions.total_latency, + actual_latency_predictions.total_latency); + EXPECT_EQ(expected_latency_attributions, actual_latency_attributions); pipeline_reporter_ = nullptr; } @@ -1072,12 +1348,12 @@ pipeline_reporter_ = nullptr; } -// Tests that when a new frame with missing dispatch stages is presented to the -// user, event latency predictions are reported properly. +// Tests that when a new frame with missing dispatch stages is presented to +// the user, event latency predictions are reported properly. TEST_F(CompositorFrameReporterTest, EventLatencyDispatchPredictionsWithMissingStages) { - // Invalid EventLatency stage durations will cause program to crash, validity - // checked in event_latency_tracing_recorder.cc. + // Invalid EventLatency stage durations will cause program to crash, + // validity checked in event_latency_tracing_recorder.cc. std::vector<int> dispatch_times = {400, 600, 700, -1, -1}; std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { CreateScrollUpdateEventMetricsWithDispatchTimes( @@ -1274,8 +1550,8 @@ actual_predictions1.compositor_durations[i]); EXPECT_EQ(expected_compositor2[i], actual_predictions2.compositor_durations[i]); - EXPECT_EQ(expected_compositor2[i], - actual_predictions2.compositor_durations[i]); + EXPECT_EQ(expected_compositor3[i], + actual_predictions3.compositor_durations[i]); } EXPECT_EQ(expected_transition1, actual_predictions1.transition_duration); EXPECT_EQ(expected_total1, actual_predictions1.total_duration); @@ -1394,5 +1670,284 @@ pipeline_reporter_ = nullptr; } +// Tests that when a frame is presented to the user, high latency attribution +// for EventLatency is reported properly for filtered EventTypes. +TEST_F(CompositorFrameReporterTest, EventLatencyAttributionPredictions) { + std::vector<int> dispatch_times = {300, 300, 300, 50000, 300}; + // The prediction should only be updated with the ScrollUpdateType event, + // other EventType metrics should be ignored. + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + EventMetrics::List events_metrics = { + std::make_move_iterator(std::begin(event_metrics_ptrs)), + std::make_move_iterator(std::end(event_metrics_ptrs))}; + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(50000); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test with no high latency attribution. + CompositorFrameReporter::EventLatencyInfo expected_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(expected_predictions1.dispatch_durations, + std::vector<int>{300, 300, 300, 50000, 300}); + expected_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + expected_predictions1.total_duration = base::Microseconds(102100); + + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + std::unique_ptr<EventMetrics>& event_metrics = + pipeline_reporter_->events_metrics_for_testing()[0]; + std::vector<std::string> attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(0, (int)attribution.size()); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test with 1 high latency stage attributed. + CompositorFrameReporter::EventLatencyInfo expected_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(expected_predictions2.dispatch_durations, + std::vector<int>{300, 300, 300, 12725, 300}); + expected_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + expected_predictions2.total_duration = base::Microseconds(64825); + + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{300, 300, 300, 300, 300}); + actual_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); + actual_predictions2.total_duration = base::Microseconds(52400); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(1, (int)attribution.size()); + EXPECT_EQ("RendererCompositorToMain", attribution[0]); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test with more than 1 high latency stage attributed. + CompositorFrameReporter::EventLatencyInfo expected_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(expected_predictions3.dispatch_durations, + std::vector<int>{300, 300, 300, 12725, 300}); + expected_predictions3.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions3.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); + expected_predictions3.total_duration = base::Microseconds(27550); + + CompositorFrameReporter::EventLatencyInfo actual_predictions3 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(actual_predictions3.dispatch_durations, + std::vector<int>{300, 300, 300, 300, 300}); + actual_predictions3.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions3.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + actual_predictions3.total_duration = base::Microseconds(2700); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions3, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("RendererCompositorToMain", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + + // Check that all prediction values are accurate. + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1.dispatch_durations[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2.dispatch_durations[i], + actual_predictions2.dispatch_durations[i]); + EXPECT_EQ(expected_predictions3.dispatch_durations[i], + actual_predictions3.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfStages; i++) { + EXPECT_EQ(expected_predictions1.compositor_durations[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_predictions2.compositor_durations[i], + actual_predictions2.compositor_durations[i]); + EXPECT_EQ(expected_predictions3.compositor_durations[i], + actual_predictions3.compositor_durations[i]); + } + EXPECT_EQ(expected_predictions1.transition_duration, + actual_predictions1.transition_duration); + EXPECT_EQ(expected_predictions1.total_duration, + actual_predictions1.total_duration); + EXPECT_EQ(expected_predictions2.transition_duration, + actual_predictions2.transition_duration); + EXPECT_EQ(expected_predictions2.total_duration, + actual_predictions2.total_duration); + EXPECT_EQ(expected_predictions3.transition_duration, + actual_predictions3.transition_duration); + EXPECT_EQ(expected_predictions3.total_duration, + actual_predictions3.total_duration); + + pipeline_reporter_ = nullptr; +} + +// Tests that when a frame is presented to the user, high latency attribution +// for EventLatency is reported properly for filtered EventTypes. +TEST_F(CompositorFrameReporterTest, EventLatencyAttributionChangePredictions) { + std::vector<int> dispatch_times = {40000, -1, -1, 300, 50000}; + // The prediction should only be updated with the ScrollUpdateType event, + // other EventType metrics should be ignored. + std::unique_ptr<EventMetrics> event_metrics_ptrs[] = { + CreateScrollUpdateEventMetricsWithDispatchTimes( + false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, + dispatch_times)}; + EXPECT_THAT(event_metrics_ptrs, Each(NotNull())); + EventMetrics::List events_metrics = { + std::make_move_iterator(std::begin(event_metrics_ptrs)), + std::make_move_iterator(std::end(event_metrics_ptrs))}; + + AdvanceNowByUs(300); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now()); + + // For this test there are only 3 compositor substages updated which reflects + // a more realistic scenario. + + AdvanceNowByUs(300); // kBeginImplFrameToSendBeginMainFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now()); + + AdvanceNowByUs(50000); // kEndActivateToSubmitCompositorFrame duration + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now()); + + // kSubmitCompositorFrameToPresentationCompositorFrame duration + AdvanceNowByUs(300); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + + pipeline_reporter_->AddEventsMetrics(std::move(events_metrics)); + + // Test 1 + CompositorFrameReporter::EventLatencyInfo expected_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(expected_predictions1.dispatch_durations, + std::vector<int>{10300, -1, -1, 300, 42500}); + expected_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(expected_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 15200, 300}); + expected_predictions1.total_duration = base::Microseconds(69200); + + CompositorFrameReporter::EventLatencyInfo actual_predictions1 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(actual_predictions1.dispatch_durations, + std::vector<int>{400, -1, -1, 300, 40000}); + actual_predictions1.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions1.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 3600, 300}); + actual_predictions1.total_duration = base::Microseconds(45200); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions1, kLatencyPredictionDeviationThreshold); + + std::unique_ptr<EventMetrics>& event_metrics = + pipeline_reporter_->events_metrics_for_testing()[0]; + std::vector<std::string> attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("GenerationToRendererCompositor", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + event_metrics->ClearHighLatencyStagesForTesting(); + + // Test 2 + CompositorFrameReporter::EventLatencyInfo expected_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(expected_predictions2.dispatch_durations, + std::vector<int>{10225, -1, -1, 300, 12725}); + expected_predictions2.transition_duration = base::Microseconds(300); + + IntToTimeDeltaVector(expected_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); + expected_predictions2.total_duration = base::Microseconds(36875); + + CompositorFrameReporter::EventLatencyInfo actual_predictions2 = + CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, + kNumOfStages); + IntToTimeDeltaVector(actual_predictions2.dispatch_durations, + std::vector<int>{300, -1, -1, 300, 300}); + actual_predictions2.transition_duration = base::Microseconds(300); + IntToTimeDeltaVector(actual_predictions2.compositor_durations, + std::vector<int>{300, -1, -1, -1, -1, 300, 300}); + actual_predictions2.total_duration = base::Microseconds(2100); + pipeline_reporter_->CalculateEventLatencyPrediction( + actual_predictions2, kLatencyPredictionDeviationThreshold); + + attribution = event_metrics->GetHighLatencyStages(); + EXPECT_EQ(2, (int)attribution.size()); + EXPECT_EQ("RendererMainProcessing", attribution[0]); + EXPECT_EQ("EndActivateToSubmitCompositorFrame", attribution[1]); + + // Check that all prediction values are accurate. + for (int i = 0; i < kNumDispatchStages; i++) { + EXPECT_EQ(expected_predictions1.dispatch_durations[i], + actual_predictions1.dispatch_durations[i]); + EXPECT_EQ(expected_predictions2.dispatch_durations[i], + actual_predictions2.dispatch_durations[i]); + } + for (int i = 0; i < kNumOfStages; i++) { + EXPECT_EQ(expected_predictions1.compositor_durations[i], + actual_predictions1.compositor_durations[i]); + EXPECT_EQ(expected_predictions2.compositor_durations[i], + actual_predictions2.compositor_durations[i]); + } + EXPECT_EQ(expected_predictions1.transition_duration, + actual_predictions1.transition_duration); + EXPECT_EQ(expected_predictions1.total_duration, + actual_predictions1.total_duration); + EXPECT_EQ(expected_predictions2.transition_duration, + actual_predictions2.transition_duration); + EXPECT_EQ(expected_predictions2.total_duration, + actual_predictions2.total_duration); + + pipeline_reporter_ = nullptr; +} + } // namespace } // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc index 9258cf1..5e7816e 100644 --- a/cc/metrics/compositor_frame_reporting_controller.cc +++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -23,11 +23,11 @@ using FrameTerminationStatus = CompositorFrameReporter::FrameTerminationStatus; constexpr char kTraceCategory[] = "cc,benchmark"; -constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount); +constexpr int kNumOfStages = static_cast<int>(StageType::kStageTypeCount) - 1; constexpr int kNumDispatchStages = static_cast<int>(EventMetrics::DispatchStage::kMaxValue); constexpr base::TimeDelta kDefaultLatencyPredictionDeviationThreshold = - base::Milliseconds(8.33); + viz::BeginFrameArgs::DefaultInterval() / 2; } // namespace CompositorFrameReportingController::CompositorFrameReportingController( @@ -37,8 +37,8 @@ : should_report_histograms_(should_report_histograms), layer_tree_host_id_(layer_tree_host_id), latency_ukm_reporter_(std::make_unique<LatencyUkmReporter>()), - previous_latency_predictions_main_(kNumOfStages, base::Microseconds(-1)), - previous_latency_predictions_impl_(kNumOfStages, base::Microseconds(-1)), + previous_latency_predictions_main_(base::Microseconds(-1)), + previous_latency_predictions_impl_(base::Microseconds(-1)), event_latency_predictions_( CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfStages)) { @@ -483,18 +483,20 @@ reporter->TerminateFrame(termination_status, details.presentation_feedback.timestamp); - base::TimeDelta frame_interval = + base::TimeDelta latency_prediction_deviation_threshold = details.presentation_feedback.interval.is_zero() ? kDefaultLatencyPredictionDeviationThreshold : (details.presentation_feedback.interval) / 2; switch (reporter->get_reporter_type()) { case CompositorFrameReporter::ReporterType::kImpl: - reporter->CalculateStageLatencyPrediction( - previous_latency_predictions_impl_); + reporter->CalculateCompositorLatencyPrediction( + previous_latency_predictions_impl_, + latency_prediction_deviation_threshold); break; case CompositorFrameReporter::ReporterType::kMain: - reporter->CalculateStageLatencyPrediction( - previous_latency_predictions_main_); + reporter->CalculateCompositorLatencyPrediction( + previous_latency_predictions_main_, + latency_prediction_deviation_threshold); break; } @@ -516,8 +518,8 @@ // TODO(crbug.com/1334827): Consider using a separate container to // differentiate event predictions with and without a main dispatch stage. - reporter->CalculateEventLatencyPrediction(event_latency_predictions_, - frame_interval); + reporter->CalculateEventLatencyPrediction( + event_latency_predictions_, latency_prediction_deviation_threshold); // For presented frames, if `reporter` was cloned from another reporter, // and the original reporter is still alive, then check whether the cloned
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h index 52485fe..321e492 100644 --- a/cc/metrics/compositor_frame_reporting_controller.h +++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -237,10 +237,10 @@ // interval of last begin frame args. base::TimeDelta last_interval_; - // These variables store the breakdown stage latency predictions made based - // on impl and main reporter's previous frames. - std::vector<base::TimeDelta> previous_latency_predictions_main_; - std::vector<base::TimeDelta> previous_latency_predictions_impl_; + CompositorFrameReporter::CompositorLatencyInfo + previous_latency_predictions_main_; + CompositorFrameReporter::CompositorLatencyInfo + previous_latency_predictions_impl_; // Container that stores the EventLatency stage latency predictions based on // previous event traces.
diff --git a/cc/metrics/event_latency_tracing_recorder.cc b/cc/metrics/event_latency_tracing_recorder.cc index 8ad1d52d..138dac53 100644 --- a/cc/metrics/event_latency_tracing_recorder.cc +++ b/cc/metrics/event_latency_tracing_recorder.cc
@@ -18,9 +18,45 @@ constexpr char kTracingCategory[] = "cc,benchmark,input"; constexpr base::TimeDelta high_latency_threshold = base::Milliseconds(90); -// Returns the name of the event dispatch breakdown of EventLatency trace events -// between `start_stage` and `end_stage`. -constexpr const char* GetDispatchBreakdownName( +constexpr perfetto::protos::pbzero::EventLatency::EventType ToProtoEnum( + EventMetrics::EventType event_type) { +#define CASE(event_type, proto_event_type) \ + case EventMetrics::EventType::event_type: \ + return perfetto::protos::pbzero::EventLatency::proto_event_type + switch (event_type) { + CASE(kMousePressed, MOUSE_PRESSED); + CASE(kMouseReleased, MOUSE_RELEASED); + CASE(kMouseWheel, MOUSE_WHEEL); + CASE(kKeyPressed, KEY_PRESSED); + CASE(kKeyReleased, KEY_RELEASED); + CASE(kTouchPressed, TOUCH_PRESSED); + CASE(kTouchReleased, TOUCH_RELEASED); + CASE(kTouchMoved, TOUCH_MOVED); + CASE(kGestureScrollBegin, GESTURE_SCROLL_BEGIN); + CASE(kGestureScrollUpdate, GESTURE_SCROLL_UPDATE); + CASE(kGestureScrollEnd, GESTURE_SCROLL_END); + CASE(kGestureDoubleTap, GESTURE_DOUBLE_TAP); + CASE(kGestureLongPress, GESTURE_LONG_PRESS); + CASE(kGestureLongTap, GESTURE_LONG_TAP); + CASE(kGestureShowPress, GESTURE_SHOW_PRESS); + CASE(kGestureTap, GESTURE_TAP); + CASE(kGestureTapCancel, GESTURE_TAP_CANCEL); + CASE(kGestureTapDown, GESTURE_TAP_DOWN); + CASE(kGestureTapUnconfirmed, GESTURE_TAP_UNCONFIRMED); + CASE(kGestureTwoFingerTap, GESTURE_TWO_FINGER_TAP); + CASE(kFirstGestureScrollUpdate, FIRST_GESTURE_SCROLL_UPDATE); + CASE(kMouseDragged, MOUSE_DRAGGED); + CASE(kGesturePinchBegin, GESTURE_PINCH_BEGIN); + CASE(kGesturePinchEnd, GESTURE_PINCH_END); + CASE(kGesturePinchUpdate, GESTURE_PINCH_UPDATE); + CASE(kInertialGestureScrollUpdate, INERTIAL_GESTURE_SCROLL_UPDATE); + } +} + +} // namespace + +// static +const char* EventLatencyTracingRecorder::GetDispatchBreakdownName( EventMetrics::DispatchStage start_stage, EventMetrics::DispatchStage end_stage) { switch (start_stage) { @@ -54,9 +90,8 @@ } } -// Returns the name of EventLatency breakdown between `dispatch_stage` and -// `compositor_stage`. -constexpr const char* GetDispatchToCompositorBreakdownName( +// static +const char* EventLatencyTracingRecorder::GetDispatchToCompositorBreakdownName( EventMetrics::DispatchStage dispatch_stage, CompositorFrameReporter::StageType compositor_stage) { switch (dispatch_stage) { @@ -112,9 +147,8 @@ } } -// Returns the name of EventLatency breakdown between `dispatch_stage` and -// termination for events not associated with a frame update. -constexpr const char* GetDispatchToTerminationBreakdownName( +// static +const char* EventLatencyTracingRecorder::GetDispatchToTerminationBreakdownName( EventMetrics::DispatchStage dispatch_stage) { switch (dispatch_stage) { case EventMetrics::DispatchStage::kArrivedInRendererCompositor: @@ -133,43 +167,6 @@ } } -constexpr perfetto::protos::pbzero::EventLatency::EventType ToProtoEnum( - EventMetrics::EventType event_type) { -#define CASE(event_type, proto_event_type) \ - case EventMetrics::EventType::event_type: \ - return perfetto::protos::pbzero::EventLatency::proto_event_type - switch (event_type) { - CASE(kMousePressed, MOUSE_PRESSED); - CASE(kMouseReleased, MOUSE_RELEASED); - CASE(kMouseWheel, MOUSE_WHEEL); - CASE(kKeyPressed, KEY_PRESSED); - CASE(kKeyReleased, KEY_RELEASED); - CASE(kTouchPressed, TOUCH_PRESSED); - CASE(kTouchReleased, TOUCH_RELEASED); - CASE(kTouchMoved, TOUCH_MOVED); - CASE(kGestureScrollBegin, GESTURE_SCROLL_BEGIN); - CASE(kGestureScrollUpdate, GESTURE_SCROLL_UPDATE); - CASE(kGestureScrollEnd, GESTURE_SCROLL_END); - CASE(kGestureDoubleTap, GESTURE_DOUBLE_TAP); - CASE(kGestureLongPress, GESTURE_LONG_PRESS); - CASE(kGestureLongTap, GESTURE_LONG_TAP); - CASE(kGestureShowPress, GESTURE_SHOW_PRESS); - CASE(kGestureTap, GESTURE_TAP); - CASE(kGestureTapCancel, GESTURE_TAP_CANCEL); - CASE(kGestureTapDown, GESTURE_TAP_DOWN); - CASE(kGestureTapUnconfirmed, GESTURE_TAP_UNCONFIRMED); - CASE(kGestureTwoFingerTap, GESTURE_TWO_FINGER_TAP); - CASE(kFirstGestureScrollUpdate, FIRST_GESTURE_SCROLL_UPDATE); - CASE(kMouseDragged, MOUSE_DRAGGED); - CASE(kGesturePinchBegin, GESTURE_PINCH_BEGIN); - CASE(kGesturePinchEnd, GESTURE_PINCH_END); - CASE(kGesturePinchUpdate, GESTURE_PINCH_UPDATE); - CASE(kInertialGestureScrollUpdate, INERTIAL_GESTURE_SCROLL_UPDATE); - } -} - -} // namespace - // static void EventLatencyTracingRecorder::RecordEventLatencyTraceEvent( EventMetrics* event_metrics, @@ -195,6 +192,12 @@ bool has_high_latency = (termination_time - generated_timestamp) > high_latency_threshold; event_latency->set_has_high_latency(has_high_latency); + for (auto stage : event_metrics->GetHighLatencyStages()) { + // TODO(crbug.com/1334827): Consider changing the high_latency_stage + // type from a string to enum type in chrome_track_event.proto, + // similar to event_type. + event_latency->add_high_latency_stage(stage); + } }); // Event dispatch stages.
diff --git a/cc/metrics/event_latency_tracing_recorder.h b/cc/metrics/event_latency_tracing_recorder.h index 67202ca..75503719 100644 --- a/cc/metrics/event_latency_tracing_recorder.h +++ b/cc/metrics/event_latency_tracing_recorder.h
@@ -15,6 +15,23 @@ class EventLatencyTracingRecorder { public: + // Returns the name of the event dispatch breakdown of EventLatency trace + // events between `start_stage` and `end_stage`. + static const char* GetDispatchBreakdownName( + EventMetrics::DispatchStage start_stage, + EventMetrics::DispatchStage end_stage); + + // Returns the name of EventLatency breakdown between `dispatch_stage` and + // `compositor_stage`. + static const char* GetDispatchToCompositorBreakdownName( + EventMetrics::DispatchStage dispatch_stage, + CompositorFrameReporter::StageType compositor_stage); + + // Returns the name of EventLatency breakdown between `dispatch_stage` and + // termination for events not associated with a frame update. + static const char* GetDispatchToTerminationBreakdownName( + EventMetrics::DispatchStage dispatch_stage); + static void RecordEventLatencyTraceEvent( EventMetrics* event_metrics, base::TimeTicks termination_time,
diff --git a/cc/metrics/event_metrics.cc b/cc/metrics/event_metrics.cc index aff7043..62306ee 100644 --- a/cc/metrics/event_metrics.cc +++ b/cc/metrics/event_metrics.cc
@@ -6,6 +6,7 @@ #include <algorithm> #include <ostream> +#include <string> #include <utility> #include "base/check.h" @@ -272,6 +273,10 @@ return kInterestingEvents[static_cast<int>(type_)].name; } +void EventMetrics::SetHighLatencyStage(const std::string& stage) { + high_latency_stages_.push_back(stage); +} + void EventMetrics::SetDispatchStageTimestamp(DispatchStage stage) { DCHECK(dispatch_stage_timestamps_[static_cast<size_t>(stage)].is_null());
diff --git a/cc/metrics/event_metrics.h b/cc/metrics/event_metrics.h index b8c43eab..ce133874 100644 --- a/cc/metrics/event_metrics.h +++ b/cc/metrics/event_metrics.h
@@ -6,6 +6,7 @@ #define CC_METRICS_EVENT_METRICS_H_ #include <memory> +#include <string> #include <vector> #include "base/memory/raw_ptr.h" @@ -106,6 +107,12 @@ // Returns a string representing event type. const char* GetTypeName() const; + void SetHighLatencyStage(const std::string& stage); + const std::vector<std::string>& GetHighLatencyStages() const { + return high_latency_stages_; + } + void ClearHighLatencyStagesForTesting() { high_latency_stages_.clear(); } + void SetDispatchStageTimestamp(DispatchStage stage); base::TimeTicks GetDispatchStageTimestamp(DispatchStage stage) const; @@ -167,6 +174,8 @@ EventType type_; + std::vector<std::string> high_latency_stages_; + const raw_ptr<const base::TickClock> tick_clock_; // Timestamps of different stages of event dispatch. Timestamps are set as the
diff --git a/chrome/VERSION b/chrome/VERSION index 74c57bb..86dacc6 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=106 MINOR=0 -BUILD=5230 +BUILD=5231 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index d6d99fc..fed5085 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -1080,6 +1080,7 @@ "//chrome/browser/ui/android/autofill/internal:junit", "//chrome/browser/ui/android/default_browser_promo:java", "//chrome/browser/ui/android/default_browser_promo:junit", + "//chrome/browser/ui/android/fast_checkout:junit", "//chrome/browser/ui/android/favicon:java", "//chrome/browser/ui/android/layouts:java", "//chrome/browser/ui/android/layouts:junit",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java index 05333347..ba125dc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java
@@ -68,6 +68,13 @@ } @CalledByNative + @ContentSettingValues + private static int getPermission(@ContentSettingsType int type, String origin) { + return InstalledWebappPermissionManager.get().getPermission( + type, Origin.create(Uri.parse(origin))); + } + + @CalledByNative private static String getOriginFromPermission(Permission permission) { return permission.origin.toString(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java index b33304d..7f6cd5e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappPermissionManager.java
@@ -221,9 +221,8 @@ } } - @VisibleForTesting @ContentSettingValues - int getPermission(@ContentSettingsType int type, Origin origin) { + public int getPermission(@ContentSettingsType int type, Origin origin) { switch (type) { case ContentSettingsType.NOTIFICATIONS: { @ContentSettingValues
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java index ddb41aae..554ad43 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -183,7 +183,6 @@ private float mLeftMargin; private float mRightMargin; private final boolean mIncognito; - private float mBrightness; // Whether the CascadingStripStacker should be used. private boolean mShouldCascadeTabs; private boolean mIsFirstLayoutPass; @@ -252,7 +251,6 @@ res.getString(R.string.accessibility_toolbar_btn_new_incognito_tab)); mContext = context; mIncognito = incognito; - mBrightness = 1.f; // Create tab menu mTabMenu = new ListPopupWindow(mContext); @@ -329,28 +327,6 @@ } /** - * @return The brightness of background tabs in the tabstrip. - */ - public float getBackgroundTabBrightness() { - // TODO(crbug.com/1347591): Remove unused brightness code. - return mInReorderMode ? 0.75f : 1.0f; - } - - /** - * Sets the brightness for the entire tabstrip. - */ - public void setBrightness(float brightness) { - mBrightness = brightness; - } - - /** - * @return The brightness of the entire tabstrip. - */ - public float getBrightness() { - return mBrightness; - } - - /** * @return The opacity to use for the fade on the left side of the tab strip. */ public float getLeftFadeOpacity() { @@ -1752,13 +1728,15 @@ ANIM_TAB_MOVE_MS); mRunningAnimator.start(); - // 3. Clear any tab group margins if they are enabled. + // 3. Un-dim the background tabs. + setBackgroundTabsDimmed(false); + + // 4. Clear any tab group margins if they are enabled. if (isTabGroupsEnabled()) { resetTabGroupMargins(); - setBackgroundTabsDimmed(false); } - // 4. Request an update. + // 5. Request an update. mUpdateHost.requestUpdate(); } @@ -1838,7 +1816,8 @@ for (int i = 0; i < mStripTabs.length; i++) { final StripLayoutTab tab = mStripTabs[i]; - if (mTabGroupModelFilter.getRootId(getTabById(tab.getId())) == groupId) { + if (mTabGroupModelFilter.getRootId(getTabById(tab.getId())) == groupId + && tab != mInteractingTab) { tab.setBrightness(dimmed ? BACKGROUND_TAB_BRIGHTNESS_DIMMED : BACKGROUND_TAB_BRIGHTNESS_DEFAULT); } @@ -1919,7 +1898,7 @@ if (mHoveringOverGroup != hoveringOverGroup) { // 1.a. Reset hover variables. mHoveringOverGroup = hoveringOverGroup; - mHoverStartTime = 0L; + mHoverStartTime = INVALID_TIME; mHoverStartOffset = 0; // 1.b. Set tab group dim as necessary.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java index b927bda..e374b9f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -462,20 +462,6 @@ return getActiveStripLayoutHelper().getRightFadeOpacity(); } - /** - * @return The brightness of background tabs in the tabstrip. - */ - public float getBackgroundTabBrightness() { - return getActiveStripLayoutHelper().getBackgroundTabBrightness(); - } - - /** - * @return The brightness of the entire tabstrip. - */ - public float getBrightness() { - return getActiveStripLayoutHelper().getBrightness(); - } - /** Update the title cache for the available tabs in the model. */ private void updateTitleCacheForInit() { LayerTitleCache titleCache = mLayerTitleCacheSupplier.get();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java index 1406614..eaf72ea3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
@@ -114,8 +114,7 @@ final float width = layoutHelper.getWidth() * mDpToPx; final float height = layoutHelper.getHeight() * mDpToPx; TabStripSceneLayerJni.get().updateTabStripLayer(mNativePtr, TabStripSceneLayer.this, width, - height, yOffset * mDpToPx, layoutHelper.getBackgroundTabBrightness(), - layoutHelper.getBrightness(), shouldReaddBackground(layoutHelper.getOrientation())); + height, yOffset * mDpToPx, shouldReaddBackground(layoutHelper.getOrientation())); updateStripScrim(layoutHelper.getStripScrim()); @@ -200,8 +199,7 @@ long nativeTabStripSceneLayer, TabStripSceneLayer caller, boolean visible); void finishBuildingFrame(long nativeTabStripSceneLayer, TabStripSceneLayer caller); void updateTabStripLayer(long nativeTabStripSceneLayer, TabStripSceneLayer caller, - float width, float height, float yOffset, float backgroundTabBrightness, - float brightness, boolean shouldReadBackground); + float width, float height, float yOffset, boolean shouldReadBackground); void updateStripScrim(long nativeTabStripSceneLayer, TabStripSceneLayer caller, float x, float y, float width, float height, int color, float alpha); void updateNewTabButton(long nativeTabStripSceneLayer, TabStripSceneLayer caller,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureActivity.java index 16d1fdeb..3677d083 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureActivity.java
@@ -163,28 +163,28 @@ private MediaActionButtonsManager() { mPreviousTrack = createRemoteAction(MediaSessionAction.PREVIOUS_TRACK, - R.drawable.ic_skip_previous_white_36dp, R.string.accessibility_previous_track); - mPlay = createRemoteAction(MediaSessionAction.PLAY, R.drawable.ic_play_arrow_white_36dp, + R.drawable.ic_skip_previous_white_24dp, R.string.accessibility_previous_track); + mPlay = createRemoteAction(MediaSessionAction.PLAY, R.drawable.ic_play_arrow_white_24dp, R.string.accessibility_play); - mPause = createRemoteAction(MediaSessionAction.PAUSE, R.drawable.ic_pause_white_36dp, + mPause = createRemoteAction(MediaSessionAction.PAUSE, R.drawable.ic_pause_white_24dp, R.string.accessibility_pause); mReplay = createRemoteAction(MediaSessionAction.PLAY, R.drawable.ic_replay_white_24dp, R.string.accessibility_replay); mNextTrack = createRemoteAction(MediaSessionAction.NEXT_TRACK, - R.drawable.ic_skip_next_white_36dp, R.string.accessibility_next_track); + R.drawable.ic_skip_next_white_24dp, R.string.accessibility_next_track); mHangUp = createRemoteAction(MediaSessionAction.HANG_UP, - R.drawable.ic_call_end_white_36dp, R.string.accessibility_hang_up); + R.drawable.ic_call_end_white_24dp, R.string.accessibility_hang_up); mMicrophone = new ToggleRemoteAction( createRemoteAction(MediaSessionAction.TOGGLE_MICROPHONE, - R.drawable.ic_mic_white_36dp, R.string.accessibility_mute_microphone), + R.drawable.ic_mic_white_24dp, R.string.accessibility_mute_microphone), createRemoteAction(MediaSessionAction.TOGGLE_MICROPHONE, - R.drawable.ic_mic_off_white_36dp, + R.drawable.ic_mic_off_white_24dp, R.string.accessibility_unmute_microphone)); - mCamera = new ToggleRemoteAction(createRemoteAction(MediaSessionAction.TOGGLE_CAMERA, - R.drawable.ic_videocam_white_36dp, - R.string.accessibility_turn_off_camera), + mCamera = new ToggleRemoteAction( createRemoteAction(MediaSessionAction.TOGGLE_CAMERA, - R.drawable.ic_videocam_off_white_36dp, + R.drawable.ic_videocam_24dp, R.string.accessibility_turn_off_camera), + createRemoteAction(MediaSessionAction.TOGGLE_CAMERA, + R.drawable.ic_videocam_off_white_24dp, R.string.accessibility_turn_on_camera)); mPlaybackState = PlaybackState.END_OF_VIDEO;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java index 3f4ccbd9..ae2969d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
@@ -115,7 +115,7 @@ .setContentTitle(title) .setContentText(message) .setOngoing(true) - .addAction(R.drawable.ic_stop_white_36dp, MSG_STOP, + .addAction(R.drawable.ic_stop_white_24dp, MSG_STOP, TracingNotificationServiceImpl.getStopRecordingIntent(context)); showNotification(sTracingActiveNotificationBuilder.build()); }
diff --git a/chrome/app/theme/chromium/win/chromium.ico b/chrome/app/theme/chromium/win/chromium.ico index 1cfe8f13..d0aa0c2 100644 --- a/chrome/app/theme/chromium/win/chromium.ico +++ b/chrome/app/theme/chromium/win/chromium.ico Binary files differ
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index f7fd2499..3b1d0dc 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -4792,12 +4792,10 @@ "icon_loader_chromeos.cc", "lifetime/application_lifetime_chromeos.cc", "lifetime/application_lifetime_chromeos.h", - "media/chromeos_login_media_access_handler.cc", - "media/chromeos_login_media_access_handler.h", + "media/chromeos_login_and_lock_media_access_handler.cc", + "media/chromeos_login_and_lock_media_access_handler.h", "media/public_session_media_access_handler.cc", "media/public_session_media_access_handler.h", - "media/public_session_tab_capture_access_handler.cc", - "media/public_session_tab_capture_access_handler.h", "media/webrtc/desktop_media_list_ash.cc", "media/webrtc/desktop_media_list_ash.h", "media/webrtc/window_icon_util_chromeos.cc", @@ -5351,6 +5349,8 @@ "chromeos/app_mode/chrome_kiosk_external_loader_broker.h", "chromeos/app_mode/kiosk_app_external_loader.cc", "chromeos/app_mode/kiosk_app_external_loader.h", + "chromeos/app_mode/kiosk_app_service_launcher.cc", + "chromeos/app_mode/kiosk_app_service_launcher.h", "chromeos/app_mode/kiosk_settings_navigation_throttle.cc", "chromeos/app_mode/kiosk_settings_navigation_throttle.h", "chromeos/app_mode/startup_app_launcher_update_checker.cc", @@ -5477,6 +5477,8 @@ "lacros/for_which_extension_type.h", "lacros/force_installed_tracker_lacros.cc", "lacros/force_installed_tracker_lacros.h", + "lacros/fullscreen_controller_client_lacros.cc", + "lacros/fullscreen_controller_client_lacros.h", "lacros/identity_manager_lacros.cc", "lacros/identity_manager_lacros.h", "lacros/lacros_butter_bar.cc", @@ -5588,6 +5590,7 @@ "//chromeos/startup", "//chromeos/ui/base", "//chromeos/ui/frame", + "//chromeos/ui/wm", "//components/arc/common", "//extensions/browser/updater", "//ui/chromeos/styles:cros_styles_views",
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc index b273d21..1d4d5d2 100644 --- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc +++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -31,8 +31,6 @@ left_fade_(cc::UIResourceLayer::Create()), right_fade_(cc::UIResourceLayer::Create()), model_selector_button_(cc::UIResourceLayer::Create()), - background_tab_brightness_(1.f), - brightness_(1.f), write_index_(0), content_tree_(nullptr) { new_tab_button_->SetIsDrawable(true); @@ -115,23 +113,12 @@ jfloat width, jfloat height, jfloat y_offset, - jfloat background_tab_brightness, - jfloat brightness, jboolean should_readd_background) { - background_tab_brightness_ = background_tab_brightness; gfx::RectF content(0, y_offset, width, height); layer()->SetPosition(gfx::PointF(0, y_offset)); tab_strip_layer_->SetBounds(gfx::Size(width, height)); scrollable_strip_layer_->SetBounds(gfx::Size(width, height)); - if (brightness != brightness_) { - brightness_ = brightness; - cc::FilterOperations filters; - if (brightness_ < 1.f) - filters.Append(cc::FilterOperation::CreateBrightnessFilter(brightness_)); - tab_strip_layer_->SetFilters(filters); - } - // Content tree should not be affected by tab strip scene layer visibility. if (content_tree_) content_tree_->layer()->SetPosition(gfx::PointF(0, -y_offset));
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h index 3b36b91a..4a85d0d 100644 --- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h +++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
@@ -53,8 +53,6 @@ jfloat width, jfloat height, jfloat y_offset, - jfloat background_tab_brightness, - jfloat brightness, jboolean should_readd_background); void UpdateStripScrim(JNIEnv* env, @@ -148,8 +146,6 @@ scoped_refptr<cc::UIResourceLayer> right_fade_; scoped_refptr<cc::UIResourceLayer> model_selector_button_; - float background_tab_brightness_; - float brightness_; unsigned write_index_; TabHandleLayerList tab_handle_layers_; raw_ptr<SceneLayer> content_tree_;
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc index 48e4c3e5..0b01525 100644 --- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc +++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -718,11 +718,9 @@ return; } - const base::Value* disabled_system_features_pref = - local_state->GetList(policy::policy_prefs::kSystemFeaturesDisableList); - if (!disabled_system_features_pref) { - return; - } + const base::Value::List& disabled_system_features_pref = + local_state->GetValueList( + policy::policy_prefs::kSystemFeaturesDisableList); const bool is_pref_disabled_mode_hidden = local_state->GetString( @@ -1026,12 +1024,12 @@ } void ExtensionAppsChromeOs::UpdateAppDisabledState( - const base::Value* disabled_system_features_pref, + const base::Value::List& disabled_system_features_pref, int feature, const std::string& app_id, bool is_disabled_mode_changed) { - const bool is_disabled = base::Contains( - disabled_system_features_pref->GetListDeprecated(), base::Value(feature)); + const bool is_disabled = + base::Contains(disabled_system_features_pref, base::Value(feature)); // Sometimes the policy is updated before the app is installed, so this way // the disabled_apps_ is updated regardless the Publish should happen or not // and the app will be published with the correct readiness upon its
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.h b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.h index 2e83ec8..c416f01 100644 --- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.h +++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.h
@@ -176,10 +176,11 @@ content::WebContents* LaunchImpl(AppLaunchParams&& params) override; - void UpdateAppDisabledState(const base::Value* disabled_system_features_pref, - int feature, - const std::string& app_id, - bool is_disabled_mode_changed); + void UpdateAppDisabledState( + const base::Value::List& disabled_system_features_pref, + int feature, + const std::string& app_id, + bool is_disabled_mode_changed); void LaunchExtension(const std::string& app_id, int32_t event_flags,
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_crosapi_browsertest.cc b/chrome/browser/apps/app_service/publishers/web_apps_crosapi_browsertest.cc index 03ca64d9..4c479f1 100644 --- a/chrome/browser/apps/app_service/publishers/web_apps_crosapi_browsertest.cc +++ b/chrome/browser/apps/app_service/publishers/web_apps_crosapi_browsertest.cc
@@ -13,16 +13,12 @@ #include "base/test/scoped_feature_list.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" -#include "chrome/browser/ash/crosapi/crosapi_ash.h" -#include "chrome/browser/ash/crosapi/crosapi_manager.h" -#include "chrome/browser/ash/crosapi/test_controller_ash.h" +#include "chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.h" #include "chrome/browser/web_applications/test/app_registration_waiter.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/common/chrome_features.h" -#include "chrome/test/base/chromeos/ash_browser_test_starter.h" -#include "chrome/test/base/in_process_browser_test.h" #include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" #include "components/services/app_service/public/cpp/app_launch_util.h" #include "content/public/test/browser_test.h" @@ -107,7 +103,8 @@ } // namespace -class WebAppsCrosapiBrowserTest : public InProcessBrowserTest { +class WebAppsCrosapiBrowserTest + : public crosapi::AshRequiresLacrosBrowserTestBase { public: WebAppsCrosapiBrowserTest() { scoped_feature_list_.InitWithFeatures( @@ -116,29 +113,20 @@ ~WebAppsCrosapiBrowserTest() override = default; protected: - void SetUpInProcessBrowserTestFixture() override { - if (!ash_starter_.HasLacrosArgument()) { - return; - } - ASSERT_TRUE(ash_starter_.PrepareEnvironmentForLacros()); - } - void SetUpOnMainThread() override { - if (!ash_starter_.HasLacrosArgument()) { + crosapi::AshRequiresLacrosBrowserTestBase::SetUpOnMainThread(); + if (!HasLacrosArgument()) { return; } - auto* manager = crosapi::CrosapiManager::Get(); - test_controller_ash_ = std::make_unique<crosapi::TestControllerAsh>(); - manager->crosapi_ash()->SetTestControllerForTesting( - test_controller_ash_.get()); - ash_starter_.StartLacros(this); + web_app::AppTypeInitializationWaiter(profile(), apps::AppType::kWeb) + .Await(); } std::string InstallWebApp(const std::string& start_url, apps::WindowMode mode) { crosapi::mojom::StandaloneBrowserTestControllerAsyncWaiter waiter( - test_controller_ash_->GetStandaloneBrowserTestController().get()); + GetStandaloneBrowserTestController()); std::string app_id; waiter.InstallWebApp(start_url, mode, &app_id); web_app::AppRegistrationWaiter(browser()->profile(), app_id).Await(); @@ -151,16 +139,12 @@ return apps::AppServiceProxyFactory::GetForProfile(profile()); } - const test::AshBrowserTestStarter& ash_starter() { return ash_starter_; } - private: base::test::ScopedFeatureList scoped_feature_list_; - test::AshBrowserTestStarter ash_starter_; - std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_; }; IN_PROC_BROWSER_TEST_F(WebAppsCrosapiBrowserTest, PinUsingContextMenu) { - if (!ash_starter().HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; } @@ -228,7 +212,7 @@ } IN_PROC_BROWSER_TEST_F(WebAppsCrosapiBrowserTest, Uninstall) { - if (!ash_starter().HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; }
diff --git a/chrome/browser/apps/app_service/webapk/webapk_prefs.cc b/chrome/browser/apps/app_service/webapk/webapk_prefs.cc index ee49c82..efefa03 100644 --- a/chrome/browser/apps/app_service/webapk/webapk_prefs.cc +++ b/chrome/browser/apps/app_service/webapk/webapk_prefs.cc
@@ -55,14 +55,13 @@ absl::optional<std::string> GetWebApkPackageName(Profile* profile, const std::string& app_id) { - const base::Value* app_dict = profile->GetPrefs() - ->GetDictionary(kGeneratedWebApksPref) - ->FindDictKey(app_id); + const base::Value::Dict* app_dict = + profile->GetPrefs()->GetValueDict(kGeneratedWebApksPref).FindDict(app_id); if (!app_dict) { return absl::nullopt; } - const std::string* package_name = app_dict->FindStringKey(kPackageNameKey); + const std::string* package_name = app_dict->FindString(kPackageNameKey); if (!package_name) { return absl::nullopt; }
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_dispatcher_unittest.cc b/chrome/browser/ash/borealis/borealis_disk_manager_dispatcher_unittest.cc index ee3d838b6..3c2957b 100644 --- a/chrome/browser/ash/borealis/borealis_disk_manager_dispatcher_unittest.cc +++ b/chrome/browser/ash/borealis/borealis_disk_manager_dispatcher_unittest.cc
@@ -6,6 +6,7 @@ #include "chrome/browser/ash/borealis/borealis_disk_manager.h" #include "chrome/browser/ash/borealis/borealis_disk_manager_impl.h" +#include "chrome/browser/ash/borealis/borealis_metrics.h" #include "chrome/browser/ash/borealis/testing/callback_factory.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc b/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc index 9a04b09..7bc4edbc 100644 --- a/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc +++ b/chrome/browser/ash/borealis/borealis_disk_manager_impl.cc
@@ -95,21 +95,17 @@ BorealisDiskManagerImpl::BorealisDiskManagerImpl(const BorealisContext* context) : context_(context), - request_count_(0), + service_(borealis::BorealisService::GetForProfile(context_->profile())), free_space_provider_(std::make_unique<FreeSpaceProvider>()), weak_factory_(this) { - borealis::BorealisService::GetForProfile(context_->profile()) - ->DiskManagerDispatcher() - .SetDiskManagerDelegate(this); + service_->DiskManagerDispatcher().SetDiskManagerDelegate(this); } BorealisDiskManagerImpl::~BorealisDiskManagerImpl() { if (DiskManagementVersion() == DiskManagementVersion::CROSDISK) { RecordBorealisDiskClientNumRequestsPerSessionHistogram(request_count_); } - borealis::BorealisService::GetForProfile(context_->profile()) - ->DiskManagerDispatcher() - .RemoveDiskManagerDelegate(this); + service_->DiskManagerDispatcher().RemoveDiskManagerDelegate(this); } // Helper function that returns how many bytes the |available_space| would
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_impl.h b/chrome/browser/ash/borealis/borealis_disk_manager_impl.h index 18f5330b..d4a4bc8 100644 --- a/chrome/browser/ash/borealis/borealis_disk_manager_impl.h +++ b/chrome/browser/ash/borealis/borealis_disk_manager_impl.h
@@ -7,8 +7,9 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/ash/borealis/borealis_context_manager.h" +#include "chrome/browser/ash/borealis/borealis_context.h" #include "chrome/browser/ash/borealis/borealis_disk_manager.h" +#include "chrome/browser/ash/borealis/borealis_service.h" namespace borealis { // Amount of space, in bytes, that borealis needs to leave free on the host. @@ -110,7 +111,8 @@ Described<BorealisSyncDiskSizeResult>> success_or_error); const BorealisContext* const context_; - int request_count_; + BorealisService* const service_; + int request_count_{0}; std::unique_ptr<BuildDiskInfo> build_disk_info_transition_; std::unique_ptr<ResizeDisk> resize_disk_transition_; std::unique_ptr<SyncDisk> sync_disk_transition_;
diff --git a/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc b/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc index a98e3d2..703983a7 100644 --- a/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc +++ b/chrome/browser/ash/borealis/borealis_disk_manager_unittest.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/ash/borealis/borealis_context.h" #include "chrome/browser/ash/borealis/borealis_disk_manager_dispatcher.h" #include "chrome/browser/ash/borealis/borealis_features.h" +#include "chrome/browser/ash/borealis/borealis_metrics.h" #include "chrome/browser/ash/borealis/borealis_service_fake.h" #include "chrome/browser/ash/borealis/borealis_window_manager.h" #include "chrome/browser/ash/borealis/testing/callback_factory.h"
diff --git a/chrome/browser/ash/bruschetta/bruschetta_mount_provider.cc b/chrome/browser/ash/bruschetta/bruschetta_mount_provider.cc index 290ae5b..e6af40cb 100644 --- a/chrome/browser/ash/bruschetta/bruschetta_mount_provider.cc +++ b/chrome/browser/ash/bruschetta/bruschetta_mount_provider.cc
@@ -59,9 +59,9 @@ std::move(callback).Run(false, 0, 0, base::FilePath()); return; } + auto* tracker = guest_os::GuestOsSessionTracker::GetForProfile(profile_); - auto info = guest_os::GuestOsSessionTracker::GetForProfile(profile_)->GetInfo( - guest_id_); + auto info = tracker->GetInfo(guest_id_); if (!info) { // Shouldn't happen unless you managed to shutdown the VM at the same // instant as you booted it. @@ -69,6 +69,9 @@ std::move(callback).Run(false, 0, 0, base::FilePath()); return; } + unmount_subscription_ = tracker->RunOnShutdown( + guest_id_, base::BindOnce(&BruschettaMountProvider::Unmount, + weak_ptr_factory_.GetWeakPtr())); std::move(callback).Run(true, info->cid, info->sftp_vsock_port, info->homedir); }
diff --git a/chrome/browser/ash/bruschetta/bruschetta_mount_provider.h b/chrome/browser/ash/bruschetta/bruschetta_mount_provider.h index 808daa7..2112e22 100644 --- a/chrome/browser/ash/bruschetta/bruschetta_mount_provider.h +++ b/chrome/browser/ash/bruschetta/bruschetta_mount_provider.h
@@ -39,6 +39,7 @@ void OnRunning(PrepareCallback callback, BruschettaResult result); Profile* profile_; guest_os::GuestId guest_id_; + base::CallbackListSubscription unmount_subscription_; base::WeakPtrFactory<BruschettaMountProvider> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn index ed8d45d..55de272 100644 --- a/chrome/browser/ash/crosapi/BUILD.gn +++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -117,6 +117,8 @@ "files_app_launcher.h", "force_installed_tracker_ash.cc", "force_installed_tracker_ash.h", + "fullscreen_controller_ash.cc", + "fullscreen_controller_ash.h", "geolocation_service_ash.cc", "geolocation_service_ash.h", "hosted_app_util.cc", @@ -350,6 +352,8 @@ testonly = true sources = [ + "ash_requires_lacros_browsertestbase.cc", + "ash_requires_lacros_browsertestbase.h", "fake_browser_manager.cc", "fake_browser_manager.h", "input_method_test_interface_ash.cc", @@ -393,6 +397,7 @@ "download_controller_ash_unittest.cc", "fake_migration_progress_tracker.h", "field_trial_service_ash_unittest.cc", + "fullscreen_controller_ash_unittest.cc", "geolocation_service_ash_unittest.cc", "keystore_service_ash_unittest.cc", "lacros_availability_policy_observer_unittest.cc",
diff --git a/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.cc b/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.cc new file mode 100644 index 0000000..8502f376 --- /dev/null +++ b/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.cc
@@ -0,0 +1,53 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h" + +#include "ash/constants/ash_features.h" +#include "base/location.h" +#include "base/one_shot_event.h" +#include "base/run_loop.h" +#include "chrome/browser/ash/crosapi/crosapi_ash.h" +#include "chrome/browser/ash/crosapi/crosapi_manager.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace crosapi { + +AshRequiresLacrosBrowserTestBase::AshRequiresLacrosBrowserTestBase() { + scoped_feature_list_.InitAndEnableFeature(chromeos::features::kLacrosSupport); +} + +AshRequiresLacrosBrowserTestBase::~AshRequiresLacrosBrowserTestBase() = default; + +void AshRequiresLacrosBrowserTestBase::SetUpInProcessBrowserTestFixture() { + if (!ash_starter_.HasLacrosArgument()) { + return; + } + ASSERT_TRUE(ash_starter_.PrepareEnvironmentForLacros()); +} + +void AshRequiresLacrosBrowserTestBase::SetUpOnMainThread() { + if (!ash_starter_.HasLacrosArgument()) { + return; + } + auto* manager = crosapi::CrosapiManager::Get(); + test_controller_ash_ = std::make_unique<crosapi::TestControllerAsh>(); + manager->crosapi_ash()->SetTestControllerForTesting( // IN-TEST + test_controller_ash_.get()); + + ash_starter_.StartLacros(this); + + base::RunLoop run_loop; + test_controller_ash_->on_standalone_browser_test_controller_bound().Post( + FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); +} + +mojom::StandaloneBrowserTestController* +AshRequiresLacrosBrowserTestBase::GetStandaloneBrowserTestController() { + CHECK(test_controller_ash_); + return test_controller_ash_->GetStandaloneBrowserTestController().get(); +} + +} // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h b/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h new file mode 100644 index 0000000..2416141 --- /dev/null +++ b/chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h
@@ -0,0 +1,48 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_CROSAPI_ASH_REQUIRES_LACROS_BROWSERTESTBASE_H_ +#define CHROME_BROWSER_ASH_CROSAPI_ASH_REQUIRES_LACROS_BROWSERTESTBASE_H_ + +#include "chrome/test/base/in_process_browser_test.h" + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/ash/crosapi/test_controller_ash.h" +#include "chrome/test/base/chromeos/ash_browser_test_starter.h" +#include "chromeos/crosapi/mojom/test_controller.mojom.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace crosapi { + +// Base class for Ash browser tests that depend on Lacros and use +// StandaloneBrowserTestController. +class AshRequiresLacrosBrowserTestBase : public InProcessBrowserTest { + public: + AshRequiresLacrosBrowserTestBase(); + ~AshRequiresLacrosBrowserTestBase() override; + + protected: + void SetUpInProcessBrowserTestFixture() override; + + // Waits for Lacros to start, and for the StandaloneBrowserTestController to + // connect. + void SetUpOnMainThread() override; + + // Returns whether the --lacros-chrome-path is provided. + // If returns false, we should not do any Lacros related testing + // because the Lacros instance is not provided. + bool HasLacrosArgument() const { return ash_starter_.HasLacrosArgument(); } + + // Controller to send commands to the connected Lacros crosapi client. + mojom::StandaloneBrowserTestController* GetStandaloneBrowserTestController(); + + private: + base::test::ScopedFeatureList scoped_feature_list_; + test::AshBrowserTestStarter ash_starter_; + std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_; +}; + +} // namespace crosapi + +#endif // CHROME_BROWSER_ASH_CROSAPI_ASH_REQUIRES_LACROS_BROWSERTESTBASE_H_
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc index b150648..5f31f30 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.cc +++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -52,6 +52,7 @@ #include "chrome/browser/ash/crosapi/file_manager_ash.h" #include "chrome/browser/ash/crosapi/file_system_provider_service_ash.h" #include "chrome/browser/ash/crosapi/force_installed_tracker_ash.h" +#include "chrome/browser/ash/crosapi/fullscreen_controller_ash.h" #include "chrome/browser/ash/crosapi/geolocation_service_ash.h" #include "chrome/browser/ash/crosapi/identity_manager_ash.h" #include "chrome/browser/ash/crosapi/idle_service_ash.h" @@ -200,6 +201,7 @@ std::make_unique<FileSystemProviderServiceAsh>()), force_installed_tracker_ash_( std::make_unique<ForceInstalledTrackerAsh>()), + fullscreen_controller_ash_(std::make_unique<FullscreenControllerAsh>()), geolocation_service_ash_(std::make_unique<GeolocationServiceAsh>()), identity_manager_ash_(std::make_unique<IdentityManagerAsh>()), idle_service_ash_(std::make_unique<IdleServiceAsh>()), @@ -392,6 +394,11 @@ force_installed_tracker_ash_->BindReceiver(std::move(receiver)); } +void CrosapiAsh::BindFullscreenController( + mojo::PendingReceiver<crosapi::mojom::FullscreenController> receiver) { + fullscreen_controller_ash_->BindReceiver(std::move(receiver)); +} + void CrosapiAsh::BindGeolocationService( mojo::PendingReceiver<crosapi::mojom::GeolocationService> receiver) { geolocation_service_ash_->BindReceiver(std::move(receiver));
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h index f142a18a..1068806 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.h +++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -62,6 +62,7 @@ class FileManagerAsh; class FileSystemProviderServiceAsh; class ForceInstalledTrackerAsh; +class FullscreenControllerAsh; class GeolocationServiceAsh; class IdentityManagerAsh; class IdleServiceAsh; @@ -200,6 +201,8 @@ override; void BindForceInstalledTracker( mojo::PendingReceiver<mojom::ForceInstalledTracker> receiver) override; + void BindFullscreenController( + mojo::PendingReceiver<mojom::FullscreenController> receiver) override; void BindGeolocationService( mojo::PendingReceiver<mojom::GeolocationService> receiver) override; void BindIdentityManager( @@ -347,6 +350,10 @@ return force_installed_tracker_ash_.get(); } + FullscreenControllerAsh* fullscreen_controller_ash() { + return fullscreen_controller_ash_.get(); + } + KioskSessionServiceAsh* kiosk_session_service() { return kiosk_session_service_ash_.get(); } @@ -463,6 +470,7 @@ std::unique_ptr<FileSystemProviderServiceAsh> file_system_provider_service_ash_; std::unique_ptr<ForceInstalledTrackerAsh> force_installed_tracker_ash_; + std::unique_ptr<FullscreenControllerAsh> fullscreen_controller_ash_; std::unique_ptr<GeolocationServiceAsh> geolocation_service_ash_; std::unique_ptr<IdentityManagerAsh> identity_manager_ash_; std::unique_ptr<IdleServiceAsh> idle_service_ash_;
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc index c17d879e..0d39fe8c 100644 --- a/chrome/browser/ash/crosapi/crosapi_util.cc +++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -71,6 +71,7 @@ #include "chromeos/crosapi/mojom/file_manager.mojom.h" #include "chromeos/crosapi/mojom/file_system_provider.mojom.h" #include "chromeos/crosapi/mojom/force_installed_tracker.mojom.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" #include "chromeos/crosapi/mojom/geolocation.mojom.h" #include "chromeos/crosapi/mojom/holding_space_service.mojom.h" #include "chromeos/crosapi/mojom/identity_manager.mojom.h" @@ -241,7 +242,7 @@ return {T::Uuid_, T::Version_}; } -static_assert(crosapi::mojom::Crosapi::Version_ == 93, +static_assert(crosapi::mojom::Crosapi::Version_ == 94, "If you add a new crosapi, please add it to " "kInterfaceVersionEntries below."); @@ -286,6 +287,7 @@ MakeInterfaceVersionEntry<crosapi::mojom::FileManager>(), MakeInterfaceVersionEntry<crosapi::mojom::FileSystemProviderService>(), MakeInterfaceVersionEntry<crosapi::mojom::ForceInstalledTracker>(), + MakeInterfaceVersionEntry<crosapi::mojom::FullscreenController>(), MakeInterfaceVersionEntry<crosapi::mojom::GeolocationService>(), MakeInterfaceVersionEntry<crosapi::mojom::HoldingSpaceService>(), MakeInterfaceVersionEntry<crosapi::mojom::IdentityManager>(),
diff --git a/chrome/browser/ash/crosapi/fullscreen_controller_ash.cc b/chrome/browser/ash/crosapi/fullscreen_controller_ash.cc new file mode 100644 index 0000000..652da4f --- /dev/null +++ b/chrome/browser/ash/crosapi/fullscreen_controller_ash.cc
@@ -0,0 +1,49 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/crosapi/fullscreen_controller_ash.h" + +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace crosapi { + +FullscreenControllerAsh::FullscreenControllerAsh() = default; +FullscreenControllerAsh::~FullscreenControllerAsh() = default; + +void FullscreenControllerAsh::BindReceiver( + mojo::PendingReceiver<mojom::FullscreenController> pending_receiver) { + receivers_.Add(this, std::move(pending_receiver)); +} + +void FullscreenControllerAsh::ShouldExitFullscreenBeforeLock( + base::OnceCallback<void(bool)> callback) { + // Ash should exit full screen before lock (which is the default) if there are + // no remote clients. + if (remotes_.empty()) { + std::move(callback).Run(true); + return; + } + + // Assumes that there is only one remote client. + remotes_.begin()->get()->ShouldExitFullscreenBeforeLock( + base::BindOnce(&FullscreenControllerAsh::OnShouldExitFullscreenBeforeLock, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +void FullscreenControllerAsh::AddClient( + mojo::PendingRemote<mojom::FullscreenControllerClient> client) { + remotes_.Add( + mojo::Remote<mojom::FullscreenControllerClient>(std::move(client))); +} + +void FullscreenControllerAsh::OnShouldExitFullscreenBeforeLock( + base::OnceCallback<void(bool)> callback, + bool should_exit_fullscreen) { + std::move(callback).Run(should_exit_fullscreen); +} + +} // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/fullscreen_controller_ash.h b/chrome/browser/ash/crosapi/fullscreen_controller_ash.h new file mode 100644 index 0000000..99c1af4 --- /dev/null +++ b/chrome/browser/ash/crosapi/fullscreen_controller_ash.h
@@ -0,0 +1,48 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ASH_CROSAPI_FULLSCREEN_CONTROLLER_ASH_H_ +#define CHROME_BROWSER_ASH_CROSAPI_FULLSCREEN_CONTROLLER_ASH_H_ + +#include "base/memory/weak_ptr.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" + +namespace crosapi { + +// The ash-chrome implementation of the fullscreen controller crosapi interface. +class FullscreenControllerAsh : public mojom::FullscreenController { + public: + FullscreenControllerAsh(); + FullscreenControllerAsh(const FullscreenControllerAsh&) = delete; + FullscreenControllerAsh& operator=(const FullscreenControllerAsh&) = delete; + ~FullscreenControllerAsh() override; + + void BindReceiver( + mojo::PendingReceiver<mojom::FullscreenController> receiver); + + // Whether full screen mode should be exited on session lock/unlock. + void ShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback); + + // crosapi::mojom::FullscreenController: + void AddClient( + mojo::PendingRemote<mojom::FullscreenControllerClient> client) override; + + private: + // Passed as a callback to `ShouldExitFullscreenBeforeLock` to the remote + // client. Forwards the received response to Ash. + void OnShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback, + bool should_exit_fullscreen); + + mojo::ReceiverSet<mojom::FullscreenController> receivers_; + mojo::RemoteSet<mojom::FullscreenControllerClient> remotes_; + + base::WeakPtrFactory<FullscreenControllerAsh> weak_factory_{this}; +}; + +} // namespace crosapi + +#endif // CHROME_BROWSER_ASH_CROSAPI_FULLSCREEN_CONTROLLER_ASH_H_
diff --git a/chrome/browser/ash/crosapi/fullscreen_controller_ash_unittest.cc b/chrome/browser/ash/crosapi/fullscreen_controller_ash_unittest.cc new file mode 100644 index 0000000..5ae4e53 --- /dev/null +++ b/chrome/browser/ash/crosapi/fullscreen_controller_ash_unittest.cc
@@ -0,0 +1,84 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/crosapi/fullscreen_controller_ash.h" + +#include "base/test/task_environment.h" +#include "base/test/test_future.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crosapi { + +using mojom::FullscreenControllerClient; + +using ShouldExitFullscreenBeforeLockCallback = + mojom::FullscreenControllerClient::ShouldExitFullscreenBeforeLockCallback; + +class FullscreenControllerAshBaseTest : public testing::Test { + public: + void InvokeShouldExitFullscreenBeforeLock(bool expected_result) { + base::test::TestFuture<bool> future; + fullscreen_controller_ash()->ShouldExitFullscreenBeforeLock( + future.GetCallback()); + + EXPECT_EQ(future.Get(), expected_result); + } + + FullscreenControllerAsh* fullscreen_controller_ash() { + return &fullscreen_controller_ash_; + } + + private: + base::test::SingleThreadTaskEnvironment task_environment_; + FullscreenControllerAsh fullscreen_controller_ash_; +}; + +// Test that the default (True) is returned if +// `ShouldExitFullscreenBeforeLock()` is invoked with no client bound. +TEST_F(FullscreenControllerAshBaseTest, + ShouldExitFullscreenBeforeLockWithoutBoundClient) { + InvokeShouldExitFullscreenBeforeLock(/*expected_result=*/true); +} + +class FullscreenControllerAshTest : public FullscreenControllerAshBaseTest, + public testing::WithParamInterface<bool> { + public: + class MockFullscreenControllerClient : public FullscreenControllerClient { + public: + MOCK_METHOD(void, + ShouldExitFullscreenBeforeLock, + (ShouldExitFullscreenBeforeLockCallback callback), + (override)); + }; + + protected: + testing::StrictMock<MockFullscreenControllerClient> client_; +}; + +// Test that the correct response is returned if +// `ShouldExitFullscreenBeforeLock()` is invoked with a client bound. +TEST_P(FullscreenControllerAshTest, + ShouldExitFullscreenBeforeLockWithBoundClient) { + bool expected_result = GetParam(); + + // Bind `client_`. + mojo::Receiver<FullscreenControllerClient> client_receiver{&client_}; + fullscreen_controller_ash()->AddClient( + client_receiver.BindNewPipeAndPassRemoteWithVersion()); + + // Mock `client_` response for `ShouldExitFullscreenBeforeLock()`. + EXPECT_CALL(client_, ShouldExitFullscreenBeforeLock) + .WillOnce(testing::Invoke( + [expected_result](ShouldExitFullscreenBeforeLockCallback callback) { + std::move(callback).Run(expected_result); + })); + + InvokeShouldExitFullscreenBeforeLock(expected_result); +} + +INSTANTIATE_TEST_SUITE_P(All, FullscreenControllerAshTest, testing::Bool()); + +} // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc index ca038d0..e384281 100644 --- a/chrome/browser/ash/crosapi/test_controller_ash.cc +++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -365,6 +365,9 @@ standalone_browser_test_controller_.Bind(std::move(controller)); standalone_browser_test_controller_.set_disconnect_handler(base::BindOnce( &TestControllerAsh::OnControllerDisconnected, base::Unretained(this))); + + if (!on_standalone_browser_test_controller_bound_.is_signaled()) + on_standalone_browser_test_controller_bound_.Signal(); } void TestControllerAsh::WaiterFinished(OverviewWaiter* waiter) {
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.h b/chrome/browser/ash/crosapi/test_controller_ash.h index 3b8de6b..b97198e5 100644 --- a/chrome/browser/ash/crosapi/test_controller_ash.h +++ b/chrome/browser/ash/crosapi/test_controller_ash.h
@@ -9,10 +9,12 @@ #include <string> #include <vector> +#include "base/one_shot_event.h" #include "chrome/browser/ash/crosapi/crosapi_ash.h" #include "chromeos/crosapi/mojom/test_controller.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" #include "ui/base/models/simple_menu_model.h" namespace crosapi { @@ -101,6 +103,12 @@ return standalone_browser_test_controller_; } + // Signals when standalone browser test controller becomes bound. + const base::OneShotEvent& on_standalone_browser_test_controller_bound() + const { + return on_standalone_browser_test_controller_bound_; + } + private: class OverviewWaiter; @@ -135,6 +143,8 @@ // Controller to send commands to the connected lacros crosapi client. mojo::Remote<mojom::StandaloneBrowserTestController> standalone_browser_test_controller_; + + base::OneShotEvent on_standalone_browser_test_controller_bound_; }; class TestShillControllerAsh : public crosapi::mojom::TestShillController {
diff --git a/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc b/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc index 2c5922f..2c09847 100644 --- a/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc +++ b/chrome/browser/ash/crosapi/vpn_extension_observer_ash_browsertest.cc
@@ -6,11 +6,7 @@ #include "ash/public/cpp/network_config_service.h" #include "base/test/bind.h" #include "base/test/test_future.h" -#include "chrome/browser/ash/crosapi/crosapi_ash.h" -#include "chrome/browser/ash/crosapi/crosapi_manager.h" -#include "chrome/browser/ash/crosapi/test_controller_ash.h" -#include "chrome/test/base/chromeos/ash_browser_test_starter.h" -#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h" #include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" #include "chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" @@ -24,38 +20,17 @@ } // namespace -class VpnExtensionObserverBrowserTest : public InProcessBrowserTest { +class VpnExtensionObserverBrowserTest + : public crosapi::AshRequiresLacrosBrowserTestBase { public: - void SetUpInProcessBrowserTestFixture() override { - if (!ash_starter_.HasLacrosArgument()) { - return; - } - ASSERT_TRUE(ash_starter_.PrepareEnvironmentForLacros()); - } - - void SetUpOnMainThread() override { - if (!ash_starter_.HasLacrosArgument()) { - return; - } - auto* manager = crosapi::CrosapiManager::Get(); - test_controller_ash_ = std::make_unique<crosapi::TestControllerAsh>(); - manager->crosapi_ash()->SetTestControllerForTesting( - test_controller_ash_.get()); - ash_starter_.StartLacros(this); - } - std::string LoadVpnExtension(const std::string& extension_name) { crosapi::mojom::StandaloneBrowserTestControllerAsyncWaiter waiter( - test_controller_ash_->GetStandaloneBrowserTestController().get()); + GetStandaloneBrowserTestController()); std::string extension_id; waiter.LoadVpnExtension(extension_name, &extension_id); return extension_id; } - - protected: - test::AshBrowserTestStarter ash_starter_; - std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_; }; class ExtensionEventWaiter @@ -87,10 +62,8 @@ mojo::Remote<cros_network::mojom::CrosNetworkConfig> cros_network_config_; }; -// TODO(1339457): This test is flaky. -IN_PROC_BROWSER_TEST_F(VpnExtensionObserverBrowserTest, - DISABLED_LoadVpnExtension) { - if (!ash_starter_.HasLacrosArgument()) { +IN_PROC_BROWSER_TEST_F(VpnExtensionObserverBrowserTest, LoadVpnExtension) { + if (!HasLacrosArgument()) { return; } auto waiter = std::make_unique<ExtensionEventWaiter>();
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task.cc b/chrome/browser/ash/file_manager/empty_trash_io_task.cc index 376ad74..e704b78 100644 --- a/chrome/browser/ash/file_manager/empty_trash_io_task.cc +++ b/chrome/browser/ash/file_manager/empty_trash_io_task.cc
@@ -13,7 +13,6 @@ #include "content/public/browser/browser_thread.h" namespace file_manager::io_task { - namespace { storage::FileSystemOperationRunner::OperationID @@ -63,20 +62,20 @@ complete_callback_ = std::move(complete_callback); enabled_trash_locations_ = - GenerateEnabledTrashLocationsForProfile(profile_, base_path_); + trash::GenerateEnabledTrashLocationsForProfile(profile_, base_path_); progress_.state = State::kInProgress; - TrashPathsMap::const_iterator it = enabled_trash_locations_.cbegin(); + trash::TrashPathsMap::const_iterator it = enabled_trash_locations_.cbegin(); if (it == enabled_trash_locations_.end()) { Complete(State::kSuccess); return; } - RemoveTrashSubDirectory(it, kFilesFolderName); + RemoveTrashSubDirectory(it, trash::kFilesFolderName); } void EmptyTrashIOTask::RemoveTrashSubDirectory( - TrashPathsMap::const_iterator& trash_location, + trash::TrashPathsMap::const_iterator& trash_location, const std::string& folder_name_to_remove) { const base::FilePath& trash_parent_path = trash_location->first; const base::FilePath trash_path = @@ -104,7 +103,7 @@ } void EmptyTrashIOTask::OnRemoveTrashSubDirectory( - TrashPathsMap::const_iterator& it, + trash::TrashPathsMap::const_iterator& it, const std::string& removed_folder_name, base::File::Error status) { progress_.outputs[progress_.outputs.size() - 1].error = status; @@ -113,8 +112,8 @@ Complete(State::kError); return; } - if (removed_folder_name == kFilesFolderName) { - RemoveTrashSubDirectory(it, kInfoFolderName); + if (removed_folder_name == trash::kFilesFolderName) { + RemoveTrashSubDirectory(it, trash::kInfoFolderName); return; } it++; @@ -123,7 +122,7 @@ return; } - RemoveTrashSubDirectory(it, kFilesFolderName); + RemoveTrashSubDirectory(it, trash::kFilesFolderName); } // Calls the completion callback for the task. `progress_` should not be
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task.h b/chrome/browser/ash/file_manager/empty_trash_io_task.h index 0f6c93c..1d18178 100644 --- a/chrome/browser/ash/file_manager/empty_trash_io_task.h +++ b/chrome/browser/ash/file_manager/empty_trash_io_task.h
@@ -49,12 +49,13 @@ private: // Removes the entire trash subdirectory (e.g. .Trash/files) recursively. It // only iterates over the enabled trash locations. - void RemoveTrashSubDirectory(TrashPathsMap::const_iterator& trash_location, - const std::string& folder_name_to_remove); + void RemoveTrashSubDirectory( + trash::TrashPathsMap::const_iterator& trash_location, + const std::string& folder_name_to_remove); // After removing the trash directory, continue iterating until there are no // more enabled trash directories left. - void OnRemoveTrashSubDirectory(TrashPathsMap::const_iterator& it, + void OnRemoveTrashSubDirectory(trash::TrashPathsMap::const_iterator& it, const std::string& removed_folder_name, base::File::Error status); @@ -73,7 +74,7 @@ raw_ptr<Profile> profile_; // A map containing paths which are enabled for trashing. - TrashPathsMap enabled_trash_locations_; + trash::TrashPathsMap enabled_trash_locations_; // Stores the id of the restore operation if one is in progress. Used to stop // the empty trash operation.
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc index 0d21f67..4b1aaa4 100644 --- a/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc +++ b/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc
@@ -63,8 +63,10 @@ trash_parent_path.Append(relative_trash_folder); EXPECT_TRUE(EnsureTrashDirectorySetup(trash_directory)); - trash_subdirectories.emplace_back(trash_directory.Append(kFilesFolderName)); - trash_subdirectories.emplace_back(trash_directory.Append(kInfoFolderName)); + trash_subdirectories.emplace_back( + trash_directory.Append(trash::kFilesFolderName)); + trash_subdirectories.emplace_back( + trash_directory.Append(trash::kInfoFolderName)); return trash_directory; } @@ -72,12 +74,14 @@ TrashDirectoriesAndSubDirectories directories; // Setup ~/MyFiles/.Trash - directories.trash_directories.emplace_back(SetupTrashDirectory( - my_files_dir_, kTrashFolderName, directories.trash_subdirectories)); + directories.trash_directories.emplace_back( + SetupTrashDirectory(my_files_dir_, trash::kTrashFolderName, + directories.trash_subdirectories)); // Setup ~/MyFiles/Downloads/.Trash - directories.trash_directories.emplace_back(SetupTrashDirectory( - downloads_dir_, kTrashFolderName, directories.trash_subdirectories)); + directories.trash_directories.emplace_back( + SetupTrashDirectory(downloads_dir_, trash::kTrashFolderName, + directories.trash_subdirectories)); // Setup /media/fuse/termina_hash_pengiun/.local/share/Trash directories.trash_directories.emplace_back(SetupTrashDirectory(
diff --git a/chrome/browser/ash/file_manager/restore_io_task.cc b/chrome/browser/ash/file_manager/restore_io_task.cc index 5f92bd17c..a5b8808 100644 --- a/chrome/browser/ash/file_manager/restore_io_task.cc +++ b/chrome/browser/ash/file_manager/restore_io_task.cc
@@ -16,8 +16,7 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { namespace { @@ -78,7 +77,8 @@ } progress_.state = State::kInProgress; - validator_ = std::make_unique<TrashInfoValidator>(profile_, base_path_); + validator_ = + std::make_unique<trash::TrashInfoValidator>(profile_, base_path_); validator_->SetDisconnectHandler(base::BindOnce( &RestoreIOTask::Complete, weak_ptr_factory_.GetWeakPtr(), State::kError)); @@ -111,7 +111,7 @@ void RestoreIOTask::EnsureParentRestorePathExists( size_t idx, - base::FileErrorOr<ParsedTrashInfoData> parsed_data) { + base::FileErrorOr<trash::ParsedTrashInfoData> parsed_data) { if (parsed_data.is_error()) { progress_.sources[idx].error = parsed_data.error(); Complete(State::kError); @@ -265,5 +265,4 @@ operation_id_.emplace(id); } -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task
diff --git a/chrome/browser/ash/file_manager/restore_io_task.h b/chrome/browser/ash/file_manager/restore_io_task.h index b0d4656..027dcc8 100644 --- a/chrome/browser/ash/file_manager/restore_io_task.h +++ b/chrome/browser/ash/file_manager/restore_io_task.h
@@ -19,8 +19,7 @@ class Profile; -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { // This class represents a task restoring from trash. A restore task attempts to // restore files from a supported Trash folder back to it's original path. If @@ -54,7 +53,7 @@ // actually exists. In the event the file path has been removed, recreate it. void EnsureParentRestorePathExists( size_t idx, - base::FileErrorOr<ParsedTrashInfoData> parsed_data); + base::FileErrorOr<trash::ParsedTrashInfoData> parsed_data); void OnParentRestorePathExists(size_t idx, const base::FilePath& trashed_file_location, @@ -100,7 +99,7 @@ absl::optional<storage::FileSystemOperationRunner::OperationID> operation_id_; // Validates and parses .trashinfo files. - std::unique_ptr<TrashInfoValidator> validator_ = nullptr; + std::unique_ptr<trash::TrashInfoValidator> validator_ = nullptr; ProgressCallback progress_callback_; CompleteCallback complete_callback_; @@ -108,7 +107,6 @@ base::WeakPtrFactory<RestoreIOTask> weak_ptr_factory_{this}; }; -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task #endif // CHROME_BROWSER_ASH_FILE_MANAGER_RESTORE_IO_TASK_H_
diff --git a/chrome/browser/ash/file_manager/restore_io_task_unittest.cc b/chrome/browser/ash/file_manager/restore_io_task_unittest.cc index ef9f35c4..9cae2bc 100644 --- a/chrome/browser/ash/file_manager/restore_io_task_unittest.cc +++ b/chrome/browser/ash/file_manager/restore_io_task_unittest.cc
@@ -28,8 +28,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/storage_key/storage_key.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { namespace { using ::base::test::RunClosure; @@ -155,9 +154,10 @@ EnsureTrashDirectorySetup(downloads_dir_); std::string foo_contents = base::RandBytesAsString(kTestFileSize); - const base::FilePath file_path = downloads_dir_.Append(kTrashFolderName) - .Append(kInfoFolderName) - .Append("foo.txt.trashinfo"); + const base::FilePath file_path = + downloads_dir_.Append(trash::kTrashFolderName) + .Append(trash::kInfoFolderName) + .Append("foo.txt.trashinfo"); ASSERT_TRUE(base::WriteFile(file_path, foo_contents)); base::RunLoop run_loop; @@ -191,12 +191,13 @@ std::string foo_metadata_contents = GenerateTrashInfoContents("/../../../bad/actor/foo.txt"); - const base::FilePath trash_path = downloads_dir_.Append(kTrashFolderName); + const base::FilePath trash_path = + downloads_dir_.Append(trash::kTrashFolderName); const base::FilePath info_file_path = - trash_path.Append(kInfoFolderName).Append("foo.txt.trashinfo"); + trash_path.Append(trash::kInfoFolderName).Append("foo.txt.trashinfo"); ASSERT_TRUE(base::WriteFile(info_file_path, foo_metadata_contents)); const base::FilePath files_path = - trash_path.Append(kFilesFolderName).Append("foo.txt"); + trash_path.Append(trash::kFilesFolderName).Append("foo.txt"); ASSERT_TRUE(base::WriteFile(files_path, foo_contents)); base::RunLoop run_loop; @@ -228,12 +229,13 @@ std::string foo_metadata_contents = GenerateTrashInfoContents("/Downloads/bar/foo.txt"); - const base::FilePath trash_path = downloads_dir_.Append(kTrashFolderName); + const base::FilePath trash_path = + downloads_dir_.Append(trash::kTrashFolderName); const base::FilePath info_file_path = - trash_path.Append(kInfoFolderName).Append("foo.txt.trashinfo"); + trash_path.Append(trash::kInfoFolderName).Append("foo.txt.trashinfo"); ASSERT_TRUE(base::WriteFile(info_file_path, foo_metadata_contents)); const base::FilePath files_path = - trash_path.Append(kFilesFolderName).Append("foo.txt"); + trash_path.Append(trash::kFilesFolderName).Append("foo.txt"); ASSERT_TRUE(base::WriteFile(files_path, foo_contents)); base::RunLoop run_loop; @@ -264,12 +266,13 @@ std::string foo_metadata_contents = GenerateTrashInfoContents("/Downloads/bar/foo.txt"); - const base::FilePath trash_path = downloads_dir_.Append(kTrashFolderName); + const base::FilePath trash_path = + downloads_dir_.Append(trash::kTrashFolderName); const base::FilePath info_file_path = - trash_path.Append(kInfoFolderName).Append("foo.txt.trashinfo"); + trash_path.Append(trash::kInfoFolderName).Append("foo.txt.trashinfo"); ASSERT_TRUE(base::WriteFile(info_file_path, foo_metadata_contents)); const base::FilePath files_path = - trash_path.Append(kFilesFolderName).Append("foo.txt"); + trash_path.Append(trash::kFilesFolderName).Append("foo.txt"); ASSERT_TRUE(base::WriteFile(files_path, foo_contents)); // Create conflicting item at same place restore is going to happen at. @@ -369,12 +372,13 @@ std::string foo_contents = base::RandBytesAsString(kTestFileSize); - const base::FilePath trash_path = downloads_dir_.Append(kTrashFolderName); + const base::FilePath trash_path = + downloads_dir_.Append(trash::kTrashFolderName); const base::FilePath info_file_path = - trash_path.Append(kInfoFolderName).Append("foo.txt.trashinfo"); + trash_path.Append(trash::kInfoFolderName).Append("foo.txt.trashinfo"); ASSERT_TRUE(base::WriteFile(info_file_path, foo_contents)); const base::FilePath files_path = - trash_path.Append(kFilesFolderName).Append("foo.txt"); + trash_path.Append(trash::kFilesFolderName).Append("foo.txt"); ASSERT_TRUE(base::WriteFile(files_path, foo_contents)); base::RunLoop run_loop; @@ -397,5 +401,4 @@ } } // namespace -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task
diff --git a/chrome/browser/ash/file_manager/trash_common_util.cc b/chrome/browser/ash/file_manager/trash_common_util.cc index c260696..8bb2f894 100644 --- a/chrome/browser/ash/file_manager/trash_common_util.cc +++ b/chrome/browser/ash/file_manager/trash_common_util.cc
@@ -9,8 +9,7 @@ #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/ash/file_manager/volume_manager.h" -namespace file_manager { -namespace io_task { +namespace file_manager::trash { constexpr char kTrashFolderName[] = ".Trash"; constexpr char kInfoFolderName[] = "info"; @@ -104,5 +103,4 @@ return enabled_trash_locations; } -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::trash
diff --git a/chrome/browser/ash/file_manager/trash_common_util.h b/chrome/browser/ash/file_manager/trash_common_util.h index 3624ad21..31570c6 100644 --- a/chrome/browser/ash/file_manager/trash_common_util.h +++ b/chrome/browser/ash/file_manager/trash_common_util.h
@@ -12,8 +12,7 @@ class Profile; -namespace file_manager { -namespace io_task { +namespace file_manager::trash { // Constant representing the Trash folder name. extern const char kTrashFolderName[]; @@ -89,7 +88,6 @@ Profile* profile, const base::FilePath& base_path); -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::trash #endif // CHROME_BROWSER_ASH_FILE_MANAGER_TRASH_COMMON_UTIL_H_
diff --git a/chrome/browser/ash/file_manager/trash_info_validator.cc b/chrome/browser/ash/file_manager/trash_info_validator.cc index f35e6ca..0c70a98 100644 --- a/chrome/browser/ash/file_manager/trash_info_validator.cc +++ b/chrome/browser/ash/file_manager/trash_info_validator.cc
@@ -14,7 +14,7 @@ class Profile; -namespace file_manager { +namespace file_manager::trash { namespace { @@ -28,7 +28,7 @@ TrashInfoValidator::TrashInfoValidator(Profile* profile, const base::FilePath& base_path) { enabled_trash_locations_ = - io_task::GenerateEnabledTrashLocationsForProfile(profile, base_path); + trash::GenerateEnabledTrashLocationsForProfile(profile, base_path); parser_ = std::make_unique<chromeos::trash_service::TrashInfoParser>(); } @@ -47,7 +47,7 @@ const base::FilePath& trash_info_path, ValidateAndParseTrashInfoCallback callback) { // Validates the supplied file ends in a .trashinfo extension. - if (trash_info_path.FinalExtension() != io_task::kTrashInfoExtension) { + if (trash_info_path.FinalExtension() != kTrashInfoExtension) { RunCallbackWithError(base::File::FILE_ERROR_INVALID_URL, std::move(callback)); return; @@ -74,7 +74,7 @@ // Ensure the corresponding file that this metadata file refers to actually // exists. base::FilePath trashed_file_location = - trash_folder_location.Append(io_task::kFilesFolderName) + trash_folder_location.Append(kFilesFolderName) .Append(trash_info_path.BaseName().RemoveFinalExtension()); base::ThreadPool::PostTaskAndReplyWithResult( @@ -143,4 +143,4 @@ base::FileErrorOr<ParsedTrashInfoData>(std::move(parsed_data))); } -} // namespace file_manager +} // namespace file_manager::trash
diff --git a/chrome/browser/ash/file_manager/trash_info_validator.h b/chrome/browser/ash/file_manager/trash_info_validator.h index 25b3295e..e030b86 100644 --- a/chrome/browser/ash/file_manager/trash_info_validator.h +++ b/chrome/browser/ash/file_manager/trash_info_validator.h
@@ -14,7 +14,7 @@ #include "chrome/browser/ash/file_manager/trash_common_util.h" #include "chromeos/ash/components/trash_service/public/cpp/trash_info_parser.h" -namespace file_manager { +namespace file_manager::trash { // On a successful parse of .trashinfo files, returns the restoration path, // deletion date and actual location of the trashed file. @@ -64,8 +64,9 @@ // - Resides in an enabled trash directory // - The file resides in the info directory // - Has an identical item in the files directory with no .trashinfo suffix - // On confirming the above it then calls the TrashService to retrieve the - // parsed trashinfo data. The `trash_info_path` must be absolute. + // In the event the above fails, the `callback` will be invoked with an error, + // on success it then calls the TrashService to retrieve the parsed trashinfo + // data. The `trash_info_path` must be absolute. void ValidateAndParseTrashInfo(const base::FilePath& trash_info_path, ValidateAndParseTrashInfoCallback callback); @@ -94,7 +95,7 @@ base::Time deletion_date); // A map containing paths which are enabled for trashing. - io_task::TrashPathsMap enabled_trash_locations_; + trash::TrashPathsMap enabled_trash_locations_; // Holds the connection open to the `TrashService`. This is a sandboxed // process that performs parsing of the trashinfo files. @@ -103,6 +104,6 @@ base::WeakPtrFactory<TrashInfoValidator> weak_ptr_factory_{this}; }; -} // namespace file_manager +} // namespace file_manager::trash #endif // CHROME_BROWSER_ASH_FILE_MANAGER_TRASH_INFO_VALIDATOR_H_
diff --git a/chrome/browser/ash/file_manager/trash_io_task.cc b/chrome/browser/ash/file_manager/trash_io_task.cc index 79e08e2a..586b2dd 100644 --- a/chrome/browser/ash/file_manager/trash_io_task.cc +++ b/chrome/browser/ash/file_manager/trash_io_task.cc
@@ -23,8 +23,7 @@ #include "content/public/browser/browser_thread.h" #include "google_apis/common/task_util.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { namespace { // Generates and updates the `entry` with the standard contents of the @@ -127,7 +126,7 @@ // Build the list of known paths that are enabled, for now Downloads is a bind // mount at MyFiles/Downloads so treat them as separate volumes. free_space_map_ = - GenerateEnabledTrashLocationsForProfile(profile_, base_path_); + trash::GenerateEnabledTrashLocationsForProfile(profile_, base_path_); progress_.state = State::kInProgress; UpdateTrashEntry(0); @@ -153,7 +152,7 @@ // sorted by key. base::FilePath keys will insert in lexicographical order // however in the case of nested directories, reverse lexicographical order is // preferred to ensure the closer parent path by depth is chosen. - const TrashPathsMap::reverse_iterator& trash_parent_path_it = + const trash::TrashPathsMap::reverse_iterator& trash_parent_path_it = std::find_if(free_space_map_.rbegin(), free_space_map_.rend(), [&source_path](const auto& it) -> bool { return it.first.IsParent(source_path); @@ -168,7 +167,7 @@ return; } - TrashLocation& trash_location = trash_parent_path_it->second; + trash::TrashLocation& trash_location = trash_parent_path_it->second; const base::FilePath trash_parent_path = trash_parent_path_it->first; TrashEntry& entry = trash_entries_[source_idx]; entry.trash_mount_path = trash_parent_path; @@ -194,7 +193,7 @@ void TrashIOTask::ValidateAndDecrementFreeSpace( size_t source_idx, - const TrashPathsMap::reverse_iterator& it) { + const trash::TrashPathsMap::reverse_iterator& it) { size_t trash_contents_size = trash_entries_[source_idx].trash_info_contents.size(); progress_.total_bytes += trash_contents_size; @@ -253,8 +252,9 @@ SetupSubDirectory(it, it->second.trash_files); } -void TrashIOTask::GetFreeDiskSpace(size_t source_idx, - const TrashPathsMap::reverse_iterator& it) { +void TrashIOTask::GetFreeDiskSpace( + size_t source_idx, + const trash::TrashPathsMap::reverse_iterator& it) { base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace, @@ -278,17 +278,20 @@ return base::FilePath(relative_path); } -void TrashIOTask::GotFreeDiskSpace(size_t source_idx, - const TrashPathsMap::reverse_iterator& it, - int64_t free_space) { - TrashLocation& trash_location = it->second; +void TrashIOTask::GotFreeDiskSpace( + size_t source_idx, + const trash::TrashPathsMap::reverse_iterator& it, + int64_t free_space) { + trash::TrashLocation& trash_location = it->second; const base::FilePath& trash_parent_path = it->first; base::FilePath trash_path = MakeRelativeFromBasePath( trash_parent_path.Append(trash_location.relative_folder_path)); - trash_location.trash_files = CreateFileSystemURL( - progress_.sources[source_idx].url, trash_path.Append(kFilesFolderName)); - trash_location.trash_info = CreateFileSystemURL( - progress_.sources[source_idx].url, trash_path.Append(kInfoFolderName)); + trash_location.trash_files = + CreateFileSystemURL(progress_.sources[source_idx].url, + trash_path.Append(trash::kFilesFolderName)); + trash_location.trash_info = + CreateFileSystemURL(progress_.sources[source_idx].url, + trash_path.Append(trash::kInfoFolderName)); trash_location.free_space = free_space; trash_location.require_setup = true; @@ -296,7 +299,7 @@ } void TrashIOTask::SetupSubDirectory( - TrashPathsMap::const_iterator& it, + trash::TrashPathsMap::const_iterator& it, const storage::FileSystemURL trash_subdirectory) { // All enabled trash directories exist in the `free_space_map_` however some // may not be used for this IO task. Skip the ones that don't require setup. @@ -325,7 +328,7 @@ } void TrashIOTask::OnSetupSubDirectory( - TrashPathsMap::const_iterator& it, + trash::TrashPathsMap::const_iterator& it, const storage::FileSystemURL trash_subdirectory, base::File::Error error) { if (error != base::File::FILE_OK) { @@ -360,7 +363,7 @@ const TrashEntry& entry = trash_entries_[source_idx]; const auto trash_path = MakeRelativeFromBasePath( entry.trash_mount_path.Append(entry.relative_trash_path) - .Append(kFilesFolderName)); + .Append(trash::kFilesFolderName)); const storage::FileSystemURL files_location = CreateFileSystemURL(progress_.sources[source_idx].url, trash_path); @@ -388,8 +391,8 @@ const std::string file_name = destination_result.value().path().BaseName().value(); - const base::FilePath destination_path = - GenerateTrashPath(absolute_trash_path, kInfoFolderName, file_name); + const base::FilePath destination_path = trash::GenerateTrashPath( + absolute_trash_path, trash::kInfoFolderName, file_name); progress_.outputs.emplace_back( CreateFileSystemURL(progress_.sources[source_idx].url, destination_path), absl::nullopt); @@ -517,5 +520,4 @@ operation_id_.emplace(id); } -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task
diff --git a/chrome/browser/ash/file_manager/trash_io_task.h b/chrome/browser/ash/file_manager/trash_io_task.h index 126588b0..bc331bf 100644 --- a/chrome/browser/ash/file_manager/trash_io_task.h +++ b/chrome/browser/ash/file_manager/trash_io_task.h
@@ -24,8 +24,7 @@ class Profile; -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { namespace { @@ -96,24 +95,25 @@ const base::FilePath& path); void SetCurrentOperationID( storage::FileSystemOperationRunner::OperationID id); - void ValidateAndDecrementFreeSpace(size_t source_idx, - const TrashPathsMap::reverse_iterator& it); + void ValidateAndDecrementFreeSpace( + size_t source_idx, + const trash::TrashPathsMap::reverse_iterator& it); // Get the free disk space for `trash_parent_path` to know whether the // metadata can be written. The `folder_name` is used to differentiate between // .Trash and .Trash-1000 folder names on various file systems (both are valid // in the XDG spec). void GetFreeDiskSpace(size_t source_idx, - const TrashPathsMap::reverse_iterator& it); + const trash::TrashPathsMap::reverse_iterator& it); void GotFreeDiskSpace(size_t source_idx, - const TrashPathsMap::reverse_iterator& it, + const trash::TrashPathsMap::reverse_iterator& it, int64_t free_space); // Sets up the .Trash/files and .Trash/info subdirectories specified by the // `trash_subdirectory` parameter. Will create the parent directories as well // in the instance .Trash folder does not exist. - void SetupSubDirectory(TrashPathsMap::const_iterator& it, + void SetupSubDirectory(trash::TrashPathsMap::const_iterator& it, const storage::FileSystemURL trash_subdirectory); - void OnSetupSubDirectory(TrashPathsMap::const_iterator& it, + void OnSetupSubDirectory(trash::TrashPathsMap::const_iterator& it, const storage::FileSystemURL trash_subdirectory, base::File::Error error); base::FilePath MakeRelativeFromBasePath(const base::FilePath& absolute_path); @@ -160,7 +160,7 @@ // Maintains the free space required to write all the metadata files along // with the underlying locations of the .Trash/{files,info} directories. - TrashPathsMap free_space_map_; + trash::TrashPathsMap free_space_map_; // Stores the size reported by the last progress update so we can compute the // delta on the next progress update. @@ -189,7 +189,6 @@ base::WeakPtrFactory<TrashIOTask> weak_ptr_factory_{this}; }; -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task #endif // CHROME_BROWSER_ASH_FILE_MANAGER_TRASH_IO_TASK_H_
diff --git a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc index 3f3b366..0329edf 100644 --- a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc +++ b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
@@ -26,8 +26,7 @@ #include "storage/common/file_system/file_system_types.h" #include "testing/gmock/include/gmock/gmock.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { namespace { using ::base::test::RunClosure; @@ -69,10 +68,11 @@ }; void AssertTrashSetup(const base::FilePath& parent_path) { - base::FilePath trash_path = parent_path.Append(kTrashFolderName); + base::FilePath trash_path = parent_path.Append(trash::kTrashFolderName); ASSERT_TRUE(base::DirectoryExists(trash_path)); - ASSERT_TRUE(base::DirectoryExists(trash_path.Append(kFilesFolderName))); - ASSERT_TRUE(base::DirectoryExists(trash_path.Append(kInfoFolderName))); + ASSERT_TRUE( + base::DirectoryExists(trash_path.Append(trash::kFilesFolderName))); + ASSERT_TRUE(base::DirectoryExists(trash_path.Append(trash::kInfoFolderName))); } void ExpectFileContents(const base::FilePath& path, @@ -380,13 +380,13 @@ const base::FilePath trash_path = crostini_dir_.AppendASCII(".local/share/Trash"); const base::FilePath files_path = - GenerateTrashPath(trash_path, kFilesFolderName, file_name); + trash::GenerateTrashPath(trash_path, trash::kFilesFolderName, file_name); ExpectFileContents(files_path, foo_contents); // Ensure the contents of the files at // .local/share/Trash/info/foo.txt.trashinfo contains the expected content. const base::FilePath info_path = - GenerateTrashPath(trash_path, kInfoFolderName, file_name); + trash::GenerateTrashPath(trash_path, trash::kInfoFolderName, file_name); ExpectFileContents(info_path, file_trashinfo_contents); } @@ -438,5 +438,4 @@ } } // namespace -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task
diff --git a/chrome/browser/ash/file_manager/trash_unittest_base.cc b/chrome/browser/ash/file_manager/trash_unittest_base.cc index 62de372..99f9b733 100644 --- a/chrome/browser/ash/file_manager/trash_unittest_base.cc +++ b/chrome/browser/ash/file_manager/trash_unittest_base.cc
@@ -19,8 +19,7 @@ #include "storage/browser/file_system/external_mount_points.h" #include "storage/browser/test/test_file_system_context.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { TrashBaseTest::TrashBaseTest() = default; @@ -132,14 +131,16 @@ const base::FilePath TrashBaseTest::GenerateInfoPath( const std::string& file_name) { - return GenerateTrashPath(downloads_dir_.Append(kTrashFolderName), - kInfoFolderName, file_name); + return trash::GenerateTrashPath( + downloads_dir_.Append(trash::kTrashFolderName), trash::kInfoFolderName, + file_name); } const base::FilePath TrashBaseTest::GenerateFilesPath( const std::string& file_name) { - return GenerateTrashPath(downloads_dir_.Append(kTrashFolderName), - kFilesFolderName, file_name); + return trash::GenerateTrashPath( + downloads_dir_.Append(trash::kTrashFolderName), trash::kFilesFolderName, + file_name); } const std::string TrashBaseTest::CreateTrashInfoContentsFromPath( @@ -168,15 +169,14 @@ bool TrashBaseTest::EnsureTrashDirectorySetup( const base::FilePath& parent_path) { - base::FilePath trash_path = parent_path.Append(kTrashFolderName); - if (!base::CreateDirectory(trash_path.Append(kInfoFolderName))) { + base::FilePath trash_path = parent_path.Append(trash::kTrashFolderName); + if (!base::CreateDirectory(trash_path.Append(trash::kInfoFolderName))) { return false; } - if (!base::CreateDirectory(trash_path.Append(kFilesFolderName))) { + if (!base::CreateDirectory(trash_path.Append(trash::kFilesFolderName))) { return false; } return true; } -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task
diff --git a/chrome/browser/ash/file_manager/trash_unittest_base.h b/chrome/browser/ash/file_manager/trash_unittest_base.h index a86bcb7..c25642e 100644 --- a/chrome/browser/ash/file_manager/trash_unittest_base.h +++ b/chrome/browser/ash/file_manager/trash_unittest_base.h
@@ -21,8 +21,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/storage_key/storage_key.h" -namespace file_manager { -namespace io_task { +namespace file_manager::io_task { inline constexpr size_t kTestFileSize = 32; @@ -84,7 +83,6 @@ scoped_refptr<storage::FileSystemContext> file_system_context_; }; -} // namespace io_task -} // namespace file_manager +} // namespace file_manager::io_task #endif // CHROME_BROWSER_ASH_FILE_MANAGER_TRASH_IO_TASK_H_
diff --git a/chrome/browser/ash/guest_os/guest_os_session_tracker.cc b/chrome/browser/ash/guest_os/guest_os_session_tracker.cc index 0cadeb99..785dcf6 100644 --- a/chrome/browser/ash/guest_os/guest_os_session_tracker.cc +++ b/chrome/browser/ash/guest_os/guest_os_session_tracker.cc
@@ -153,10 +153,20 @@ return; } vms_.erase(signal.name()); - base::EraseIf(guests_, - [name = signal.name()](std::pair<GuestId, GuestInfo> pair) { - return pair.first.vm_name == name; - }); + std::vector<GuestId> ids; + for (const auto& pair : guests_) { + if (pair.first.vm_name != signal.name()) { + continue; + } + ids.push_back(pair.first); + } + for (const auto& id : ids) { + guests_.erase(id); + auto cb_list = container_shutdown_callbacks_.find(id); + if (cb_list != container_shutdown_callbacks_.end()) { + cb_list->second->Notify(); + } + } } // ash::CiceroneClient::Observer overrides. @@ -204,10 +214,14 @@ } GuestId id{VmType::UNKNOWN, signal.vm_name(), signal.container_name()}; guests_.erase(id); + auto cb_list = container_shutdown_callbacks_.find(id); + if (cb_list != container_shutdown_callbacks_.end()) { + cb_list->second->Notify(); + } } base::CallbackListSubscription GuestOsSessionTracker::RunOnceContainerStarted( - GuestId id, + const GuestId& id, base::OnceCallback<void(GuestInfo)> callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); auto iter = guests_.find(id); @@ -227,4 +241,15 @@ guests_.insert_or_assign(id, info); } +base::CallbackListSubscription GuestOsSessionTracker::RunOnShutdown( + const GuestId& id, + base::OnceCallback<void()> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto& cb_list = container_shutdown_callbacks_[id]; + if (!cb_list) { + cb_list = std::make_unique<base::OnceCallbackList<void()>>(); + } + return cb_list->Add(std::move(callback)); +} + } // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/guest_os_session_tracker.h b/chrome/browser/ash/guest_os/guest_os_session_tracker.h index 2a2f9bb3..8101277 100644 --- a/chrome/browser/ash/guest_os/guest_os_session_tracker.h +++ b/chrome/browser/ash/guest_os/guest_os_session_tracker.h
@@ -53,9 +53,15 @@ // RunOnceContainerStarted hangs forever. We need to list running containers // and adopt them, the same as we do for VMs. base::CallbackListSubscription RunOnceContainerStarted( - GuestId id, + const GuestId& id, base::OnceCallback<void(GuestInfo)> callback); + // Runs `callback` when the guest identified by `id` shuts down. To cancel the + // callback (e.g. upon timeout) destroy the returned subscription. + base::CallbackListSubscription RunOnShutdown( + const GuestId& id, + base::OnceCallback<void()> callback); + // Returns information about a running guest. Returns nullopt if the guest // isn't recognised e.g. it's not running. If you just want to check if a // guest is running or not and don't need the info, use `IsRunning` instead @@ -100,6 +106,8 @@ base::flat_map<GuestId, std::unique_ptr<base::OnceCallbackList<void(GuestInfo)>>> container_start_callbacks_; + base::flat_map<GuestId, std::unique_ptr<base::OnceCallbackList<void()>>> + container_shutdown_callbacks_; base::WeakPtrFactory<GuestOsSessionTracker> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ash/guest_os/guest_os_session_tracker_unittest.cc b/chrome/browser/ash/guest_os/guest_os_session_tracker_unittest.cc index 7b16295..55b435c 100644 --- a/chrome/browser/ash/guest_os/guest_os_session_tracker_unittest.cc +++ b/chrome/browser/ash/guest_os/guest_os_session_tracker_unittest.cc
@@ -199,4 +199,27 @@ EXPECT_FALSE(called); } +TEST_F(GuestOsSessionTrackerTest, RunOnContainerShutdown) { + FakeConciergeClient()->NotifyVmStarted(vm_started_signal_); + FakeCiceroneClient()->NotifyContainerStarted(container_started_signal_); + GuestId id{VmType::UNKNOWN, "vm_name", "penguin"}; + bool called = false; + auto _ = tracker_.RunOnShutdown( + id, base::BindLambdaForTesting([&called]() { called = true; })); + FakeCiceroneClient()->NotifyContainerShutdownSignal( + container_shutdown_signal_); + EXPECT_TRUE(called); +} + +TEST_F(GuestOsSessionTrackerTest, RunOnVmShutdown) { + FakeConciergeClient()->NotifyVmStarted(vm_started_signal_); + FakeCiceroneClient()->NotifyContainerStarted(container_started_signal_); + GuestId id{VmType::UNKNOWN, "vm_name", "penguin"}; + bool called = false; + auto _ = tracker_.RunOnShutdown( + id, base::BindLambdaForTesting([&called]() { called = true; })); + FakeConciergeClient()->NotifyVmStopped(vm_shutdown_signal_); + EXPECT_TRUE(called); +} + } // namespace guest_os
diff --git a/chrome/browser/ash/hats/hats_config.cc b/chrome/browser/ash/hats/hats_config.cc index 587bce2f..5d3e5727 100644 --- a/chrome/browser/ash/hats/hats_config.cc +++ b/chrome/browser/ash/hats/hats_config.cc
@@ -147,4 +147,14 @@ kHatsPersonalizationWallpaperSurveyCycleEndTs, // cycle_end_timestamp_pref_name }; +// MediaApp PDF Editing experience survey -- shown after a user clicks `Save` +// after editing a PDF in the MediaApp (Gallery), and the save is complete. +const HatsConfig kHatsMediaAppPdfSurvey = { + ::features::kHappinessTrackingMediaAppPdf, // feature + "Browser.ChromeOS.HatsSatisfaction.MediaAppPdf", // histogram_name + base::Days(7), // new_device_threshold + prefs::kHatsMediaAppPdfIsSelected, // hatsIsSelectedPrefName + prefs::kHatsMediaAppPdfCycleEndTs, // hatsCycleEndTimestampPrefName +}; + } // namespace ash
diff --git a/chrome/browser/ash/hats/hats_config.h b/chrome/browser/ash/hats/hats_config.h index 973f761..542a806f 100644 --- a/chrome/browser/ash/hats/hats_config.h +++ b/chrome/browser/ash/hats/hats_config.h
@@ -56,6 +56,7 @@ extern const HatsConfig kHatsPersonalizationAvatarSurvey; extern const HatsConfig kHatsPersonalizationScreensaverSurvey; extern const HatsConfig kHatsPersonalizationWallpaperSurvey; +extern const HatsConfig kHatsMediaAppPdfSurvey; } // namespace ash
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc index eddbf10b..254725f 100644 --- a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc +++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
@@ -291,6 +291,12 @@ return lock_screen_start_reauth_dialog_->GetDialogWidth(); } +content::WebContents* InSessionPasswordSyncManager::GetDialogWebContents() { + if (!lock_screen_start_reauth_dialog_) + return nullptr; + return lock_screen_start_reauth_dialog_->GetWebContents(); +} + bool InSessionPasswordSyncManager::IsReauthDialogLoadedForTesting( base::OnceClosure callback) { if (is_dialog_loaded_for_testing_)
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager.h b/chrome/browser/ash/login/saml/in_session_password_sync_manager.h index 9793c707..4858656 100644 --- a/chrome/browser/ash/login/saml/in_session_password_sync_manager.h +++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager.h
@@ -117,6 +117,9 @@ // Get lockscreen reauth dialog width. int GetDialogWidth(); + // Get web contents of lockscreen reauth dialog. + content::WebContents* GetDialogWebContents(); + // Check if reauth dialog is loaded and ready for testing. bool IsReauthDialogLoadedForTesting(base::OnceClosure callback);
diff --git a/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.cc b/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.cc index 38cc44ba..7ead5d8b 100644 --- a/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.cc +++ b/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.cc
@@ -69,6 +69,29 @@ return dialog_test_helper; } +// static +absl::optional<LockScreenReauthDialogTestHelper> +LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad() { + absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = + LockScreenReauthDialogTestHelper::ShowDialogAndWait(); + if (!reauth_dialog_helper.has_value()) { + return absl::nullopt; + } + + reauth_dialog_helper->ForceSamlRedirect(); + + // Expect the 'Verify Account' screen (the first screen the dialog shows) to + // be visible and proceed to the SAML page. + reauth_dialog_helper->WaitForVerifyAccountScreen(); + reauth_dialog_helper->ClickVerifyButton(); + + reauth_dialog_helper->WaitForSamlScreen(); + reauth_dialog_helper->ExpectVerifyAccountScreenHidden(); + + reauth_dialog_helper->WaitForIdpPageLoad(); + return reauth_dialog_helper; +} + bool LockScreenReauthDialogTestHelper::ShowDialogAndWaitImpl() { // Check precondition: Screen is locked. if (!session_manager::SessionManager::Get()->IsScreenLocked()) {
diff --git a/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h b/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h index 4c227ec..cc6a362 100644 --- a/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h +++ b/chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h
@@ -36,6 +36,12 @@ // Returns an empty `absl::optional` if the operation fails. static absl::optional<LockScreenReauthDialogTestHelper> ShowDialogAndWait(); + // Triggers the online re-authentication dialog, clicks through VerifyAccount + // screen and waits for IdP page to load. Returns an empty `absl::optional` if + // the operation fails. + static absl::optional<LockScreenReauthDialogTestHelper> + StartSamlAndWaitForIdpPageLoad(); + ~LockScreenReauthDialogTestHelper(); // Non-copyable, movable.
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc index 1c4dc61..dc667c23 100644 --- a/chrome/browser/ash/login/saml/saml_browsertest.cc +++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -32,7 +32,9 @@ #include "chrome/browser/ash/attestation/mock_machine_certificate_uploader.h" #include "chrome/browser/ash/attestation/tpm_challenge_key.h" #include "chrome/browser/ash/login/existing_user_controller.h" +#include "chrome/browser/ash/login/lock/screen_locker_tester.h" #include "chrome/browser/ash/login/saml/fake_saml_idp_mixin.h" +#include "chrome/browser/ash/login/saml/lockscreen_reauth_dialog_test_helper.h" #include "chrome/browser/ash/login/startup_utils.h" #include "chrome/browser/ash/login/test/device_state_mixin.h" #include "chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h" @@ -1677,16 +1679,49 @@ // pages is controlled by the kLoginVideoCaptureAllowedUrls pref rather than the // underlying user content setting. IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, TestLoginMediaPermission) { - fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html"); + fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html"); + SetLoginBehaviorPolicyToSAMLInterstitial(); + WaitForSigninScreen(); + if (GetParam()) { + ShowSAMLLoginForm(); + MaybeWaitForSAMLToLoad(); + } else { + ShowSAMLInterstitial(); + ClickNextOnSAMLInterstitialPage(); + } + + const GURL url0(fake_saml_idp()->GetSamlPageUrl()); const GURL url1("https://google.com"); const GURL url2("https://corp.example.com"); const GURL url3("https://not-allowed.com"); - SetLoginVideoCaptureAllowedUrls({url1, url2}); - WaitForSigninScreen(); + SetLoginVideoCaptureAllowedUrls({url0, url1, url2}); - // Make sure WebUI is loaded. - LoginDisplayHost::default_host()->GetWizardController(); + // Trigger the permission check from the js side. + { + bool get_user_media_success = false; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + SigninFrameJS().web_contents(), + "navigator.getUserMedia(" + " {video: true}," + " function() { window.domAutomationController.send(true); }," + " function() { window.domAutomationController.send(false); });", + &get_user_media_success)); + ASSERT_TRUE(get_user_media_success); + } + { + bool get_user_media_success = true; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + SigninFrameJS().web_contents(), + "navigator.getUserMedia(" + " {audio: true}," + " function() { window.domAutomationController.send(true); }," + " function() { window.domAutomationController.send(false); });", + &get_user_media_success)); + ASSERT_FALSE(get_user_media_success); + } + + // Check permissions directly by calling the `CheckMediaAccessPermission`. content::WebContents* web_contents = GetLoginUI()->GetWebContents(); content::WebContentsDelegate* web_contents_delegate = web_contents->GetDelegate(); @@ -1723,6 +1758,72 @@ blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)); } +// Tests that requesting webcam access from the lock screen works correctly. +IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, TestLockMediaPermission) { + SetSAMLOfflineSigninTimeLimitPolicy(0); + const GURL url0(fake_saml_idp()->GetSamlPageUrl()); + const GURL url1("https://google.com"); + const GURL url2("https://corp.example.com"); + const GURL url3("https://not-allowed.com"); + SetLoginVideoCaptureAllowedUrls({url0, url1, url2}); + ShowGAIALoginForm(); + LogInWithSAML(saml_test_users::kFirstUserCorpExampleComEmail, + kTestAuthSIDCookie1, kTestAuthLSIDCookie1); + ScreenLockerTester().Lock(); + + absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); + ASSERT_TRUE(reauth_dialog_helper); + + { + bool get_user_media_success = false; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + reauth_dialog_helper->SigninFrameJS().web_contents(), + "navigator.getUserMedia(" + " {video: true}," + " function() { window.domAutomationController.send(true); }," + " function() { window.domAutomationController.send(false); });", + &get_user_media_success)); + ASSERT_TRUE(get_user_media_success); + } + { + bool get_user_media_success = true; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + reauth_dialog_helper->SigninFrameJS().web_contents(), + "navigator.getUserMedia(" + " {audio: true}," + " function() { window.domAutomationController.send(true); }," + " function() { window.domAutomationController.send(false); });", + &get_user_media_success)); + ASSERT_FALSE(get_user_media_success); + } + + // Check permissions directly by calling the `CheckMediaAccessPermission`. + // Video devices should be allowed from the lock screen for specified urls. + content::WebContents* web_contents = + reauth_dialog_helper->DialogWebContents(); + content::WebContentsDelegate* web_contents_delegate = + web_contents->GetDelegate(); + + // Mic should always be blocked. + EXPECT_FALSE(web_contents_delegate->CheckMediaAccessPermission( + web_contents->GetPrimaryMainFrame(), url1, + blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)); + + // Camera should be allowed if allowed by the allowlist, otherwise blocked. + EXPECT_TRUE(web_contents_delegate->CheckMediaAccessPermission( + web_contents->GetPrimaryMainFrame(), url1, + blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)); + + EXPECT_TRUE(web_contents_delegate->CheckMediaAccessPermission( + web_contents->GetPrimaryMainFrame(), url2, + blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)); + + EXPECT_FALSE(web_contents_delegate->CheckMediaAccessPermission( + web_contents->GetPrimaryMainFrame(), url3, + blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE)); +} + INSTANTIATE_TEST_SUITE_P(All, SAMLPolicyTest, testing::Bool()); class SAMLPasswordAttributesTest : public SAMLPolicyTest {
diff --git a/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc b/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc index 89da72e..f59a588 100644 --- a/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc +++ b/chrome/browser/ash/login/saml/saml_lockscreen_browsertest.cc
@@ -148,29 +148,10 @@ AccountId GetAccountId() { return logged_in_user_mixin_.GetAccountId(); } - absl::optional<LockScreenReauthDialogTestHelper> - StartSamlAndWaitForIdpPageLoad() { - absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - LockScreenReauthDialogTestHelper::ShowDialogAndWait(); - DCHECK(reauth_dialog_helper); - reauth_dialog_helper->ForceSamlRedirect(); - - // Expect the 'Verify Account' screen (the first screen the dialog shows) to - // be visible and proceed to the SAML page. - reauth_dialog_helper->WaitForVerifyAccountScreen(); - reauth_dialog_helper->ClickVerifyButton(); - - reauth_dialog_helper->WaitForSamlScreen(); - reauth_dialog_helper->ExpectVerifyAccountScreenHidden(); - - reauth_dialog_helper->WaitForIdpPageLoad(); - return reauth_dialog_helper; - } - // Go through online authentication (with saml) flow on the lock screen. void UnlockWithSAML() { absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); // Fill-in the SAML IdP form and submit. test::JSChecker signin_frame_js = reauth_dialog_helper->SigninFrameJS(); @@ -254,7 +235,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); reauth_dialog_helper->ClickCancelButtonOnSamlScreen(); @@ -273,7 +254,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); content::DOMMessageQueue message_queue( reauth_dialog_helper->DialogWebContents()); @@ -316,7 +297,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); test::JSChecker signin_frame_js = reauth_dialog_helper->SigninFrameJS(); signin_frame_js.Evaluate( @@ -350,7 +331,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); // Fill-in the SAML IdP form and submit. test::JSChecker signin_frame_js = reauth_dialog_helper->SigninFrameJS(); @@ -387,7 +368,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); // Fill-in the SAML IdP form and submit. test::JSChecker signin_frame_js = reauth_dialog_helper->SigninFrameJS(); @@ -427,7 +408,7 @@ ScreenLockerTester().Lock(); absl::optional<LockScreenReauthDialogTestHelper> reauth_dialog_helper = - StartSamlAndWaitForIdpPageLoad(); + LockScreenReauthDialogTestHelper::StartSamlAndWaitForIdpPageLoad(); // Authenticate in the IdP with another account other than the one used in // sign in.
diff --git a/chrome/browser/ash/login/test/js_checker.h b/chrome/browser/ash/login/test/js_checker.h index 6b28cad..c72df1d 100644 --- a/chrome/browser/ash/login/test/js_checker.h +++ b/chrome/browser/ash/login/test/js_checker.h
@@ -235,6 +235,8 @@ web_contents_ = web_contents; } + content::WebContents* web_contents() { return web_contents_; } + private: void GetBoolImpl(const std::string& expression, bool* result); void GetIntImpl(const std::string& expression, int* result);
diff --git a/chrome/browser/ash/login/webview_login_browsertest.cc b/chrome/browser/ash/login/webview_login_browsertest.cc index bda313be..4425787 100644 --- a/chrome/browser/ash/login/webview_login_browsertest.cc +++ b/chrome/browser/ash/login/webview_login_browsertest.cc
@@ -956,34 +956,6 @@ EXPECT_FALSE(found_signin_frame_partition_1); } -// Tests that requesting webcam access from the login screen works correctly. -// This is needed for taking profile pictures. -IN_PROC_BROWSER_TEST_F(WebviewLoginTest, RequestCamera) { - WaitForGaiaPageLoad(); - - // Video devices should be allowed from the login screen. - content::WebContents* web_contents = GetLoginUI()->GetWebContents(); - bool getUserMediaSuccess = false; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - web_contents->GetPrimaryMainFrame(), - "navigator.getUserMedia(" - " {video: true}," - " function() { window.domAutomationController.send(true); }," - " function() { window.domAutomationController.send(false); });", - &getUserMediaSuccess)); - EXPECT_TRUE(getUserMediaSuccess); - - // Audio devices should be denied from the login screen. - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - web_contents->GetPrimaryMainFrame(), - "navigator.getUserMedia(" - " {audio: true}," - " function() { window.domAutomationController.send(true); }," - " function() { window.domAutomationController.send(false); });", - &getUserMediaSuccess)); - EXPECT_FALSE(getUserMediaSuccess); -} - enum class FrameUrlOrigin { kSameOrigin, kDifferentOrigin }; // Parametrized test fixture that configures FakeGaia to server an iframe in the
diff --git a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc index 6cfcf25..11fc4176 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc +++ b/chrome/browser/ash/system_extensions/system_extensions_browsertest.cc
@@ -6,11 +6,14 @@ #include "ash/constants/ash_switches.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/memory/weak_ptr.h" #include "base/path_service.h" +#include "base/scoped_observation.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/ash/system_extensions/system_extensions_install_manager.h" +#include "chrome/browser/ash/system_extensions/system_extensions_profile_utils.h" #include "chrome/browser/ash/system_extensions/system_extensions_provider.h" #include "chrome/browser/ash/system_extensions/system_extensions_provider_factory.h" #include "chrome/browser/profiles/profile.h" @@ -19,6 +22,8 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/navigation_entry.h" +#include "content/public/browser/service_worker_context.h" +#include "content/public/browser/storage_partition.h" #include "content/public/common/content_features.h" #include "content/public/common/page_type.h" #include "content/public/test/browser_test.h" @@ -44,48 +49,146 @@ return test_dir.Append("system_extensions").Append("basic_system_extension"); } -// Class that returns the result of the first System Extension service worker it -// sees. -class ServiceWorkerRegistrationObserver +// Wrapper around base::OneShotEvent that allows callers to signal with +// arguments. +template <typename... Args> +class OneShotEventWrapper { + public: + OneShotEventWrapper() = default; + + ~OneShotEventWrapper() = default; + + void Signal(Args... args) { + run_with_args_ = base::BindRepeating(&OneShotEventWrapper::RunWithArgs, + weak_ptr_factory_.GetWeakPtr(), + std::forward<Args>(args)...); + one_shot_event_.Signal(); + } + + void Post(const base::Location& from_here, + base::OnceCallback<void(Args...)> task) { + one_shot_event_.Post( + from_here, + base::BindOnce(&OneShotEventWrapper::RunTask, + weak_ptr_factory_.GetWeakPtr(), std::move(task))); + } + + private: + void RunTask(base::OnceCallback<void(Args...)> task) { + run_with_args_.Run(std::move(task)); + } + + void RunWithArgs(Args... args, base::OnceCallback<void(Args...)> task) { + std::move(task).Run(std::forward<Args>(args)...); + } + + base::OneShotEvent one_shot_event_; + base::RepeatingCallback<void(base::OnceCallback<void(Args...)>)> + run_with_args_; + base::WeakPtrFactory<OneShotEventWrapper> weak_ptr_factory_{this}; +}; + +// Class that can be used to wait for SystemExtensionsInstallManager events. +// If an event is triggered more than once, this class will CHECK. +class TestInstallationEventsWaiter : public SystemExtensionsInstallManager::Observer { public: - explicit ServiceWorkerRegistrationObserver(SystemExtensionsProvider& provider) + explicit TestInstallationEventsWaiter(SystemExtensionsProvider& provider) : install_manager_(provider.install_manager()) { - install_manager_.AddObserver(this); + observation_.Observe(&install_manager_); } - ~ServiceWorkerRegistrationObserver() override {} - // Returns the saved result or waits to get a result and returns it. + ~TestInstallationEventsWaiter() override = default; + + // Returns the result of a Service Worker registration. Waits if there hasn't + // been one yet. std::pair<SystemExtensionId, blink::ServiceWorkerStatusCode> - GetIdAndStatusCode() { - if (id_.has_value()) - return {id_.value(), status_code_.value()}; + WaitForServiceWorkerRegistered() { + absl::optional<SystemExtensionId> id; + absl::optional<blink::ServiceWorkerStatusCode> status_code; - run_loop_.Run(); - return {id_.value(), status_code_.value()}; + base::RunLoop run_loop; + on_service_worker_registered_.Post( + FROM_HERE, + base::BindLambdaForTesting( + [&](SystemExtensionId returned_id, + blink::ServiceWorkerStatusCode returned_status_code) { + id = returned_id; + status_code = returned_status_code; + run_loop.Quit(); + })); + run_loop.Run(); + + return {id.value(), status_code.value()}; + } + + // Returns the result of a Service Worker unregistration. Waits if there + // hasn't been one yet. + std::pair<SystemExtensionId, bool> WaitForServiceWorkerUnregistered() { + absl::optional<SystemExtensionId> id; + absl::optional<bool> succeeded; + + base::RunLoop run_loop; + on_service_worker_unregistered_.Post( + FROM_HERE, base::BindLambdaForTesting([&](SystemExtensionId returned_id, + bool returned_succeeded) { + id = returned_id; + succeeded = returned_succeeded; + run_loop.Quit(); + })); + run_loop.Run(); + + return {id.value(), succeeded.value()}; + } + + // Returns the result of a asset deletion operations. Waits if there + // hasn't been one yet. + std::pair<SystemExtensionId, bool> WaitForAssetsDeleted() { + absl::optional<SystemExtensionId> id; + absl::optional<bool> succeeded; + + base::RunLoop run_loop; + on_assets_deleted_.Post( + FROM_HERE, base::BindLambdaForTesting([&](SystemExtensionId returned_id, + bool returned_succeeded) { + id = returned_id; + succeeded = returned_succeeded; + run_loop.Quit(); + })); + run_loop.Run(); + + return {id.value(), succeeded.value()}; } // SystemExtensionsInstallManager::Observer void OnServiceWorkerRegistered( const SystemExtensionId& id, blink::ServiceWorkerStatusCode status_code) override { - install_manager_.RemoveObserver(this); + on_service_worker_registered_.Signal(id, status_code); + } - // Should happen because we unregistered as observers. - DCHECK(!id_.has_value()); + void OnServiceWorkerUnregistered(const SystemExtensionId& id, + bool succeeded) override { + on_service_worker_unregistered_.Signal(id, succeeded); + } - id_ = id; - status_code_ = status_code; - run_loop_.Quit(); + void OnSystemExtensionAssetsDeleted(const SystemExtensionId& id, + bool succeeded) override { + on_assets_deleted_.Signal(id, succeeded); } private: + OneShotEventWrapper<SystemExtensionId, blink::ServiceWorkerStatusCode> + on_service_worker_registered_; + OneShotEventWrapper<SystemExtensionId, bool> on_service_worker_unregistered_; + OneShotEventWrapper<SystemExtensionId, bool> on_assets_deleted_; + + base::ScopedObservation<SystemExtensionsInstallManager, + SystemExtensionsInstallManager::Observer> + observation_{this}; + // Should be present for the duration of the test. SystemExtensionsInstallManager& install_manager_; - - base::RunLoop run_loop_; - absl::optional<SystemExtensionId> id_; - absl::optional<blink::ServiceWorkerStatusCode> status_code_; }; class SystemExtensionsBrowserTest : public InProcessBrowserTest { @@ -155,7 +258,7 @@ auto& provider = SystemExtensionsProvider::Get(browser()->profile()); auto& install_manager = provider.install_manager(); - ServiceWorkerRegistrationObserver sw_registration_observer(provider); + TestInstallationEventsWaiter waiter(provider); base::RunLoop run_loop; install_manager.InstallUnpackedExtensionFromDir( GetBasicSystemExtensionDir(), @@ -166,13 +269,81 @@ })); run_loop.Run(); - const auto [id, status_code] = sw_registration_observer.GetIdAndStatusCode(); + const auto [id, status_code] = waiter.WaitForServiceWorkerRegistered(); EXPECT_EQ(kTestSystemExtensionId, id); EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status_code); TestInstalledTestExtensionWorks(); } +IN_PROC_BROWSER_TEST_F(SystemExtensionsBrowserTest, Uninstall_Success) { + auto& provider = SystemExtensionsProvider::Get(browser()->profile()); + auto& install_manager = provider.install_manager(); + + TestInstallationEventsWaiter waiter(provider); + + { + // Install and wait for the service worker to be registered. + base::RunLoop run_loop; + install_manager.InstallUnpackedExtensionFromDir( + GetBasicSystemExtensionDir(), + base::BindLambdaForTesting( + [&](InstallStatusOrSystemExtensionId result) { run_loop.Quit(); })); + run_loop.Run(); + waiter.WaitForServiceWorkerRegistered(); + } + + // Uninstall the extension. + install_manager.Uninstall(kTestSystemExtensionId); + + auto& registry = provider.registry(); + EXPECT_TRUE(registry.GetIds().empty()); + EXPECT_FALSE(registry.GetById(kTestSystemExtensionId)); + + // Test that navigating to the System Extension's resources fails. + auto* tab = browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), + GURL(kTestSystemExtensionIndexURL))); + content::NavigationEntry* entry = tab->GetController().GetVisibleEntry(); + EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType()); + + { + // Test that the resources have been deleted. + const auto [id, deletion_succeeded] = waiter.WaitForAssetsDeleted(); + EXPECT_EQ(id, kTestSystemExtensionId); + EXPECT_TRUE(deletion_succeeded); + base::ScopedAllowBlockingForTesting allow_blocking; + const base::FilePath system_extension_dir = GetDirectoryForSystemExtension( + *browser()->profile(), kTestSystemExtensionId); + EXPECT_FALSE(base::PathExists(system_extension_dir)); + } + + { + // Test that the service worker has been unregistered. + const auto [id, unregistration_succeeded] = + waiter.WaitForServiceWorkerUnregistered(); + EXPECT_EQ(id, kTestSystemExtensionId); + EXPECT_TRUE(unregistration_succeeded); + + const GURL scope(kTestSystemExtensionEmptyPathURL); + auto* worker_context = browser() + ->profile() + ->GetDefaultStoragePartition() + ->GetServiceWorkerContext(); + + base::RunLoop run_loop; + worker_context->CheckHasServiceWorker( + scope, blink::StorageKey(url::Origin::Create(scope)), + base::BindLambdaForTesting( + [&](content::ServiceWorkerCapability capability) { + EXPECT_EQ(capability, + content::ServiceWorkerCapability::NO_SERVICE_WORKER); + run_loop.Quit(); + })); + run_loop.Run(); + } +} + IN_PROC_BROWSER_TEST_F(SystemExtensionsSwitchBrowserTest, ExtensionInstalled) { auto& provider = SystemExtensionsProvider::Get(browser()->profile()); auto& install_manager = provider.install_manager();
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc index 41000e6e..fadb976 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc +++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
@@ -81,6 +81,7 @@ void SystemExtensionsInstallManager::StartInstallation( OnceInstallCallback final_callback, const base::FilePath& unpacked_system_extension_dir) { + // Installation Step #1: Convert a manifest into a SystemExtension object. sandboxed_unpacker_.GetSystemExtensionFromDir( unpacked_system_extension_dir, base::BindOnce( @@ -104,6 +105,8 @@ const base::FilePath system_extensions_dir = GetSystemExtensionsProfileDir(*profile_); + // Installation Step #2: Copy the System Extensions assets to a profile + // directory. io_helper_.AsyncCall(&IOHelper::CopyExtensionAssets) .WithArgs(unpacked_system_extension_dir, dest_dir, system_extensions_dir) .Then(base::BindOnce( @@ -122,12 +125,15 @@ return; } - SystemExtensionId id = system_extension.id; + // Installation Step #3: Create a WebUIConfig so that resources are served. auto config = std::make_unique<SystemExtensionsWebUIConfig>(system_extension); content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig( std::move(config)); + // Installation Step #4: Add the System Extension to the registry. + SystemExtensionId id = system_extension.id; registry_manager_->AddSystemExtension(std::move(system_extension)); + std::move(final_callback).Run(std::move(id)); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, @@ -148,15 +154,17 @@ blink::mojom::ServiceWorkerUpdateViaCache::kImports); blink::StorageKey key(url::Origin::Create(options.scope)); + // Installation Step #5: Register a Service Worker for the System Extension. auto* worker_context = profile_->GetDefaultStoragePartition()->GetServiceWorkerContext(); worker_context->RegisterServiceWorker( system_extension->service_worker_url, key, options, - base::BindOnce(&SystemExtensionsInstallManager::OnRegisterServiceWorker, - weak_ptr_factory_.GetWeakPtr(), system_extension_id)); + base::BindOnce( + &SystemExtensionsInstallManager::NotifyServiceWorkerRegistered, + weak_ptr_factory_.GetWeakPtr(), system_extension_id)); } -void SystemExtensionsInstallManager::OnRegisterServiceWorker( +void SystemExtensionsInstallManager::NotifyServiceWorkerRegistered( const SystemExtensionId& system_extension_id, blink::ServiceWorkerStatusCode status_code) { if (status_code != blink::ServiceWorkerStatusCode::kOk) { @@ -169,6 +177,64 @@ observer.OnServiceWorkerRegistered(system_extension_id, status_code); } +void SystemExtensionsInstallManager::Uninstall( + const SystemExtensionId& system_extension_id) { + auto* system_extension = registry_->GetById(system_extension_id); + if (!system_extension) { + return; + } + + const GURL& scope = system_extension->base_url; + const url::Origin& origin = url::Origin::Create(system_extension->base_url); + + // The un-installation steps are in reverse order of the installation steps. + + // Uninstallation Step #1: Unregister the Service Worker. + auto* worker_context = + profile_->GetDefaultStoragePartition()->GetServiceWorkerContext(); + blink::StorageKey key(origin); + worker_context->UnregisterServiceWorker( + scope, key, + base::BindOnce( + &SystemExtensionsInstallManager::NotifyServiceWorkerUnregistered, + weak_ptr_factory_.GetWeakPtr(), system_extension_id)); + + // Uninstallation Step #2: Remove the WebUIConfig for the System Extension. + content::WebUIConfigMap::GetInstance().RemoveConfig(origin); + + // Uninstallation Step #3: Remove System Extension from the registry. + registry_manager_->RemoveSystemExtension(system_extension_id); + + // Uninstallation Step #4: Delete the System Extension assets. + io_helper_.AsyncCall(&IOHelper::RemoveExtensionAssets) + .WithArgs(GetDirectoryForSystemExtension(*profile_, system_extension_id)) + .Then(base::BindOnce(&SystemExtensionsInstallManager::NotifyAssetsRemoved, + weak_ptr_factory_.GetWeakPtr(), + system_extension_id)); +} + +void SystemExtensionsInstallManager::NotifyServiceWorkerUnregistered( + const SystemExtensionId& system_extension_id, + bool succeeded) { + // TODO(b/238578914): Consider changing UnregisterServiceWorker to pass a + // ServiceWorkerStatusCode instead of a bool. + if (!succeeded) + LOG(ERROR) << "Failed to unregister Service Worker."; + + for (auto& observer : observers_) + observer.OnServiceWorkerUnregistered(system_extension_id, succeeded); +} + +void SystemExtensionsInstallManager::NotifyAssetsRemoved( + const SystemExtensionId& system_extension_id, + bool succeeded) { + if (!succeeded) + LOG(ERROR) << "Failed to remove System Extension assets."; + + for (auto& observer : observers_) + observer.OnSystemExtensionAssetsDeleted(system_extension_id, succeeded); +} + bool SystemExtensionsInstallManager::IOHelper::CopyExtensionAssets( const base::FilePath& unpacked_extension_dir, const base::FilePath& dest_dir, @@ -196,11 +262,20 @@ // `/{profile_path}/System Extensions/{system_extension_id}/` if (!base::CopyDirectory(unpacked_extension_dir, dest_dir, /*recursive=*/true)) { - return false; LOG(ERROR) << "Failed to copy System Extension assets."; + return false; } return true; } +bool SystemExtensionsInstallManager::IOHelper::RemoveExtensionAssets( + const base::FilePath& system_extension_dir) { + if (!base::DeletePathRecursively(system_extension_dir)) { + LOG(ERROR) << "Failed to delete System Extension assets."; + return false; + } + return true; +} + } // namespace ash
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h index 7678282c..fb12687 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h +++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
@@ -27,11 +27,25 @@ class SystemExtensionsInstallManager { public: + // Observer for installation and uninstallation steps events. This should be + // used for classes that need to perform an action in response to an + // installation step. + // TODO(b/241308071): Once it's implemented, suggest using + // SystemExtensionsRegistry::Observer for clients that only care about when + // a System Extension is installed or uninstalled. class Observer : public base::CheckedObserver { public: virtual void OnServiceWorkerRegistered( const SystemExtensionId& system_extension_id, blink::ServiceWorkerStatusCode status_code) {} + + virtual void OnServiceWorkerUnregistered( + const SystemExtensionId& system_extension_id, + bool succeeded) {} + + virtual void OnSystemExtensionAssetsDeleted( + const SystemExtensionId& system_extension_id, + bool succeeded) {} }; SystemExtensionsInstallManager( @@ -44,6 +58,12 @@ const SystemExtensionsInstallManager&) = delete; ~SystemExtensionsInstallManager(); + void AddObserver(Observer* observer) { observers_.AddObserver(observer); } + + void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } + using OnceInstallCallback = base::OnceCallback<void(InstallStatusOrSystemExtensionId)>; void InstallUnpackedExtensionFromDir( @@ -54,11 +74,15 @@ return on_command_line_install_finished_; } - void AddObserver(Observer* observer) { observers_.AddObserver(observer); } - - void RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); - } + // Uninstallation always succeeds. + // + // Currently only two operations can fail, unregistering the Service Worker, + // and deleting the System Extension's assets. Regardless of these operations + // failing, the System Extension will be considered uninstalled. Both of these + // failure cases will be handled by a garbage collector outside of this class. + // TODO(b/241198799): Change this comment once the garbage collector is + // implemented. + void Uninstall(const SystemExtensionId& system_extension_id); private: // Helper class to run blocking IO operations on a separate thread. @@ -67,6 +91,7 @@ bool CopyExtensionAssets(const base::FilePath& unpacked_extension_dir, const base::FilePath& dest_dir, const base::FilePath& system_extensions_dir); + bool RemoveExtensionAssets(const base::FilePath& system_extension_dir); }; void InstallFromCommandLineIfNecessary(); @@ -83,13 +108,18 @@ SystemExtension system_extension, bool did_succeed); void RegisterServiceWorker(const SystemExtensionId& id); - void OnRegisterServiceWorker(const SystemExtensionId& id, - blink::ServiceWorkerStatusCode status_code); void DispatchWindowManagerStartEvent(const SystemExtensionId& id, int64_t version_id, int process_id, int thread_id); + void NotifyServiceWorkerRegistered( + const SystemExtensionId& id, + blink::ServiceWorkerStatusCode status_code); + void NotifyServiceWorkerUnregistered(const SystemExtensionId& id, + bool succeeded); + void NotifyAssetsRemoved(const SystemExtensionId&, bool succeeded); + // Safe because this class is owned by SystemExtensionsProvider which is owned // by the profile. raw_ptr<Profile> profile_;
diff --git a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc index 7b39756..81a712a 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc +++ b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h" +#include "base/callback_helpers.h" #include "base/debug/stack_trace.h" #include "base/system/sys_info.h" #include "chrome/browser/ash/system_extensions/system_extensions_profile_utils.h" @@ -51,6 +52,28 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } +void SystemExtensionsInternalsPageHandler::IsSystemExtensionInstalled( + IsSystemExtensionInstalledCallback callback) { + auto& registry = SystemExtensionsProvider::Get(profile_).registry(); + + const bool is_installed = !registry.GetIds().empty(); + std::move(callback).Run(is_installed); +} + +void SystemExtensionsInternalsPageHandler::UninstallSystemExtension( + UninstallSystemExtensionCallback callback) { + auto scoped_callback_runner = base::ScopedClosureRunner(std::move(callback)); + + auto& provider = SystemExtensionsProvider::Get(profile_); + auto& registry = provider.registry(); + const auto ids = registry.GetIds(); + + if (ids.empty()) + return; + + provider.install_manager().Uninstall(ids[0]); +} + void SystemExtensionsInternalsPageHandler::OnInstallFinished( InstallSystemExtensionFromDownloadsDirCallback callback, InstallStatusOrSystemExtensionId result) {
diff --git a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h index e087abf..c71a526 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h +++ b/chrome/browser/ash/system_extensions/system_extensions_internals_page_handler.h
@@ -31,6 +31,10 @@ void InstallSystemExtensionFromDownloadsDir( const base::SafeBaseName& system_extension_dir_name, InstallSystemExtensionFromDownloadsDirCallback callback) override; + void IsSystemExtensionInstalled( + IsSystemExtensionInstalledCallback callback) override; + void UninstallSystemExtension( + UninstallSystemExtensionCallback callback) override; private: void OnInstallFinished(
diff --git a/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.cc b/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.cc index 4f284e6..d3d05136 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.cc +++ b/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.cc
@@ -20,6 +20,11 @@ system_extensions_[id] = std::move(system_extension); } +void SystemExtensionsMutableRegistry::RemoveSystemExtension( + const SystemExtensionId& system_extension_id) { + system_extensions_.erase(system_extension_id); +} + std::vector<SystemExtensionId> SystemExtensionsMutableRegistry::GetIds() { std::vector<SystemExtensionId> extension_ids; for (const auto& [id, system_extension] : system_extensions_) {
diff --git a/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.h b/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.h index 87c00dc..b476ba9a 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.h +++ b/chrome/browser/ash/system_extensions/system_extensions_mutable_registry.h
@@ -29,6 +29,10 @@ // Adds |system_extension| to the map of installed System Extensions. void AddSystemExtension(SystemExtension system_extension); + // Removes the System Extension with `system_extension_id` from the + // map of installed System Extensions. + void RemoveSystemExtension(const SystemExtensionId& system_extension_id); + // SystemExtensionsRegistry std::vector<SystemExtensionId> GetIds() override; const SystemExtension* GetById(
diff --git a/chrome/browser/ash/system_extensions/system_extensions_registry_manager.cc b/chrome/browser/ash/system_extensions/system_extensions_registry_manager.cc index 26d7e434b..c6daa5f9 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_registry_manager.cc +++ b/chrome/browser/ash/system_extensions/system_extensions_registry_manager.cc
@@ -17,4 +17,9 @@ registry_.AddSystemExtension(std::move(system_extension)); } +void SystemExtensionsRegistryManager::RemoveSystemExtension( + const SystemExtensionId& system_extension_id) { + registry_.RemoveSystemExtension(system_extension_id); +} + } // namespace ash
diff --git a/chrome/browser/ash/system_extensions/system_extensions_registry_manager.h b/chrome/browser/ash/system_extensions/system_extensions_registry_manager.h index 4dbadf29..ddd84aa 100644 --- a/chrome/browser/ash/system_extensions/system_extensions_registry_manager.h +++ b/chrome/browser/ash/system_extensions/system_extensions_registry_manager.h
@@ -25,11 +25,15 @@ const SystemExtensionsRegistryManager&) = delete; ~SystemExtensionsRegistryManager(); + // Returns the registry this class uses to store SystemExtension instances. + SystemExtensionsRegistry& registry() { return registry_; } + // Adds the `system_extension` to the SystemExtensionRegistry. void AddSystemExtension(SystemExtension system_extension); - // Returns the registry this class uses to store SystemExtension instances. - SystemExtensionsRegistry& registry() { return registry_; } + // Removes the System Extension with `system_extension_id` from the registry + // if it exists. + void RemoveSystemExtension(const SystemExtensionId& system_extension_id); private: SystemExtensionsMutableRegistry registry_;
diff --git a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h index a0e6464..aba84108 100644 --- a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h +++ b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h
@@ -13,7 +13,7 @@ #include "chrome/browser/web_applications/test/fake_web_app_provider.h" #include "chrome/browser/web_applications/test/profile_test_helper.h" #include "chrome/browser/web_applications/test/web_app_test_utils.h" -#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/mixin_based_in_process_browser_test.h" class KeyedService; @@ -30,7 +30,7 @@ enum class SystemWebAppType; class SystemWebAppManager; -class SystemWebAppBrowserTestBase : public InProcessBrowserTest { +class SystemWebAppBrowserTestBase : public MixinBasedInProcessBrowserTest { public: // Performs common initialization for testing SystemWebAppManager features. // If true, |install_mock| installs a WebUIController that serves a mock
diff --git a/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc index be22d220..eaa749a 100644 --- a/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc +++ b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
@@ -45,7 +45,7 @@ void SetUpOnMainThread() override { base::ScopedAllowBlockingForTesting allow_blocking; ASSERT_TRUE(component_dir_.CreateUniqueTempDir()); - content::WebUIConfigMap::GetInstance().RemoveForTesting( + content::WebUIConfigMap::GetInstance().RemoveConfig( url::Origin::Create(GURL(ash::kChromeUntrustedUIDemoModeAppURL))); content::WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig( std::make_unique<ash::DemoModeAppUntrustedUIConfig>(
diff --git a/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.cc b/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.cc index 8f0a5984..2c7177c 100644 --- a/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.cc +++ b/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.cc
@@ -16,6 +16,8 @@ #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/ash/file_manager/fileapi_util.h" #include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/ash/hats/hats_config.h" +#include "chrome/browser/ash/hats/hats_notification_controller.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" @@ -66,6 +68,17 @@ } } +void ChromeMediaAppUIDelegate::MaybeTriggerPdfHats() { + Profile* profile = Profile::FromWebUI(web_ui_); + const base::flat_map<std::string, std::string> product_specific_data; + + if (ash::HatsNotificationController::ShouldShowSurveyToProfile( + profile, ash::kHatsMediaAppPdfSurvey)) { + hats_notification_controller_ = new ash::HatsNotificationController( + profile, ash::kHatsMediaAppPdfSurvey, product_specific_data); + } +} + void ChromeMediaAppUIDelegate::IsFileArcWritable( mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken> token, base::OnceCallback<void(bool)> is_file_arc_writable_callback) {
diff --git a/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h b/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h index 6dada05..3814fe9 100644 --- a/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h +++ b/chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h
@@ -7,12 +7,17 @@ #include "ash/webui/media_app_ui/media_app_ui_delegate.h" #include "base/callback.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "storage/browser/file_system/file_system_url.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/mojom/file_system_access/file_system_access_transfer_token.mojom.h" +namespace ash { +class HatsNotificationController; +} + namespace content { class WebUI; } @@ -32,6 +37,7 @@ // MediaAppUIDelegate: absl::optional<std::string> OpenFeedbackDialog() override; void ToggleBrowserFullscreenMode() override; + void MaybeTriggerPdfHats() override; void IsFileArcWritable( mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken> token, base::OnceCallback<void(bool)> is_file_arc_writable_callback) override; @@ -49,6 +55,9 @@ absl::optional<storage::FileSystemURL> url); content::WebUI* web_ui_; // Owns |this|. + + scoped_refptr<ash::HatsNotificationController> hats_notification_controller_; + base::WeakPtrFactory<ChromeMediaAppUIDelegate> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc index dd50509b3..2d9fb22 100644 --- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc +++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -5,6 +5,7 @@ #include <utility> #include "ash/constants/ash_features.h" +#include "ash/constants/ash_switches.h" #include "ash/webui/media_app_ui/buildflags.h" #include "ash/webui/media_app_ui/test/media_app_ui_browsertest.h" #include "ash/webui/media_app_ui/url_constants.h" @@ -27,11 +28,13 @@ #include "chrome/browser/ash/file_manager/app_service_file_tasks.h" #include "chrome/browser/ash/file_manager/file_manager_test_util.h" #include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/ash/login/test/network_portal_detector_mixin.h" #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h" #include "chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.h" #include "chrome/browser/ash/web_applications/media_app/media_web_app_info.h" #include "chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h" #include "chrome/browser/extensions/component_loader.h" +#include "chrome/browser/notifications/notification_display_service.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" @@ -58,6 +61,7 @@ #include "ui/aura/window.h" #include "ui/aura/window_observer.h" #include "ui/gfx/color_palette.h" +#include "ui/message_center/public/cpp/notification.h" using ash::SystemWebAppType; using platform_util::OpenOperationResult; @@ -130,6 +134,11 @@ // Use a fake audio stream. Some tests make noise otherwise, and could fight // with parallel tests for access to the audio device. command_line->AppendSwitch(switches::kDisableAudioOutput); + + // Enable HaTS testing. + command_line->AppendSwitchASCII( + ash::switches::kForceHappinessTrackingSystem, + features::kHappinessTrackingMediaAppPdf.name); } void MediaAppLaunchWithFile(); @@ -142,6 +151,9 @@ // Helper to initiate a test by launching with no files (zero state). content::WebContents* LaunchWithNoFiles(); + protected: + ash::NetworkPortalDetectorMixin network_portal_detector_{&mixin_host_}; + private: base::test::ScopedFeatureList feature_list_; std::unique_ptr<file_manager::test::FolderInMyFiles> launch_folder_; @@ -1715,6 +1727,57 @@ EXPECT_FALSE(app_browser->window()->IsFullscreen()); } +// Tests that invoking the maybeTriggerPdfHats() MediaApp delegate method fires +// the notification that asks the user whether to complete a HaTS survey. +// Note kForceHappinessTrackingSystem is set in the test fixture to ignore the +// "dice roll" that would normally only show the prompt by chance. +IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, MaybeTriggerPdfHats) { + content::WebContents* web_ui = LaunchWithOneTestFile(kFilePdfTall); + + constexpr char kMaybeTriggerPdfHats[] = R"( + (async function triggerPdfHats() { + await customLaunchData.delegate.maybeTriggerPdfHats(); + domAutomationController.send("success"); + })(); + )"; + + struct NotificationWatcher : public NotificationDisplayService::Observer { + base::RunLoop run_loop; + std::string seen_notification_id; + + void OnNotificationDisplayed( + const message_center::Notification& notification, + const NotificationCommon::Metadata* const metadata) override { + seen_notification_id = notification.id(); + if (run_loop.IsRunningOnCurrentThread()) { + run_loop.Quit(); + } + } + + void OnNotificationClosed(const std::string& notification_id) override {} + void OnNotificationDisplayServiceDestroyed( + NotificationDisplayService* service) override {} + } notification_watcher; + + auto* notification_display_service = + NotificationDisplayService::GetForProfile(profile()); + notification_display_service->AddObserver(¬ification_watcher); + + // Notifications only fire if the device is "online". Simulate that. + network_portal_detector_.SimulateDefaultNetworkState( + ash::NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE); + + EXPECT_EQ("success", + ExtractStringInGlobalScope(web_ui, kMaybeTriggerPdfHats)); + + if (notification_watcher.seen_notification_id.empty()) { + notification_watcher.run_loop.Run(); + } + notification_display_service->RemoveObserver(¬ification_watcher); + + EXPECT_EQ(notification_watcher.seen_notification_id, "hats_notification"); +} + IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, GuestCanReadLocalFonts) { // For this test, we first need to find a valid font to request from // /usr/share/fonts. build/linux/install-chromeos-fonts.py installs some known
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc index 4dd21ac..6334481 100644 --- a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc +++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
@@ -7,11 +7,17 @@ #include "ash/constants/ash_features.h" #include "ash/webui/grit/ash_projector_app_trusted_resources.h" #include "ash/webui/projector_app/public/cpp/projector_app_constants.h" +#include "chrome/browser/apps/app_service/app_launch_params.h" +#include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h" #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h" #include "chrome/browser/ui/ash/projector/projector_utils.h" +#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/user_display_mode.h" #include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/grit/generated_resources.h" +#include "content/public/browser/web_contents.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/l10n/l10n_util.h" #include "ui/chromeos/styles/cros_styles.h" @@ -86,3 +92,30 @@ bool ProjectorSystemWebAppDelegate::IsAppEnabled() const { return IsProjectorAppEnabled(profile_); } + +Browser* ProjectorSystemWebAppDelegate::LaunchAndNavigateSystemWebApp( + Profile* profile, + web_app::WebAppProvider* provider, + const GURL& url, + const apps::AppLaunchParams& params) const { + Browser* browser = + ash::FindSystemWebAppBrowser(profile, ash::SystemWebAppType::PROJECTOR); + // If the Projector app is not already open or we're not launching with files + // then proceed as usual. + if (!browser || params.launch_files.empty()) { + return SystemWebAppDelegate::LaunchAndNavigateSystemWebApp( + profile, provider, url, params); + } + // Otherwise, the app is already open and we're launching files. Suppose the + // user clicks on a share link for a transcoding screencast. The app's URL + // would be set to chrome://projector/app/screencastId. However, calling + // LaunchSystemWebAppAsync() always launches the app at the default start url + // of chrome://projector/app/. The launch event would reload the app back to + // the gallery view! To prevent this bug, we must match the app's current url + // to avoid a visible app reload. In general, launch events should be + // invisible to the user. + content::WebContents* web_contents = + browser->tab_strip_model()->GetActiveWebContents(); + return SystemWebAppDelegate::LaunchAndNavigateSystemWebApp( + profile, provider, web_contents->GetURL(), params); +}
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.h b/chrome/browser/ash/web_applications/projector_system_web_app_info.h index efb6f8c..73b0340 100644 --- a/chrome/browser/ash/web_applications/projector_system_web_app_info.h +++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.h
@@ -22,6 +22,11 @@ bool ShouldCaptureNavigations() const override; gfx::Size GetMinimumWindowSize() const override; bool IsAppEnabled() const override; + Browser* LaunchAndNavigateSystemWebApp( + Profile* profile, + web_app::WebAppProvider* provider, + const GURL& url, + const apps::AppLaunchParams& params) const override; }; #endif // CHROME_BROWSER_ASH_WEB_APPLICATIONS_PROJECTOR_SYSTEM_WEB_APP_INFO_H_
diff --git a/chrome/browser/ash/web_applications/terminal_source.cc b/chrome/browser/ash/web_applications/terminal_source.cc index 9f7aa34..2c3ae2f6 100644 --- a/chrome/browser/ash/web_applications/terminal_source.cc +++ b/chrome/browser/ash/web_applications/terminal_source.cc
@@ -212,6 +212,10 @@ return "frame-src 'self';"; case network::mojom::CSPDirectiveName::ObjectSrc: return "object-src 'self';"; + case network::mojom::CSPDirectiveName::ScriptSrc: + return "script-src 'self' 'wasm-unsafe-eval';"; + case network::mojom::CSPDirectiveName::WorkerSrc: + return "worker-src 'self';"; default: break; }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 9b1f52f..c43d1b2fb 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -2077,6 +2077,8 @@ "app_mode/chrome_kiosk_external_loader_broker.h", "app_mode/kiosk_app_external_loader.cc", "app_mode/kiosk_app_external_loader.h", + "app_mode/kiosk_app_service_launcher.cc", + "app_mode/kiosk_app_service_launcher.h", "app_mode/kiosk_settings_navigation_throttle.cc", "app_mode/kiosk_settings_navigation_throttle.h", "app_mode/startup_app_launcher_update_checker.cc", @@ -3471,6 +3473,7 @@ "../ui/browser_finder_chromeos_unittest.cc", "app_mode/app_session_unittest.cc", "app_mode/chrome_kiosk_app_launcher_unittest.cc", + "app_mode/kiosk_app_service_launcher_unittest.cc", "extensions/active_tab_permission_granter_delegate_chromeos_unittest.cc", "extensions/default_app_order_unittest.cc", "extensions/device_local_account_external_policy_loader_unittest.cc",
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc index 6526eb4..9d4d326 100644 --- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
@@ -4,13 +4,18 @@ #include "chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.h" +#include "base/bind.h" +#include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/syslog_logging.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/apps/app_service/app_launch_params.h" +#include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/common/chrome_features.h" #include "components/services/app_service/public/cpp/app_launch_util.h" #include "extensions/browser/app_window/app_window.h" #include "extensions/browser/app_window/app_window_registry.h" @@ -91,12 +96,20 @@ SYSLOG(INFO) << "Attempt to launch app."; - // Always open the app in a window. - ::OpenApplication( - profile_, - apps::AppLaunchParams( - extension->id(), apps::LaunchContainer::kLaunchContainerWindow, - WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromKiosk)); + if (base::FeatureList::IsEnabled(features::kKioskEnableAppService)) { + app_service_launcher_ = std::make_unique<KioskAppServiceLauncher>(profile_); + app_service_launcher_->CheckAndMaybeLaunchApp( + extension->id(), + base::BindOnce(&ChromeKioskAppLauncher::OnAppServiceAppLaunched, + weak_ptr_factory_.GetWeakPtr())); + } else { + // Always open the app in a window. + ::OpenApplication( + profile_, + apps::AppLaunchParams( + extension->id(), apps::LaunchContainer::kLaunchContainerWindow, + WindowOpenDisposition::NEW_WINDOW, apps::LaunchSource::kFromKiosk)); + } WaitForAppWindow(); } @@ -119,6 +132,12 @@ } } +void ChromeKioskAppLauncher::OnAppServiceAppLaunched(bool success) { + if (!success) { + ReportLaunchFailure(LaunchResult::kUnableToLaunch); + } +} + void ChromeKioskAppLauncher::MaybeUpdateAppData() { // Skip copying meta data from the current installed primary app when // there is a pending update.
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.h b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.h index 776e431..f3624c2 100644 --- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.h +++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.h
@@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h" #include "chrome/browser/profiles/profile.h" #include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom.h" #include "extensions/browser/app_window/app_window.h" @@ -35,6 +36,9 @@ // AppWindowRegistry::Observer: void OnAppWindowAdded(extensions::AppWindow* app_window) override; + // |KioskAppServiceLauncher| callback. + void OnAppServiceAppLaunched(bool success); + void WaitForAppWindow(); void ReportLaunchSuccess(); @@ -60,7 +64,11 @@ extensions::AppWindowRegistry::Observer> app_window_observation_{this}; + std::unique_ptr<KioskAppServiceLauncher> app_service_launcher_; + LaunchCallback on_ready_callback_; + + base::WeakPtrFactory<ChromeKioskAppLauncher> weak_ptr_factory_{this}; }; } // namespace ash
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc index f974819..21d1853 100644 --- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc +++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher_unittest.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service_test_base.h" #include "chrome/browser/ui/apps/chrome_app_delegate.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/app_window/app_window.h" @@ -272,4 +273,29 @@ EXPECT_TRUE(registry()->disabled_extensions().Contains(kExtraSecondaryAppId)); } +TEST_F(ChromeKioskAppLauncherTest, ShouldSucceedWithAppService) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kKioskEnableAppService); + + TestKioskExtensionBuilder primary_app_builder(Manifest::TYPE_PLATFORM_APP, + kTestPrimaryAppId); + primary_app_builder.set_version("1.0"); + scoped_refptr<const extensions::Extension> primary_app = + primary_app_builder.Build(); + service()->AddExtension(primary_app.get()); + + CreateLauncher(/*is_network_ready=*/true); + + TestFuture<LaunchResult> future; + launcher_->LaunchApp(future.GetCallback()); + + SimulateAppWindowLaunch(primary_app.get()); + + ASSERT_THAT(future.Get(), Eq(LaunchResult::kSuccess)); + + EXPECT_THAT(app_launch_tracker_->launched_apps(), + ElementsAre(kTestPrimaryAppId)); + EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); +} + } // namespace ash
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.cc b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.cc new file mode 100644 index 0000000..acbfd26 --- /dev/null +++ b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.cc
@@ -0,0 +1,100 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h" + +#include <utility> + +#include "base/bind.h" +#include "base/check.h" +#include "base/notreached.h" +#include "base/stl_util.h" +#include "base/syslog_logging.h" +#include "chrome/browser/apps/app_service/app_launch_params.h" +#include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/apps/app_service/launch_result_type.h" +#include "components/services/app_service/public/cpp/app_types.h" +#include "components/services/app_service/public/cpp/app_update.h" + +namespace ash { + +KioskAppServiceLauncher::KioskAppServiceLauncher(Profile* profile) { + app_service_ = apps::AppServiceProxyFactory::GetForProfile(profile); + DCHECK(app_service_); +} + +KioskAppServiceLauncher::~KioskAppServiceLauncher() = default; + +void KioskAppServiceLauncher::CheckAndMaybeLaunchApp( + const std::string& app_id, + AppLaunchedCallback app_launched_callback) { + DCHECK(!app_launched_callback_) + << "CheckAndMaybeLaunchApp() should only be called once."; + app_launched_callback_ = std::move(app_launched_callback); + + app_id_ = app_id; + + apps::Readiness readiness = apps::Readiness::kUnknown; + app_service_->AppRegistryCache().ForOneApp( + app_id_, + [&readiness](apps::AppUpdate update) { readiness = update.Readiness(); }); + + switch (readiness) { + case apps::Readiness::kUnknown: + case apps::Readiness::kTerminated: + SYSLOG(WARNING) << "Kiosk app not ready yet: " + << base::to_underlying(readiness); + app_registry_observation_.Observe(&app_service_->AppRegistryCache()); + break; + case apps::Readiness::kReady: + LaunchAppInternal(); + break; + case apps::Readiness::kDisabledByBlocklist: + case apps::Readiness::kDisabledByPolicy: + case apps::Readiness::kDisabledByUser: + case apps::Readiness::kUninstalledByUser: + case apps::Readiness::kRemoved: + case apps::Readiness::kUninstalledByMigration: + SYSLOG(ERROR) << "Kiosk app should not have readiness " + << base::to_underlying(readiness); + if (app_launched_callback_.has_value()) { + std::move(app_launched_callback_.value()).Run(false); + } + } +} + +void KioskAppServiceLauncher::OnAppUpdate(const apps::AppUpdate& update) { + if (update.AppId() != app_id_ || + update.Readiness() != apps::Readiness::kReady) { + return; + } + app_registry_observation_.Reset(); + LaunchAppInternal(); +} + +void KioskAppServiceLauncher::OnAppRegistryCacheWillBeDestroyed( + apps::AppRegistryCache* cache) { + app_registry_observation_.Reset(); +} + +void KioskAppServiceLauncher::LaunchAppInternal() { + SYSLOG(INFO) << "Kiosk app is ready to launch with App Service"; + app_service_->LaunchAppWithParams( + apps::AppLaunchParams( + app_id_, apps::LaunchContainer::kLaunchContainerWindow, + WindowOpenDisposition::NEW_POPUP, apps::LaunchSource::kFromKiosk), + base::BindOnce(&KioskAppServiceLauncher::OnAppLaunched, + weak_ptr_factory_.GetWeakPtr())); +} + +void KioskAppServiceLauncher::OnAppLaunched(apps::LaunchResult&& result) { + // App window is not active at this moment. We need to close splash screen + // after app window is activated which will be handled in subclasses. + if (app_launched_callback_.has_value()) { + std::move(app_launched_callback_.value()).Run(true); + } +} + +} // namespace ash
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h new file mode 100644 index 0000000..53207f2 --- /dev/null +++ b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h
@@ -0,0 +1,72 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_SERVICE_LAUNCHER_H_ +#define CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_SERVICE_LAUNCHER_H_ + +#include <string> +#include "base/callback_forward.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" +#include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/launch_result_type.h" +#include "chrome/browser/profiles/profile.h" +#include "components/services/app_service/public/cpp/app_registry_cache.h" +#include "components/services/app_service/public/cpp/app_update.h" + +namespace ash { + +// This class launches a Kiosk app with the following steps: +// 1. Checks if the app is ready to be launched. If not then observes the +// registry cache until the app is ready. +// 2. Starts the app using |AppServiceProxy::LaunchAppWithParams()| interface +// and waits for the launch to complete. +// It does not wait for app window to become active, which should be handled +// in the caller of this class. +class KioskAppServiceLauncher : public apps::AppRegistryCache::Observer { + public: + // Callback when the app is launched by App Service. App window instance is + // not active at this point. If called with false then the app launch has + // failed. Corresponds to |KioskLaunchController::OnAppLaunched()|. + using AppLaunchedCallback = base::OnceCallback<void(bool)>; + + explicit KioskAppServiceLauncher(Profile* profile); + KioskAppServiceLauncher(const KioskAppServiceLauncher&) = delete; + KioskAppServiceLauncher& operator=(const KioskAppServiceLauncher&) = delete; + ~KioskAppServiceLauncher() override; + + // Checks if the Kiosk app is ready to be launched by App Service. If it's + // ready then launches the app immediately. Otherwise waits for it to be ready + // and launches the app later. Should only be called once per Kiosk launch. + void CheckAndMaybeLaunchApp(const std::string& app_id, + AppLaunchedCallback app_launched_callback); + + private: + void LaunchAppInternal(); + + void OnAppLaunched(apps::LaunchResult&& result); + + // apps::AppRegistryCache::Observer: + void OnAppUpdate(const apps::AppUpdate& update) override; + void OnAppRegistryCacheWillBeDestroyed( + apps::AppRegistryCache* cache) override; + + std::string app_id_; + + // A keyed service. Not owned by this class. + raw_ptr<apps::AppServiceProxy> app_service_; + + absl::optional<AppLaunchedCallback> app_launched_callback_; + + base::ScopedObservation<apps::AppRegistryCache, + apps::AppRegistryCache::Observer> + app_registry_observation_{this}; + + base::WeakPtrFactory<KioskAppServiceLauncher> weak_ptr_factory_{this}; +}; + +} // namespace ash + +#endif // CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_APP_SERVICE_LAUNCHER_H_
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher_unittest.cc b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher_unittest.cc new file mode 100644 index 0000000..7acbf7a --- /dev/null +++ b/chrome/browser/chromeos/app_mode/kiosk_app_service_launcher_unittest.cc
@@ -0,0 +1,145 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "ash/constants/app_types.h" +#include "base/test/mock_callback.h" +#include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/apps/app_service/app_service_test.h" +#include "chrome/browser/apps/app_service/publishers/app_publisher.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "components/services/app_service/public/cpp/app_types.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace ash { + +namespace { + +using ::testing::_; + +constexpr apps::AppType kTestAppType = apps::AppType::kChromeApp; +constexpr char kTestAppId[] = "abcdefghabcdefghabcdefghabcdefgh"; + +class FakePublisher final : public apps::AppPublisher { + public: + FakePublisher(apps::AppServiceProxy* proxy, apps::AppType app_type) + : AppPublisher(proxy) { + RegisterPublisher(app_type); + } + + MOCK_METHOD4(Launch, + void(const std::string& app_id, + int32_t event_flags, + apps::LaunchSource launch_source, + apps::WindowInfoPtr window_info)); + + void LaunchAppWithParams(apps::AppLaunchParams&& params, + apps::LaunchCallback callback) override { + if (params.app_id == kTestAppId && + params.launch_source == apps::LaunchSource::kFromKiosk) { + std::move(callback).Run(apps::LaunchResult()); + } + } + + MOCK_METHOD6(LoadIcon, + void(const std::string& app_id, + const apps::IconKey& icon_key, + apps::IconType icon_type, + int32_t size_hint_in_dip, + bool allow_placeholder_icon, + apps::LoadIconCallback callback)); +}; + +} // namespace + +class KioskAppServiceLauncherTest : public BrowserWithTestWindowTest { + public: + KioskAppServiceLauncherTest() = default; + + // testing::Test: + void SetUp() override { + BrowserWithTestWindowTest::SetUp(); + app_service_test_.UninstallAllApps(profile()); + app_service_test_.SetUp(profile()); + app_service_test_.WaitForAppService(); + app_service_ = apps::AppServiceProxyFactory::GetForProfile(profile()); + publisher_ = std::make_unique<FakePublisher>(app_service_, kTestAppType); + launcher_ = std::make_unique<KioskAppServiceLauncher>(profile()); + } + + void TearDown() override { + publisher_.reset(); + launcher_.reset(); + BrowserWithTestWindowTest::TearDown(); + } + + protected: + void UpdateAppReadiness(apps::Readiness readiness) { + std::vector<apps::AppPtr> apps; + auto app = std::make_unique<apps::App>(kTestAppType, kTestAppId); + app->app_id = kTestAppId; + app->app_type = kTestAppType; + app->readiness = readiness; + apps.push_back(std::move(app)); + app_service_->AppRegistryCache().OnApps( + std::move(apps), kTestAppType, false /* should_notify_initialized */); + } + + apps::AppServiceTest app_service_test_; + apps::AppServiceProxy* app_service_ = nullptr; + + std::unique_ptr<FakePublisher> publisher_; + std::unique_ptr<KioskAppServiceLauncher> launcher_; +}; + +TEST_F(KioskAppServiceLauncherTest, ShouldFailIfAppInInvalidReadiness) { + base::MockCallback<KioskAppServiceLauncher::AppLaunchedCallback> + launched_callback; + + UpdateAppReadiness(apps::Readiness::kUninstalledByUser); + EXPECT_CALL(launched_callback, Run(false)).Times(1); + launcher_->CheckAndMaybeLaunchApp(kTestAppId, launched_callback.Get()); +} + +TEST_F(KioskAppServiceLauncherTest, ShouldWaitIfAppNotExist) { + base::MockCallback<KioskAppServiceLauncher::AppLaunchedCallback> + launched_callback; + + EXPECT_CALL(launched_callback, Run(_)).Times(0); + launcher_->CheckAndMaybeLaunchApp(kTestAppId, launched_callback.Get()); + testing::Mock::VerifyAndClearExpectations(&launched_callback); + + EXPECT_CALL(launched_callback, Run(true)).Times(1); + UpdateAppReadiness(apps::Readiness::kReady); +} + +TEST_F(KioskAppServiceLauncherTest, ShouldWaitIfAppNotReady) { + base::MockCallback<KioskAppServiceLauncher::AppLaunchedCallback> + launched_callback; + + UpdateAppReadiness(apps::Readiness::kUnknown); + EXPECT_CALL(launched_callback, Run(_)).Times(0); + launcher_->CheckAndMaybeLaunchApp(kTestAppId, launched_callback.Get()); + testing::Mock::VerifyAndClearExpectations(&launched_callback); + + EXPECT_CALL(launched_callback, Run(true)).Times(1); + UpdateAppReadiness(apps::Readiness::kReady); +} + +TEST_F(KioskAppServiceLauncherTest, ShouldLaunchIfAppReady) { + base::MockCallback<KioskAppServiceLauncher::AppLaunchedCallback> + launched_callback; + + UpdateAppReadiness(apps::Readiness::kReady); + EXPECT_CALL(launched_callback, Run(true)).Times(1); + launcher_->CheckAndMaybeLaunchApp(kTestAppId, launched_callback.Get()); +} + +} // namespace ash
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_apitest.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_apitest.cc index a40d276..641a264 100644 --- a/chrome/browser/chromeos/extensions/login_screen/login/login_apitest.cc +++ b/chrome/browser/chromeos/extensions/login_screen/login/login_apitest.cc
@@ -277,7 +277,8 @@ RunTest(kLockManagedGuestSessionNotActive); } -IN_PROC_BROWSER_TEST_F(LoginApitest, UnlockManagedGuestSession) { +// Flaky. https://crbug.com/1351338 +IN_PROC_BROWSER_TEST_F(LoginApitest, DISABLED_UnlockManagedGuestSession) { SetUpDeviceLocalAccountPolicy(); LogInWithPassword();
diff --git a/chrome/browser/device_reauth/mac/biometric_authenticator_mac.h b/chrome/browser/device_reauth/mac/biometric_authenticator_mac.h index 94832f4f..e4c5891b 100644 --- a/chrome/browser/device_reauth/mac/biometric_authenticator_mac.h +++ b/chrome/browser/device_reauth/mac/biometric_authenticator_mac.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_DEVICE_REAUTH_MAC_BIOMETRIC_AUTHENTICATOR_MAC_H_ #define CHROME_BROWSER_DEVICE_REAUTH_MAC_BIOMETRIC_AUTHENTICATOR_MAC_H_ +#include "base/callback.h" #include "chrome/browser/device_reauth/chrome_biometric_authenticator_common.h" #include "chrome/browser/device_reauth/chrome_biometric_authenticator_factory.h" #include "components/device_reauth/biometric_authenticator.h" @@ -53,9 +54,18 @@ BiometricAuthenticatorMac(); ~BiometricAuthenticatorMac() override; + // Called when the authentication compeletes with the result. + void OnAuthenticationCompleted(bool result); + + // Callback to be executed after the authentication completes. + AuthenticateCallback callback_; + // TouchId authenticator object that will handle biometric authentication - // itself + // itself. std::unique_ptr<device::fido::mac::TouchIdContext> touch_id_auth_context_; + + // Factory for weak pointers to this class. + base::WeakPtrFactory<BiometricAuthenticatorMac> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_DEVICE_REAUTH_MAC_BIOMETRIC_AUTHENTICATOR_MAC_H_
diff --git a/chrome/browser/device_reauth/mac/biometric_authenticator_mac.mm b/chrome/browser/device_reauth/mac/biometric_authenticator_mac.mm index 978084d2..0e51f583 100644 --- a/chrome/browser/device_reauth/mac/biometric_authenticator_mac.mm +++ b/chrome/browser/device_reauth/mac/biometric_authenticator_mac.mm
@@ -5,7 +5,6 @@ #include "chrome/browser/device_reauth/mac/biometric_authenticator_mac.h" #include "base/bind.h" -#include "base/callback.h" #include "base/notreached.h" #include "components/device_reauth/biometric_authenticator.h" #include "device/fido/mac/touch_id_context.h" @@ -37,18 +36,29 @@ const std::u16string message, AuthenticateCallback callback) { if (!NeedsToAuthenticate()) { + DCHECK(callback_.is_null()); std::move(callback).Run(/*success=*/true); return; } - // TODO(crbug.com/1350994): Clean the touchIdContext object after - // authentication is done. + if (callback_) { + std::move(callback_).Run(/*success=*/false); + } + touch_id_auth_context_ = device::fido::mac::TouchIdContext::Create(); - base::OnceCallback<bool(bool)> record_authentication_result = - base::BindOnce(&BiometricAuthenticatorMac::RecordAuthenticationResult, - base::Unretained(this)); + callback_ = std::move(callback); touch_id_auth_context_->PromptTouchId( message, - std::move(record_authentication_result).Then(std::move(callback))); + base::BindOnce(&BiometricAuthenticatorMac::OnAuthenticationCompleted, + weak_ptr_factory_.GetWeakPtr())); +} + +void BiometricAuthenticatorMac::OnAuthenticationCompleted(bool result) { + if (callback_.is_null()) { + return; + } + + std::move(callback_).Run(RecordAuthenticationResult(result)); + touch_id_auth_context_ = nullptr; }
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index b91ea09..54254f9 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -1008,6 +1008,8 @@ "clipboard_extension_helper_chromeos.h", "extension_keeplist_chromeos.cc", "extension_keeplist_chromeos.h", + "system_display/display_info_provider_chromeos.cc", + "system_display/display_info_provider_chromeos.h", "system_display/display_info_provider_utils.cc", "system_display/display_info_provider_utils.h", "system_display/system_display_serialization.cc", @@ -1124,8 +1126,6 @@ "extension_assets_manager_chromeos.h", "extension_garbage_collector_chromeos.cc", "extension_garbage_collector_chromeos.h", - "system_display/display_info_provider_chromeos.cc", - "system_display/display_info_provider_chromeos.h", "updater/chromeos_extension_cache_delegate.cc", "updater/chromeos_extension_cache_delegate.h", "updater/extension_cache_impl.cc", @@ -1236,8 +1236,6 @@ "chrome_kiosk_delegate.cc", "preinstalled_apps.cc", "preinstalled_apps.h", - "system_display/display_info_provider_lacros.cc", - "system_display/display_info_provider_lacros.h", ] deps += [ "//chromeos/lacros",
diff --git a/chrome/browser/extensions/active_tab_permission_granter.cc b/chrome/browser/extensions/active_tab_permission_granter.cc index 4cd2e60..6f13be4 100644 --- a/chrome/browser/extensions/active_tab_permission_granter.cc +++ b/chrome/browser/extensions/active_tab_permission_granter.cc
@@ -23,9 +23,9 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_util.h" +#include "extensions/browser/network_permissions_updater.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/renderer_startup_helper.h" -#include "extensions/common/cors_util.h" #include "extensions/common/extension.h" #include "extensions/common/extension_messages.h" #include "extensions/common/manifest_handlers/incognito_info.h" @@ -118,21 +118,17 @@ const Extension& extension, base::OnceClosure closure) { // To limit how far the new permissions reach, we only apply them to the - // ActiveTab's profile for split-mode extensions. OTOH, spanning-mode + // ActiveTab's context for split-mode extensions. OTOH, spanning-mode // extensions need to get the new permissions in all profiles (e.g. if the // ActiveTab is in an incognito window, than the [single/only/spanning] // background page in the regular profile also needs to get the new // permissions). - std::vector<content::BrowserContext*> target_contexts; - if (IncognitoInfo::IsSplitMode(&extension)) { - target_contexts = {browser_context}; - } else { - target_contexts = util::GetAllRelatedProfiles( - Profile::FromBrowserContext(browser_context), extension); - } - - util::SetCorsOriginAccessListForExtension(target_contexts, extension, - std::move(closure)); + NetworkPermissionsUpdater::ContextSet context_set = + IncognitoInfo::IsSplitMode(&extension) + ? NetworkPermissionsUpdater::ContextSet::kCurrentContextOnly + : NetworkPermissionsUpdater::ContextSet::kAllRelatedContexts; + NetworkPermissionsUpdater::UpdateExtension(*browser_context, extension, + context_set, std::move(closure)); } } // namespace
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc index 003d8a4..a9ad9597 100644 --- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc +++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc
@@ -25,6 +25,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/url_loader_interceptor.h" #include "device/fido/features.h" +#include "extensions/browser/extension_registry.h" #include "extensions/browser/pref_names.h" #include "extensions/common/extension_features.h" #include "net/dns/mock_host_resolver.h" @@ -55,10 +56,16 @@ CryptotokenBrowserTest() : base::test::WithFeatureOverride( extensions_features::kU2FSecurityKeyAPI) { + // Enable the feature flag to load the cryptoken component extension at + // startup. + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/{extensions_features::kLoadCryptoTokenExtension}, + /*disabled_features=*/{ #if BUILDFLAG(IS_WIN) - // Don't dispatch requests to the native Windows API. - scoped_feature_list_.InitAndDisableFeature(device::kWebAuthUseNativeWinApi); + // Don't dispatch requests to the native Windows API. + device::kWebAuthUseNativeWinApi #endif + }); } void SetUpCommandLine(base::CommandLine* command_line) override { @@ -266,9 +273,7 @@ return true; } -#if BUILDFLAG(IS_WIN) base::test::ScopedFeatureList scoped_feature_list_; -#endif std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_; }; @@ -405,5 +410,29 @@ INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(CryptotokenBrowserTest); +// Test that `kLoadCryptoTokenExtension` controls loading of the component +// extension. +class CryptotokenLoadBrowserTest : public base::test::WithFeatureOverride, + public InProcessBrowserTest { + protected: + CryptotokenLoadBrowserTest() + : base::test::WithFeatureOverride( + extensions_features::kLoadCryptoTokenExtension) {} + + void SetUp() override { + ComponentLoader::EnableBackgroundExtensionsForTesting(); + InProcessBrowserTest::SetUp(); + } +}; + +IN_PROC_BROWSER_TEST_P(CryptotokenLoadBrowserTest, IsLoaded) { + EXPECT_EQ(ExtensionRegistry::Get(browser()->profile()) + ->GenerateInstalledExtensionsSet() + ->GetByID(kCryptoTokenExtensionId) != nullptr, + IsParamFeatureEnabled()); +} + +INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(CryptotokenLoadBrowserTest); + } // namespace } // namespace extensions
diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc index 08d10cc..3fb3235 100644 --- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc +++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "build/build_config.h" #include "chrome/browser/extensions/api/scripting/scripting_api.h" #include "base/test/bind.h" @@ -518,7 +519,13 @@ content::test::ScopedPrerenderFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(ScriptingAPIPrerenderingTest, Basic) { +// TODO(crbug.com/1351648): disabled on Mac due to flakiness. +#if BUILDFLAG(IS_MAC) +#define MAYBE_Basic DISABLED_Basic +#else // BUILDFLAG(IS_MAC) +#define MAYBE_Basic Basic +#endif // BUILDFLAG(IS_MAC) +IN_PROC_BROWSER_TEST_F(ScriptingAPIPrerenderingTest, MAYBE_Basic) { ASSERT_TRUE(RunExtensionTest("scripting/prerendering")) << message_; }
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc index b8289914..8c52b09 100644 --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc
@@ -44,6 +44,7 @@ #include "extensions/browser/extension_system.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_features.h" #include "extensions/common/extension_l10n_util.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest_constants.h" @@ -548,8 +549,11 @@ #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) - Add(IDR_CRYPTOTOKEN_MANIFEST, - base::FilePath(FILE_PATH_LITERAL("cryptotoken"))); + if (base::FeatureList::IsEnabled( + extensions_features::kLoadCryptoTokenExtension)) { + Add(IDR_CRYPTOTOKEN_MANIFEST, + base::FilePath(FILE_PATH_LITERAL("cryptotoken"))); + } } void ComponentLoader::
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc index a0b999e..f0980b8 100644 --- a/chrome/browser/extensions/permissions_updater.cc +++ b/chrome/browser/extensions/permissions_updater.cc
@@ -180,6 +180,7 @@ // NotifyPermissionsUpdated if the profile is still valid. NetworkPermissionsUpdater::UpdateExtension( *browser_context, *extension, + NetworkPermissionsUpdater::ContextSet::kAllRelatedContexts, base::BindOnce(&NetworkPermissionsUpdateHelper::OnOriginAccessUpdated, helper->weak_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc index 57c6727..7278fef 100644 --- a/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc +++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos.cc
@@ -7,7 +7,12 @@ #include <stdint.h> #include <cmath> +#if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/public/ash_interfaces.h" +#elif BUILDFLAG(IS_CHROMEOS_LACROS) +#include "chromeos/lacros/lacros_service.h" +#endif + #include "base/bind.h" #include "base/strings/string_number_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -637,6 +642,8 @@ DispatchOnDisplayChangedEvent(); } +#if BUILDFLAG(IS_CHROMEOS_ASH) + std::unique_ptr<DisplayInfoProvider> CreateChromeDisplayInfoProvider() { mojo::PendingRemote<crosapi::mojom::CrosDisplayConfigController> display_config; @@ -646,4 +653,25 @@ std::move(display_config)); } +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + +std::unique_ptr<DisplayInfoProvider> CreateChromeDisplayInfoProvider() { + // Assume that LacrosService has already been initialized. + auto* lacros_service = chromeos::LacrosService::Get(); + if (lacros_service && + lacros_service + ->IsAvailable<crosapi::mojom::CrosDisplayConfigController>()) { + auto& remote = + lacros_service + ->GetRemote<crosapi::mojom::CrosDisplayConfigController>(); + return std::make_unique<DisplayInfoProviderChromeOS>(remote.Unbind()); + } + + LOG(ERROR) << "Cannot create DisplayInfoProvider in Lacros. " + "CrosDisplayConfigController interface is not available."; + return nullptr; +} + +#endif + } // namespace extensions
diff --git a/chrome/browser/extensions/system_display/display_info_provider_lacros.cc b/chrome/browser/extensions/system_display/display_info_provider_lacros.cc deleted file mode 100644 index b97229c..0000000 --- a/chrome/browser/extensions/system_display/display_info_provider_lacros.cc +++ /dev/null
@@ -1,193 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/system_display/display_info_provider_lacros.h" - -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" -#include "chrome/browser/extensions/system_display/display_info_provider_utils.h" -#include "chrome/browser/extensions/system_display/system_display_serialization.h" -#include "chromeos/crosapi/mojom/cros_display_config.mojom.h" -#include "chromeos/lacros/lacros_service.h" -#include "extensions/common/api/system_display.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/gfx/geometry/mojom/geometry.mojom.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" - -namespace extensions { - -DisplayInfoProviderLacros::DisplayInfoProviderLacros() { - // Relies on the fact that the instance is a singleton managed by - // DisplayInfoProvider::Get(), and assumes that instantiation takes place - // after LacrosService has been initialized. - auto* lacros_service = chromeos::LacrosService::Get(); - DCHECK(lacros_service); - if (lacros_service->IsAvailable<crosapi::mojom::SystemDisplay>() && - lacros_service->GetInterfaceVersion( - crosapi::mojom::SystemDisplay::Uuid_) >= - static_cast<int>(crosapi::mojom::SystemDisplay:: - kAddDisplayChangeObserverMinVersion)) { - lacros_service->GetRemote<crosapi::mojom::SystemDisplay>() - ->AddDisplayChangeObserver( - receiver_.BindNewPipeAndPassRemoteWithVersion()); - } -} - -DisplayInfoProviderLacros::~DisplayInfoProviderLacros() = default; - -void DisplayInfoProviderLacros::GetAllDisplaysInfo( - bool single_unified, - base::OnceCallback<void(DisplayUnitInfoList)> callback) { - auto* lacros_service = chromeos::LacrosService::Get(); - if (lacros_service->IsAvailable<crosapi::mojom::SystemDisplay>()) { - auto cb = - base::BindOnce(&DisplayInfoProviderLacros::OnCrosapiResult, - weak_ptr_factory_.GetWeakPtr(), std::move(callback)); - lacros_service->GetRemote<crosapi::mojom::SystemDisplay>() - ->GetDisplayUnitInfoList(single_unified, std::move(cb)); - - } else { - std::move(callback).Run(DisplayUnitInfoList()); - } -} - -void DisplayInfoProviderLacros::OnCrosapiResult( - base::OnceCallback<void(DisplayUnitInfoList)> callback, - std::vector<crosapi::mojom::SysDisplayUnitInfoPtr> src_info_list) { - DisplayUnitInfoList dst_info_list(src_info_list.size()); - for (size_t i = 0; i < src_info_list.size(); ++i) { - DCHECK(src_info_list[i]); - extensions::api::system_display::DeserializeDisplayUnitInfo( - *src_info_list[i], &dst_info_list[i]); - } - std::move(callback).Run(std::move(dst_info_list)); -} - -void DisplayInfoProviderLacros::OnCrosapiDisplayChanged() { - DispatchOnDisplayChangedEvent(); -} - -void DisplayInfoProviderLacros::GetDisplayLayout( - base::OnceCallback<void(DisplayLayoutList)> callback) { - auto* lacros_service = chromeos::LacrosService::Get(); - if (lacros_service - ->IsAvailable<crosapi::mojom::CrosDisplayConfigController>()) { - auto& remote = - lacros_service - ->GetRemote<crosapi::mojom::CrosDisplayConfigController>(); - - remote->GetDisplayLayoutInfo( - base::BindOnce(&OnGetDisplayLayoutResult, std::move(callback))); - - } else { - LOG(ERROR) << "Cros display config service is not available."; - std::move(callback).Run(DisplayLayoutList()); - } -} - -void DisplayInfoProviderLacros::SetDisplayProperties( - const std::string& display_id, - const api::system_display::DisplayProperties& properties, - ErrorCallback callback) { - constexpr char kTempError[] = - "SetDisplayProperties to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - std::move(callback).Run(kTempError); -} - -void DisplayInfoProviderLacros::SetDisplayLayout( - const DisplayLayoutList& layouts, - ErrorCallback callback) { - constexpr char kTempError[] = "SetDisplayLayout to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - std::move(callback).Run(kTempError); -} - -void DisplayInfoProviderLacros::EnableUnifiedDesktop(bool enable) { - constexpr char kTempError[] = - "EnableUnifiedDesktop to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; -} - -bool DisplayInfoProviderLacros::OverscanCalibrationStart( - const std::string& id) { - constexpr char kTempError[] = - "OverscanCalibrationStart to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} -bool DisplayInfoProviderLacros::OverscanCalibrationAdjust( - const std::string& id, - const api::system_display::Insets& delta) { - constexpr char kTempError[] = - "OverscanCalibrationAdjust to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -bool DisplayInfoProviderLacros::OverscanCalibrationReset( - const std::string& id) { - constexpr char kTempError[] = - "OverscanCalibrationAdjust to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -bool DisplayInfoProviderLacros::OverscanCalibrationComplete( - const std::string& id) { - constexpr char kTempError[] = - "OverscanCalibrationComplete to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -void DisplayInfoProviderLacros::ShowNativeTouchCalibration( - const std::string& id, - ErrorCallback callback) { - constexpr char kTempError[] = - "ShowNativeTouchCalibration to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - std::move(callback).Run(kTempError); -} - -bool DisplayInfoProviderLacros::StartCustomTouchCalibration( - const std::string& id) { - constexpr char kTempError[] = - "StartCustomTouchCalibration to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -bool DisplayInfoProviderLacros::CompleteCustomTouchCalibration( - const api::system_display::TouchCalibrationPairQuad& pairs, - const api::system_display::Bounds& bounds) { - constexpr char kTempError[] = - "CompleteCustomTouchCalibration to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -bool DisplayInfoProviderLacros::ClearTouchCalibration(const std::string& id) { - constexpr char kTempError[] = - "ClearTouchCalibration to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - return false; -} - -void DisplayInfoProviderLacros::SetMirrorMode( - const api::system_display::MirrorModeInfo& info, - ErrorCallback callback) { - constexpr char kTempError[] = "SetMirrorMode to be implemented in Lacros"; - NOTIMPLEMENTED() << kTempError; - std::move(callback).Run(kTempError); -} - -std::unique_ptr<DisplayInfoProvider> CreateChromeDisplayInfoProvider() { - return std::make_unique<DisplayInfoProviderLacros>(); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/system_display/display_info_provider_lacros.h b/chrome/browser/extensions/system_display/display_info_provider_lacros.h deleted file mode 100644 index 68d56dd5..0000000 --- a/chrome/browser/extensions/system_display/display_info_provider_lacros.h +++ /dev/null
@@ -1,82 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_EXTENSIONS_SYSTEM_DISPLAY_DISPLAY_INFO_PROVIDER_LACROS_H_ -#define CHROME_BROWSER_EXTENSIONS_SYSTEM_DISPLAY_DISPLAY_INFO_PROVIDER_LACROS_H_ - -#include <vector> - -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "chromeos/crosapi/mojom/system_display.mojom.h" -#include "extensions/browser/api/system_display/display_info_provider.h" -#include "extensions/common/api/system_display.h" -#include "mojo/public/cpp/bindings/receiver.h" - -namespace extensions { - -// DisplayInfoProvider used by lacros-chrome that uses crosapi to: -// * Get DisplayUnitInfoList from ash-chrome, handling potential version skew. -// * Pass display change events from ash-chrome to lacros-chrome. -class DisplayInfoProviderLacros : public DisplayInfoProvider, - public crosapi::mojom::DisplayChangeObserver { - public: - DisplayInfoProviderLacros(); - ~DisplayInfoProviderLacros() override; - DisplayInfoProviderLacros(const DisplayInfoProviderLacros&) = delete; - const DisplayInfoProviderLacros& operator=(const DisplayInfoProviderLacros&) = - delete; - - // DisplayInfoProvider: - void GetAllDisplaysInfo( - bool single_unified, - base::OnceCallback<void(DisplayUnitInfoList)> callback) override; - - void GetDisplayLayout( - base::OnceCallback<void(DisplayLayoutList result)> callback) override; - - void SetDisplayProperties( - const std::string& display_id, - const api::system_display::DisplayProperties& properties, - ErrorCallback callback) override; - - void SetDisplayLayout(const DisplayLayoutList& layouts, - ErrorCallback callback) override; - - void EnableUnifiedDesktop(bool enable) override; - - bool OverscanCalibrationStart(const std::string& id) override; - bool OverscanCalibrationAdjust( - const std::string& id, - const api::system_display::Insets& delta) override; - bool OverscanCalibrationReset(const std::string& id) override; - bool OverscanCalibrationComplete(const std::string& id) override; - void ShowNativeTouchCalibration(const std::string& id, - ErrorCallback callback) override; - bool StartCustomTouchCalibration(const std::string& id) override; - bool CompleteCustomTouchCalibration( - const api::system_display::TouchCalibrationPairQuad& pairs, - const api::system_display::Bounds& bounds) override; - bool ClearTouchCalibration(const std::string& id) override; - void SetMirrorMode(const api::system_display::MirrorModeInfo& info, - ErrorCallback callback) override; - - private: - // Receiver for SystemDisplayAsh::GetDisplayUnitInfoList(). - void OnCrosapiResult( - base::OnceCallback<void(DisplayUnitInfoList)> callback, - std::vector<crosapi::mojom::SysDisplayUnitInfoPtr> src_info_list); - - // crosapi::mojom::DisplayChangeObserver: - void OnCrosapiDisplayChanged() override; - - // Receives mojo messages from ash-chrome. - mojo::Receiver<crosapi::mojom::DisplayChangeObserver> receiver_{this}; - - base::WeakPtrFactory<DisplayInfoProviderLacros> weak_ptr_factory_{this}; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_SYSTEM_DISPLAY_DISPLAY_INFO_PROVIDER_LACROS_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 7bf0828b..3638c46 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -4138,11 +4138,6 @@ "expiry_milestone": 108 }, { - "name": "lazily-create-web-state-on-restoration", - "owners": ["olivierrobin", "sdefresne"], - "expiry_milestone": 105 - }, - { "name": "leak-detection-unauthenticated", "owners": ["rgod@google.com", "vasilii"], "expiry_milestone": 102
diff --git a/chrome/browser/installable/installed_webapp_bridge.cc b/chrome/browser/installable/installed_webapp_bridge.cc index 61de4c4..1fbccb59 100644 --- a/chrome/browser/installable/installed_webapp_bridge.cc +++ b/chrome/browser/installable/installed_webapp_bridge.cc
@@ -11,6 +11,7 @@ #include "base/android/jni_utils.h" #include "chrome/android/chrome_jni_headers/InstalledWebappBridge_jni.h" #include "url/gurl.h" +#include "url/origin.h" using base::android::ConvertJavaStringToUTF8; using base::android::ScopedJavaLocalRef; @@ -81,3 +82,18 @@ env, static_cast<int>(type), j_origin_url, j_last_committed_url, reinterpret_cast<jlong>(callback_ptr)); } + +ContentSetting InstalledWebappBridge::GetPermission(ContentSettingsType type, + const GURL& url) { + JNIEnv* env = base::android::AttachCurrentThread(); + + ScopedJavaLocalRef<jstring> java_origin = + base::android::ConvertUTF8ToJavaString( + env, url::Origin::Create(url).Serialize()); + + ContentSetting setting = + IntToContentSetting(Java_InstalledWebappBridge_getPermission( + env, static_cast<int>(type), java_origin)); + + return setting; +}
diff --git a/chrome/browser/installable/installed_webapp_bridge.h b/chrome/browser/installable/installed_webapp_bridge.h index 52c7438..9fa2895 100644 --- a/chrome/browser/installable/installed_webapp_bridge.h +++ b/chrome/browser/installable/installed_webapp_bridge.h
@@ -30,6 +30,9 @@ const GURL& origin_url, const GURL& last_committed_url, PermissionCallback callback); + + static ContentSetting GetPermission(ContentSettingsType type, + const GURL& origin); }; #endif // CHROME_BROWSER_INSTALLABLE_INSTALLED_WEBAPP_BRIDGE_H_
diff --git a/chrome/browser/lacros/DEPS b/chrome/browser/lacros/DEPS index d376652..7a711be8 100644 --- a/chrome/browser/lacros/DEPS +++ b/chrome/browser/lacros/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+chrome/browser/ui/views", + "+chromeos/ui/wm", "+components/account_manager_core", "+components/arc/lacros", "+components/memory_pressure",
diff --git a/chrome/browser/lacros/cert/cert_db_initializer_browsertest.cc b/chrome/browser/lacros/cert/cert_db_initializer_browsertest.cc index 9c8e7a3..61784d4 100644 --- a/chrome/browser/lacros/cert/cert_db_initializer_browsertest.cc +++ b/chrome/browser/lacros/cert/cert_db_initializer_browsertest.cc
@@ -181,9 +181,10 @@ [[nodiscard]] std::vector<uint8_t> GenerateClientCertForPublicKey( const std::vector<uint8_t>& public_key_spki) { net::CertBuilder issuer(/*orig_cert=*/nullptr, /*issuer=*/nullptr); + issuer.GenerateRSAKey(); auto cert_builder = net::CertBuilder::FromSubjectPublicKeyInfo(public_key_spki, &issuer); - cert_builder->SetSignatureAlgorithmRsaPkca1(net::DigestAlgorithm::Sha256); + cert_builder->SetSignatureAlgorithm(net::SignatureAlgorithm::kRsaPkcs1Sha256); cert_builder->SetValidity(base::Time::Now(), base::Time::Now() + base::Days(30));
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc index c6a4d18..283f1061 100644 --- a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc +++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/lacros/drivefs_cache.h" #include "chrome/browser/lacros/field_trial_observer.h" #include "chrome/browser/lacros/force_installed_tracker_lacros.h" +#include "chrome/browser/lacros/fullscreen_controller_client_lacros.h" #include "chrome/browser/lacros/lacros_butter_bar.h" #include "chrome/browser/lacros/lacros_extension_apps_controller.h" #include "chrome/browser/lacros/lacros_extension_apps_publisher.h" @@ -85,6 +86,8 @@ download_controller_client_ = std::make_unique<DownloadControllerClientLacros>(); file_system_provider_ = std::make_unique<LacrosFileSystemProvider>(); + fullscreen_controller_client_ = + std::make_unique<FullscreenControllerClientLacros>(); kiosk_session_service_ = std::make_unique<KioskSessionServiceLacros>(); network_change_manager_bridge_ = std::make_unique<NetworkChangeManagerBridge>();
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h index b1ba8cdb..0702f10 100644 --- a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h +++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h
@@ -19,6 +19,7 @@ class DriveFsCache; class DownloadControllerClientLacros; class ForceInstalledTrackerLacros; +class FullscreenControllerClientLacros; class LacrosButterBar; class LacrosExtensionAppsController; class LacrosExtensionAppsPublisher; @@ -75,6 +76,10 @@ // Handles requests for desk template data from ash-chrome. std::unique_ptr<DeskTemplateClientLacros> desk_template_client_; + // Handles queries regarding full screen control from ash-chrome. + std::unique_ptr<FullscreenControllerClientLacros> + fullscreen_controller_client_; + // Handles search queries from ash-chrome. std::unique_ptr<crosapi::SearchControllerLacros> search_controller_;
diff --git a/chrome/browser/lacros/fullscreen_controller_client_lacros.cc b/chrome/browser/lacros/fullscreen_controller_client_lacros.cc new file mode 100644 index 0000000..33f526f --- /dev/null +++ b/chrome/browser/lacros/fullscreen_controller_client_lacros.cc
@@ -0,0 +1,84 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/lacros/fullscreen_controller_client_lacros.h" + +#include "base/callback.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chromeos/lacros/lacros_service.h" +#include "chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/app_window/app_window.h" +#include "extensions/browser/app_window/app_window_registry.h" +#include "extensions/browser/app_window/native_app_window.h" +#include "url/gurl.h" + +FullscreenControllerClientLacros::FullscreenControllerClientLacros() { + auto* const lacros_service = chromeos::LacrosService::Get(); + if (lacros_service->IsAvailable<crosapi::mojom::FullscreenController>()) { + lacros_service->GetRemote<crosapi::mojom::FullscreenController>() + ->AddClient(receiver_.BindNewPipeAndPassRemote()); + } +} + +FullscreenControllerClientLacros::~FullscreenControllerClientLacros() = default; + +void FullscreenControllerClientLacros::ShouldExitFullscreenBeforeLock( + base::OnceCallback<void(bool)> callback) { + if (!keep_fullscreen_checker_) { + keep_fullscreen_checker_ = + std::make_unique<chromeos::KeepFullscreenForUrlChecker>( + ProfileManager::GetPrimaryUserProfile()->GetPrefs()); + } + + if (!keep_fullscreen_checker_ + ->IsKeepFullscreenWithoutNotificationPolicySet()) { + std::move(callback).Run(/*should_exit_fullscreen=*/true); + return; + } + + // Get the web content if the active window is a browser window. + content::WebContents* web_contents = nullptr; + Browser* browser = chrome::FindBrowserWithActiveWindow(); + if (browser) { + web_contents = browser->tab_strip_model()->GetActiveWebContents(); + } + + // Get the web content if the active window is an app window. + if (!web_contents) { + web_contents = GetActiveAppWindowWebContents(); + } + + if (!web_contents) { + std::move(callback).Run(/*should_exit_fullscreen=*/true); + return; + } + + // Check if it is allowed by user pref to keep full screen for the window URL. + GURL url = web_contents->GetLastCommittedURL(); + std::move(callback).Run( + keep_fullscreen_checker_->ShouldExitFullscreenForUrl(url)); +} + +content::WebContents* +FullscreenControllerClientLacros::GetActiveAppWindowWebContents() { + Profile* profile = ProfileManager::GetLastUsedProfile(); + if (!profile) { + return nullptr; + } + + const auto& app_windows = + extensions::AppWindowRegistry::Get(profile)->app_windows(); + for (auto* app_window : app_windows) { + if (app_window->GetBaseWindow()->IsActive()) { + return app_window->web_contents(); + } + } + + return nullptr; +}
diff --git a/chrome/browser/lacros/fullscreen_controller_client_lacros.h b/chrome/browser/lacros/fullscreen_controller_client_lacros.h new file mode 100644 index 0000000..7479eef2 --- /dev/null +++ b/chrome/browser/lacros/fullscreen_controller_client_lacros.h
@@ -0,0 +1,50 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_LACROS_FULLSCREEN_CONTROLLER_CLIENT_LACROS_H_ +#define CHROME_BROWSER_LACROS_FULLSCREEN_CONTROLLER_CLIENT_LACROS_H_ + +#include "base/memory/weak_ptr.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace chromeos { +class KeepFullscreenForUrlChecker; +} // namespace chromeos + +namespace content { +class WebContents; +} // namespace content + +// The lacros-chrome implementation of the full screen controller client crosapi +// interface. Receives and processes requests from ash-chrome. +class FullscreenControllerClientLacros + : public crosapi::mojom::FullscreenControllerClient { + public: + FullscreenControllerClientLacros(); + FullscreenControllerClientLacros(const FullscreenControllerClientLacros&) = + delete; + FullscreenControllerClientLacros& operator=( + const FullscreenControllerClientLacros&) = delete; + ~FullscreenControllerClientLacros() override; + + private: + // crosapi::mojom::FullscreenControllerClient: + void ShouldExitFullscreenBeforeLock( + base::OnceCallback<void(bool)> callback) override; + + content::WebContents* GetActiveAppWindowWebContents(); + + std::unique_ptr<chromeos::KeepFullscreenForUrlChecker> + keep_fullscreen_checker_; + + // Endpoint to communicate with Ash. + mojo::Receiver<crosapi::mojom::FullscreenControllerClient> receiver_{this}; + + base::WeakPtrFactory<FullscreenControllerClientLacros> weak_ptr_factory_{ + this}; +}; + +#endif // CHROME_BROWSER_LACROS_FULLSCREEN_CONTROLLER_CLIENT_LACROS_H_
diff --git a/chrome/browser/lacros/fullscreen_controller_client_lacros_unittest.cc b/chrome/browser/lacros/fullscreen_controller_client_lacros_unittest.cc new file mode 100644 index 0000000..b0595a6 --- /dev/null +++ b/chrome/browser/lacros/fullscreen_controller_client_lacros_unittest.cc
@@ -0,0 +1,264 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/lacros/fullscreen_controller_client_lacros.h" + +#include "base/callback.h" +#include "base/test/test_future.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/apps/chrome_app_delegate.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chrome/test/base/test_browser_window.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" +#include "chromeos/ui/wm/fullscreen/pref_names.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/navigation_simulator.h" +#include "content/public/test/web_contents_tester.h" +#include "extensions/browser/app_window/app_window.h" +#include "extensions/browser/app_window/app_window_registry.h" +#include "extensions/browser/app_window/test_app_window_contents.h" +#include "extensions/common/extension_builder.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.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/window.h" +#include "ui/views/controls/webview/webview.h" +#include "url/gurl.h" + +namespace chromeos { + +using crosapi::mojom::FullscreenController; +using crosapi::mojom::FullscreenControllerClient; + +namespace { + +constexpr char kActiveUrl[] = "https://wwww.test.com"; + +constexpr char kNonMatchingPattern[] = "google.com"; +constexpr char kMatchingPattern[] = "test.com"; +constexpr char kWildcardPattern[] = "*"; + +enum class TestWebContentsChoice { + kAppWindow, + kBrowserWindow, +}; + +std::string ParamToString( + const testing::TestParamInfo<TestWebContentsChoice>& info) { + switch (info.param) { + case TestWebContentsChoice::kAppWindow: + return "AppWindow"; + case TestWebContentsChoice::kBrowserWindow: + return "BrowserWindow"; + } +} + +class MockRemote : public FullscreenController { + public: + // FullscreenController: + void AddClient( + mojo::PendingRemote<FullscreenControllerClient> client) override { + remote_.Bind(std::move(client)); + } + + void ShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback) { + remote_->ShouldExitFullscreenBeforeLock( + base::BindOnce(&MockRemote::OnShouldExitFullscreenBeforeLock, + base::Unretained(this), std::move(callback))); + } + + void OnShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback, + bool should_exit_fullscreen) { + std::move(callback).Run(should_exit_fullscreen); + } + + private: + mojo::Receiver<FullscreenController> receiver_{this}; + mojo::Remote<FullscreenControllerClient> remote_; +}; + +class TestNativeAppWindow : public ChromeNativeAppWindowViewsAura { + public: + TestNativeAppWindow() { + set_web_view_for_testing( + AddChildView(std::make_unique<views::WebView>(nullptr))); + } + ~TestNativeAppWindow() override {} + + bool IsActive() const override { return true; } +}; + +} // namespace + +class FullscreenControllerClientLacrosTest : public BrowserWithTestWindowTest { + public: + void SetUp() override { + BrowserWithTestWindowTest::SetUp(); + + profile_ = ProfileManager::GetPrimaryUserProfile(); + + // Set the active profile. + PrefService* local_state = g_browser_process->local_state(); + local_state->SetString(::prefs::kProfileLastUsed, + profile_->GetBaseName().value()); + } + + void TearDown() override { BrowserWithTestWindowTest::TearDown(); } + + void SetKeepFullscreenWithoutNotificationAllowList( + const std::string& pattern) { + base::Value list(base::Value::Type::LIST); + list.Append(base::Value(pattern)); + profile_->GetPrefs()->Set( + prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); + } + + void RunTest(bool expect_should_exit_fullscreen) { + base::test::TestFuture<bool> future; + + FullscreenControllerClientLacros client; + mojo::Receiver<FullscreenControllerClient> receiver{&client}; + mock_.AddClient(receiver.BindNewPipeAndPassRemote()); + mock_.ShouldExitFullscreenBeforeLock(future.GetCallback()); + + bool should_exit_fullscreen = future.Take(); + ASSERT_EQ(should_exit_fullscreen, expect_should_exit_fullscreen); + } + + protected: + Profile* profile_ = nullptr; + testing::StrictMock<MockRemote> mock_; +}; + +// Test that ShouldExitFullscreenBeforeLock() returns true if the WebContent is +// not found and the allow list is unset. +TEST_F(FullscreenControllerClientLacrosTest, + ExitFullscreenIfWebContentsUnavailableUnsetPref) { + RunTest(/*expect_should_exit_fullscreen=*/true); +} + +// Test that ShouldExitFullscreenBeforeLock() returns true if the WebContent is +// not found and the allow list includes the wildcard character. +TEST_F(FullscreenControllerClientLacrosTest, + ExitFullscreenIfWebContentsUnavailableWildcardPref) { + SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern); + + RunTest(/*expect_should_exit_fullscreen=*/true); +} + +class FullscreenControllerClientLacrosWebContentsTest + : public FullscreenControllerClientLacrosTest, + public testing::WithParamInterface<TestWebContentsChoice> { + public: + void SetUp() override { + FullscreenControllerClientLacrosTest::SetUp(); + + switch (GetParam()) { + case TestWebContentsChoice::kAppWindow: + AddAppWindow(); + break; + case TestWebContentsChoice::kBrowserWindow: + AddBrowserWindow(); + break; + } + } + + void TearDown() override { + if (app_window_) { + app_window_->OnNativeClose(); + app_window_ = nullptr; + } + + FullscreenControllerClientLacrosTest::TearDown(); + } + + void AddAppWindow() { + // Create a new AppWindow + scoped_refptr<const extensions::Extension> extension = + extensions::ExtensionBuilder("test extension").Build(); + app_window_ = new extensions::AppWindow( + profile_, std::make_unique<ChromeAppDelegate>(profile_, true), + extension.get()); + + // Set the active WebContents + std::unique_ptr<content::WebContents> contents(content::WebContents::Create( + content::WebContents::CreateParams(app_window_->browser_context()))); + content::NavigationSimulator::NavigateAndCommitFromBrowser( + contents.get(), GURL(kActiveUrl)); + app_window_->SetAppWindowContentsForTesting( + std::make_unique<extensions::TestAppWindowContents>( + std::move(contents))); + + // Set the native app window + app_window_->SetNativeAppWindowForTesting( + std::make_unique<TestNativeAppWindow>()); + + extensions::AppWindowRegistry::Get(profile_)->AddAppWindow(app_window_); + } + + void AddBrowserWindow() { + AddTab(browser(), GURL(kActiveUrl)); + static_cast<TestBrowserWindow*>(browser()->window())->set_is_active(true); + ASSERT_TRUE(chrome::FindBrowserWithActiveWindow()); + } + + protected: + extensions::AppWindow* app_window_ = nullptr; +}; + +// Test that ShouldExitFullscreenBeforeLock() returns true if the allow list +// pref is unset. +TEST_P(FullscreenControllerClientLacrosWebContentsTest, + ExitFullscreenIfUnsetPref) { + RunTest(/*expect_should_exit_fullscreen=*/true); +} + +// Test that ShouldExitFullscreenBeforeLock() returns true if the URL of the +// active window does not match any patterns from the allow list. +TEST_P(FullscreenControllerClientLacrosWebContentsTest, + ExitFullscreenIfNonMatchingPref) { + SetKeepFullscreenWithoutNotificationAllowList(kNonMatchingPattern); + + RunTest(/*expect_should_exit_fullscreen=*/true); +} + +// Test that ShouldExitFullscreenBeforeLock() returns false if the URL of the +// active window matches a pattern from the allow list. +TEST_P(FullscreenControllerClientLacrosWebContentsTest, + KeepFullscreenIfMatchingPref) { + // Set up the URL exempt list with one matching and one non-matching pattern. + base::Value list(base::Value::Type::LIST); + list.Append(base::Value(kNonMatchingPattern)); + list.Append(base::Value(kMatchingPattern)); + profile_->GetPrefs()->Set( + prefs::kKeepFullscreenWithoutNotificationUrlAllowList, list); + + RunTest(/*expect_should_exit_fullscreen=*/false); +} + +// Test that ShouldExitFullscreenBeforeLock() returns false if the allow list +// includes the wildcard character. +TEST_P(FullscreenControllerClientLacrosWebContentsTest, + KeepFullscreenIfWildcardPref) { + SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern); + + RunTest(/*expect_should_exit_fullscreen=*/false); +} + +INSTANTIATE_TEST_SUITE_P( + All, + FullscreenControllerClientLacrosWebContentsTest, + ::testing::Values(TestWebContentsChoice::kAppWindow, + TestWebContentsChoice::kBrowserWindow), + &ParamToString); + +} // namespace chromeos
diff --git a/chrome/browser/lacros/keystore_service_lacros_browsertest.cc b/chrome/browser/lacros/keystore_service_lacros_browsertest.cc index 4028d2b..4dcf327 100644 --- a/chrome/browser/lacros/keystore_service_lacros_browsertest.cc +++ b/chrome/browser/lacros/keystore_service_lacros_browsertest.cc
@@ -112,9 +112,10 @@ const std::vector<uint8_t>& public_key_spki) { auto issuer = std::make_unique<net::CertBuilder>(/*orig_cert=*/nullptr, /*issuer=*/nullptr); + issuer->GenerateRSAKey(); auto cert_builder = net::CertBuilder::FromSubjectPublicKeyInfo(public_key_spki, issuer.get()); - cert_builder->SetSignatureAlgorithmRsaPkca1(net::DigestAlgorithm::Sha256); + cert_builder->SetSignatureAlgorithm(net::SignatureAlgorithm::kRsaPkcs1Sha256); cert_builder->SetValidity(base::Time::Now(), base::Time::Now() + base::Days(30)); return cert_builder->GetX509Certificate();
diff --git a/chrome/browser/lacros/standalone_browser_test_controller.cc b/chrome/browser/lacros/standalone_browser_test_controller.cc index 8830b64..91c06202 100644 --- a/chrome/browser/lacros/standalone_browser_test_controller.cc +++ b/chrome/browser/lacros/standalone_browser_test_controller.cc
@@ -54,6 +54,7 @@ mojo::Remote<crosapi::mojom::TestController>& test_controller) { test_controller->RegisterStandaloneBrowserTestController( controller_receiver_.BindNewPipeAndPassRemoteWithVersion()); + test_controller.FlushAsync(); } StandaloneBrowserTestController::~StandaloneBrowserTestController() = default;
diff --git a/chrome/browser/media/chromeos_login_media_access_handler.cc b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc similarity index 64% rename from chrome/browser/media/chromeos_login_media_access_handler.cc rename to chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc index 2843e4c..ddab8af 100644 --- a/chrome/browser/media/chromeos_login_media_access_handler.cc +++ b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc
@@ -2,36 +2,53 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/media/chromeos_login_media_access_handler.h" +#include "chrome/browser/media/chromeos_login_and_lock_media_access_handler.h" #include <string> #include "ash/components/settings/cros_settings_names.h" #include "base/logging.h" #include "base/values.h" +#include "chrome/browser/ash/login/saml/in_session_password_sync_manager.h" +#include "chrome/browser/ash/login/saml/in_session_password_sync_manager_factory.h" #include "chrome/browser/ash/login/ui/login_display_host.h" -#include "chrome/browser/ash/login/ui/webui_login_view.h" #include "chrome/browser/ash/settings/cros_settings.h" -#include "chrome/common/url_constants.h" +#include "chrome/browser/profiles/profile_manager.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "content/public/browser/render_frame_host.h" #include "url/gurl.h" -ChromeOSLoginMediaAccessHandler::ChromeOSLoginMediaAccessHandler() {} +ChromeOSLoginAndLockMediaAccessHandler:: + ChromeOSLoginAndLockMediaAccessHandler() = default; -ChromeOSLoginMediaAccessHandler::~ChromeOSLoginMediaAccessHandler() {} +ChromeOSLoginAndLockMediaAccessHandler:: + ~ChromeOSLoginAndLockMediaAccessHandler() = default; -bool ChromeOSLoginMediaAccessHandler::SupportsStreamType( +bool ChromeOSLoginAndLockMediaAccessHandler::SupportsStreamType( content::WebContents* web_contents, const blink::mojom::MediaStreamType type, const extensions::Extension* extension) { if (!web_contents) return false; + // Check if the `web_contents` corresponds to the login screen. auto* host = ash::LoginDisplayHost::default_host(); - return host && web_contents == host->GetOobeWebContents(); + if (host && web_contents == host->GetOobeWebContents()) { + return true; + } + // Check if the `web_contents` corresponds to the reauthentication dialog that + // is shown on the lock screen. This is the case when there is an active user + // profile and InSessionPasswordSyncManager for this profile is showing reauth + // dialog with the same `web_contents`. + Profile* profile = ProfileManager::GetActiveUserProfile(); + if (!profile) + return false; + auto* password_sync_manager = + ash::InSessionPasswordSyncManagerFactory::GetForProfile(profile); + return !!password_sync_manager && + web_contents == password_sync_manager->GetDialogWebContents(); } -bool ChromeOSLoginMediaAccessHandler::CheckMediaAccessPermission( +bool ChromeOSLoginAndLockMediaAccessHandler::CheckMediaAccessPermission( content::RenderFrameHost* render_frame_host, const GURL& security_origin, blink::mojom::MediaStreamType type, @@ -39,11 +56,6 @@ if (type != blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) return false; - // When creating new user (including supervised user), we must be able to use - // the camera to capture a user image. - if (security_origin.spec() == chrome::kChromeUIOobeURL) - return true; - const ash::CrosSettings* const settings = ash::CrosSettings::Get(); if (!settings) return false; @@ -55,7 +67,7 @@ return false; DCHECK(list_value->is_list()); - for (const auto& base_value : list_value->GetListDeprecated()) { + for (const auto& base_value : list_value->GetList()) { const std::string* value = base_value.GetIfString(); if (value) { const ContentSettingsPattern pattern = @@ -73,7 +85,7 @@ return false; } -void ChromeOSLoginMediaAccessHandler::HandleRequest( +void ChromeOSLoginAndLockMediaAccessHandler::HandleRequest( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback,
diff --git a/chrome/browser/media/chromeos_login_media_access_handler.h b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.h similarity index 65% rename from chrome/browser/media/chromeos_login_media_access_handler.h rename to chrome/browser/media/chromeos_login_and_lock_media_access_handler.h index 5c1dc19..1bf9392 100644 --- a/chrome/browser/media/chromeos_login_media_access_handler.h +++ b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.h
@@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_MEDIA_ACCESS_HANDLER_H_ -#define CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_MEDIA_ACCESS_HANDLER_H_ +#ifndef CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_AND_LOCK_MEDIA_ACCESS_HANDLER_H_ +#define CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_AND_LOCK_MEDIA_ACCESS_HANDLER_H_ #include "chrome/browser/media/media_access_handler.h" -// MediaAccessHandler for media requests coming from SAML login pages on -// ChromeOS. -class ChromeOSLoginMediaAccessHandler : public MediaAccessHandler { +// MediaAccessHandler for media requests coming from SAML IdP pages on the +// login/lock screen on ChromeOS. +class ChromeOSLoginAndLockMediaAccessHandler : public MediaAccessHandler { public: - ChromeOSLoginMediaAccessHandler(); - ~ChromeOSLoginMediaAccessHandler() override; + ChromeOSLoginAndLockMediaAccessHandler(); + ~ChromeOSLoginAndLockMediaAccessHandler() override; // MediaAccessHandler implementation. bool SupportsStreamType(content::WebContents* web_contents, @@ -29,4 +29,4 @@ const extensions::Extension* extension) override; }; -#endif // CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_MEDIA_ACCESS_HANDLER_H_ +#endif // CHROME_BROWSER_MEDIA_CHROMEOS_LOGIN_AND_LOCK_MEDIA_ACCESS_HANDLER_H_
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.cc b/chrome/browser/media/public_session_tab_capture_access_handler.cc deleted file mode 100644 index 0318e28..0000000 --- a/chrome/browser/media/public_session_tab_capture_access_handler.cc +++ /dev/null
@@ -1,89 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/media/public_session_tab_capture_access_handler.h" - -#include <utility> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "chrome/browser/chromeos/extensions/public_session_permission_helper.h" -#include "chrome/browser/profiles/profiles_state.h" -#include "chromeos/login/login_state/login_state.h" -#include "content/public/browser/web_contents.h" -#include "extensions/common/extension.h" -#include "extensions/common/permissions/manifest_permission_set.h" -#include "extensions/common/permissions/permission_set.h" -#include "extensions/common/url_pattern_set.h" - -PublicSessionTabCaptureAccessHandler::PublicSessionTabCaptureAccessHandler() {} - -PublicSessionTabCaptureAccessHandler::~PublicSessionTabCaptureAccessHandler() {} - -bool PublicSessionTabCaptureAccessHandler::SupportsStreamType( - content::WebContents* web_contents, - const blink::mojom::MediaStreamType type, - const extensions::Extension* extension) { - return tab_capture_access_handler_.SupportsStreamType(web_contents, type, - extension); -} - -bool PublicSessionTabCaptureAccessHandler::CheckMediaAccessPermission( - content::RenderFrameHost* render_frame_host, - const GURL& security_origin, - blink::mojom::MediaStreamType type, - const extensions::Extension* extension) { - return tab_capture_access_handler_.CheckMediaAccessPermission( - render_frame_host, security_origin, type, extension); -} - -void PublicSessionTabCaptureAccessHandler::HandleRequest( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback, - const extensions::Extension* extension) { - // This class handles requests for Public Sessions only, outside of them just - // pass the request through to the original class. - if (!profiles::ArePublicSessionRestrictionsEnabled() || !extension || - (request.audio_type != - blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE && - request.video_type != - blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE)) { - return tab_capture_access_handler_.HandleRequest( - web_contents, request, std::move(callback), extension); - } - - // This Unretained is safe because the lifetime of this object is until - // process exit (living inside a base::Singleton object). - auto prompt_resolved_callback = - base::BindOnce(&PublicSessionTabCaptureAccessHandler::ChainHandleRequest, - base::Unretained(this), web_contents, request, - std::move(callback), base::RetainedRef(extension)); - - extensions::permission_helper::HandlePermissionRequest( - *extension, {extensions::mojom::APIPermissionID::kTabCapture}, - web_contents, std::move(prompt_resolved_callback), - extensions::permission_helper::PromptFactory()); -} - -void PublicSessionTabCaptureAccessHandler::ChainHandleRequest( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback, - const extensions::Extension* extension, - const extensions::PermissionIDSet& allowed_permissions) { - content::MediaStreamRequest request_copy(request); - - // If the user denied tab capture, here the request gets filtered out before - // being passed on to the actual implementation. - if (!allowed_permissions.ContainsID( - extensions::mojom::APIPermissionID::kTabCapture)) { - request_copy.audio_type = blink::mojom::MediaStreamType::NO_SERVICE; - request_copy.video_type = blink::mojom::MediaStreamType::NO_SERVICE; - } - - // Pass the request through to the original class. - tab_capture_access_handler_.HandleRequest(web_contents, request_copy, - std::move(callback), extension); -}
diff --git a/chrome/browser/media/public_session_tab_capture_access_handler.h b/chrome/browser/media/public_session_tab_capture_access_handler.h deleted file mode 100644 index 27df402..0000000 --- a/chrome/browser/media/public_session_tab_capture_access_handler.h +++ /dev/null
@@ -1,64 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ -#define CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_ - -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/media/capture_access_handler_base.h" -#include "chrome/browser/media/webrtc/tab_capture_access_handler.h" -#include "extensions/common/extension_id.h" -#include "extensions/common/permissions/api_permission_set.h" -#include "third_party/blink/public/common/mediastream/media_stream_request.h" - -// MediaAccessHandler for TabCapture API in Public Sessions. This class is -// implemented as a wrapper around TabCaptureAccessHandler. It allows for finer -// access control to the TabCapture manifest permission feature inside of Public -// Sessions. -// -// In Public Sessions, extensions (and apps) are force-installed by admin policy -// so the user does not get a chance to review the permissions for these -// extensions. This is not acceptable from a security/privacy standpoint, so -// when an extension uses the TabCapture API for the first time, we show the -// user a dialog where they can choose whether to allow the extension access to -// the API. -class PublicSessionTabCaptureAccessHandler : public CaptureAccessHandlerBase { - public: - PublicSessionTabCaptureAccessHandler(); - - PublicSessionTabCaptureAccessHandler( - const PublicSessionTabCaptureAccessHandler&) = delete; - PublicSessionTabCaptureAccessHandler& operator=( - const PublicSessionTabCaptureAccessHandler&) = delete; - - ~PublicSessionTabCaptureAccessHandler() override; - - // MediaAccessHandler implementation. - bool SupportsStreamType(content::WebContents* web_contents, - const blink::mojom::MediaStreamType type, - const extensions::Extension* extension) override; - bool CheckMediaAccessPermission( - content::RenderFrameHost* render_frame_host, - const GURL& security_origin, - blink::mojom::MediaStreamType type, - const extensions::Extension* extension) override; - void HandleRequest(content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback, - const extensions::Extension* extension) override; - - private: - // Helper function used to chain the HandleRequest call into the original - // inside of TabCaptureAccessHandler. - void ChainHandleRequest( - content::WebContents* web_contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback, - const extensions::Extension* extension, - const extensions::PermissionIDSet& allowed_permissions); - - TabCaptureAccessHandler tab_capture_access_handler_; -}; - -#endif // CHROME_BROWSER_MEDIA_PUBLIC_SESSION_TAB_CAPTURE_ACCESS_HANDLER_H_
diff --git a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc index 64c0e25..35472c6 100644 --- a/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc +++ b/chrome/browser/media/webrtc/media_capture_devices_dispatcher.cc
@@ -46,9 +46,8 @@ #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) -#include "chrome/browser/media/chromeos_login_media_access_handler.h" +#include "chrome/browser/media/chromeos_login_and_lock_media_access_handler.h" #include "chrome/browser/media/public_session_media_access_handler.h" -#include "chrome/browser/media/public_session_tab_capture_access_handler.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(ENABLE_EXTENSIONS) @@ -93,7 +92,7 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #if BUILDFLAG(IS_CHROMEOS_ASH) media_access_handlers_.push_back( - std::make_unique<ChromeOSLoginMediaAccessHandler>()); + std::make_unique<ChromeOSLoginAndLockMediaAccessHandler>()); // Wrapper around ExtensionMediaAccessHandler used in Public Sessions. media_access_handlers_.push_back( std::make_unique<PublicSessionMediaAccessHandler>()); @@ -103,14 +102,8 @@ #endif media_access_handlers_.push_back( std::make_unique<DesktopCaptureAccessHandler>()); -#if BUILDFLAG(IS_CHROMEOS_ASH) - // Wrapper around TabCaptureAccessHandler used in Public Sessions. - media_access_handlers_.push_back( - std::make_unique<PublicSessionTabCaptureAccessHandler>()); -#else media_access_handlers_.push_back(std::make_unique<TabCaptureAccessHandler>()); #endif -#endif media_access_handlers_.push_back( std::make_unique<PermissionBubbleMediaAccessHandler>()); }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 30a326e..2627478 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -139,6 +139,7 @@ #include "chrome/browser/policy/browser_signin_policy_handler.h" #else #include "chrome/browser/policy/system_features_disable_list_policy_handler.h" +#include "chromeos/ui/wm/fullscreen/pref_names.h" #endif // !BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -937,9 +938,6 @@ { key::kFullscreenAlertEnabled, ash::prefs::kFullscreenAlertEnabled, base::Value::Type::BOOLEAN }, - { key::kKeepFullscreenWithoutNotificationUrlAllowList, - ash::prefs::kKeepFullscreenWithoutNotificationUrlAllowList, - base::Value::Type::LIST }, { key::kDeviceLoginScreenDefaultLargeCursorEnabled, nullptr, base::Value::Type::BOOLEAN }, @@ -1597,6 +1595,9 @@ { key::kEnableSyncConsent, prefs::kEnableSyncConsent, base::Value::Type::BOOLEAN }, + { key::kKeepFullscreenWithoutNotificationUrlAllowList, + chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList, + base::Value::Type::LIST }, { key::kRestrictedManagedGuestSessionExtensionCleanupExemptList, prefs::kRestrictedManagedGuestSessionExtensionCleanupExemptList, base::Value::Type::LIST },
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 08a3d7d..d797800 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -285,6 +285,7 @@ #include "chrome/browser/policy/networking/policy_cert_service.h" #include "chrome/browser/policy/system_features_disable_list_policy_handler.h" #include "chrome/browser/ui/webui/certificates_handler.h" +#include "chromeos/ui/wm/fullscreen/pref_names.h" #if defined(USE_CUPS) #include "chrome/browser/extensions/api/printing/printing_api_handler.h" #endif // defined(USE_CUPS) @@ -1422,6 +1423,9 @@ registry->RegisterBooleanPref(prefs::kInsightsExtensionEnabled, false); // By default showing Sync Consent is set to true. It can changed by policy. registry->RegisterBooleanPref(prefs::kEnableSyncConsent, true); + registry->RegisterListPref( + chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList, + PrefRegistry::PUBLIC); #if defined(USE_CUPS) extensions::PrintingAPIHandler::RegisterProfilePrefs(registry); #endif // defined(USE_CUPS)
diff --git a/chrome/browser/privacy_budget/privacy_budget_reid_score_estimator_unittest.cc b/chrome/browser/privacy_budget/privacy_budget_reid_score_estimator_unittest.cc index 53b728a..ed33a917 100644 --- a/chrome/browser/privacy_budget/privacy_budget_reid_score_estimator_unittest.cc +++ b/chrome/browser/privacy_budget/privacy_budget_reid_score_estimator_unittest.cc
@@ -20,7 +20,8 @@ #include "third_party/blink/public/common/privacy_budget/identifiable_token.h" #include "third_party/blink/public/common/privacy_budget/scoped_identifiability_test_sample_collector.h" -class PrivacyBudgetReidScoreEstimatorStandaloneTest : public ::testing::Test { +class PrivacyBudgetReidScoreEstimatorStandaloneTest + : public ::testing::TestWithParam<std::tuple<uint32_t, int, int, bool>> { public: PrivacyBudgetReidScoreEstimatorStandaloneTest() { prefs::RegisterPrivacyBudgetPrefs(pref_service_.registry()); @@ -30,6 +31,38 @@ void RunUntilIdle() { task_environment_.RunUntilIdle(); } + void ProcessReidRecords(IdentifiabilityStudyGroupSettings* settings, + bool fixedToken) { + for (int i = 0; i < kNumIterations; ++i) { + PrivacyBudgetReidScoreEstimator reid_storage(settings, pref_service()); + reid_storage.ResetPersistedState(); + reid_storage.Init(); + int64_t token1 = + fixedToken ? 1 : static_cast<int64_t>(base::RandUint64()); + int64_t token2 = + fixedToken ? 2 : static_cast<int64_t>(base::RandUint64()); + // Process values for 2 surfaces. + reid_storage.ProcessForReidScore(kSurface_1, + blink::IdentifiableToken(token1)); + reid_storage.ProcessForReidScore(kSurface_2, + blink::IdentifiableToken(token2)); + } + } + + // Example surfaces for testing. + const blink::IdentifiableSurface kSurface_1 = + blink::IdentifiableSurface::FromMetricHash(2077075229u); + const blink::IdentifiableSurface kSurface_2 = + blink::IdentifiableSurface::FromMetricHash(1122849309u); + + // Expected Reid surface key to be reported based on the example surfaces + // defined (kSurface_1, kSurface_2) using the function + // IdentifiableSurface::FromTypeAndToken() with type + // IdentifiableSurface::Type::kReidScoreEstimator. + const uint64_t kExpectedSurface = 11332616172707669541u; + + const int kNumIterations = 50; + private: TestingPrefServiceSimple pref_service_; base::test::SingleThreadTaskEnvironment task_environment_; @@ -56,8 +89,9 @@ reid_storage.Init(); } -TEST_F(PrivacyBudgetReidScoreEstimatorStandaloneTest, - ReportReidFixedTokenRandomSalt) { +TEST_P(PrivacyBudgetReidScoreEstimatorStandaloneTest, + ReportReidwithParameters) { + const auto [salt_ranges, bits, noise, fixed_token] = GetParam(); auto settings = IdentifiabilityStudyGroupSettings::InitFrom( /*enabled=*/true, /*expected_surface_count=*/0, @@ -66,177 +100,22 @@ /*blocks_weights=*/"", /*allowed_random_types=*/"", /*reid_blocks=*/"2077075229;1122849309", - /*reid_blocks_salts_ranges=*/"1000000", - /*reid_blocks_bits=*/"1", - /*reid_blocks_noise_probabilities=*/"0"); + /*reid_blocks_salts_ranges=*/base::NumberToString(salt_ranges), + /*reid_blocks_bits=*/base::NumberToString(bits), + /*reid_blocks_noise_probabilities=*/base::NumberToString(noise)); - constexpr auto surface_1 = - blink::IdentifiableSurface::FromMetricHash(2077075229u); - constexpr auto surface_2 = - blink::IdentifiableSurface::FromMetricHash(1122849309u); - - int64_t token1 = 1234; - int64_t token2 = 12345; - constexpr int num_iterations = 50; ukm::TestAutoSetUkmRecorder test_recorder; base::RunLoop run_loop; test_recorder.SetOnAddEntryCallback( ukm::builders::Identifiability::kEntryName, - base::BarrierClosure(num_iterations, run_loop.QuitClosure())); + base::BarrierClosure(kNumIterations, run_loop.QuitClosure())); blink::test::ScopedIdentifiabilityTestSampleCollector collector; - for (int i = 0; i < num_iterations; ++i) { - PrivacyBudgetReidScoreEstimator reid_storage(&settings, pref_service()); - reid_storage.ResetPersistedState(); - reid_storage.Init(); - // Process values for 2 surfaces. - reid_storage.ProcessForReidScore(surface_1, - blink::IdentifiableToken(token1)); - reid_storage.ProcessForReidScore(surface_2, - blink::IdentifiableToken(token2)); - } + ProcessReidRecords(&settings, fixed_token); // This should let the async tasks run. run_loop.Run(); const auto& entries = collector.entries(); bool has_value_0 = false; bool has_value_1 = false; - int count = 0; - for (auto& entry : entries) { - for (auto& metric : entry.metrics) { - auto surface = metric.surface; - if (surface.GetType() == - blink::IdentifiableSurface::Type::kReidScoreEstimator) { - EXPECT_EQ(metric.surface.ToUkmMetricHash(), 11332616172707669541u); - ++count; - uint64_t hash = static_cast<uint64_t>(metric.value.ToUkmMetricValue()); - uint32_t reid_bits = hash & 0xFFFFFFFF; - EXPECT_TRUE(reid_bits == 0 || reid_bits == 1); - if (reid_bits == 0) - has_value_0 = true; - else if (reid_bits == 1) - has_value_1 = true; - uint32_t salt = (hash >> 32); - EXPECT_LT(salt, 1000000u); - } - } - } - EXPECT_EQ(count, num_iterations); - // Since the 1 bit should be random, the probability of it being always 0 or - // always 1 is 2/(2^num_iterations), hence it should be negligible. - EXPECT_TRUE(has_value_0); - EXPECT_TRUE(has_value_1); -} - -TEST_F(PrivacyBudgetReidScoreEstimatorStandaloneTest, - ReportReidRandomTokenFixedSalt) { - auto settings = IdentifiabilityStudyGroupSettings::InitFrom( - /*enabled=*/true, - /*expected_surface_count=*/0, - /*surface_budget=*/0, - /*blocks=*/"", - /*blocks_weights=*/"", - /*allowed_random_types=*/"", - /*reid_blocks=*/"2077075229;1122849309", - /*reid_blocks_salts_ranges=*/"1", - /*reid_blocks_bits=*/"1", - /*reid_blocks_noise_probabilities=*/"0"); - - constexpr auto surface_1 = - blink::IdentifiableSurface::FromMetricHash(2077075229u); - constexpr auto surface_2 = - blink::IdentifiableSurface::FromMetricHash(1122849309u); - constexpr int num_iterations = 50; - ukm::TestAutoSetUkmRecorder test_recorder; - base::RunLoop run_loop; - test_recorder.SetOnAddEntryCallback( - ukm::builders::Identifiability::kEntryName, - base::BarrierClosure(num_iterations, run_loop.QuitClosure())); - blink::test::ScopedIdentifiabilityTestSampleCollector collector; - for (int i = 0; i < num_iterations; ++i) { - PrivacyBudgetReidScoreEstimator reid_storage(&settings, pref_service()); - reid_storage.ResetPersistedState(); - reid_storage.Init(); - // Create random tokens. - int64_t token1 = static_cast<int64_t>(base::RandUint64()); - int64_t token2 = static_cast<int64_t>(base::RandUint64()); - // Process values for 2 surfaces. - reid_storage.ProcessForReidScore(surface_1, - blink::IdentifiableToken(token1)); - reid_storage.ProcessForReidScore(surface_2, - blink::IdentifiableToken(token2)); - } - // This should let the async tasks run. - run_loop.Run(); - const auto& entries = collector.entries(); - bool has_value_0 = false; - bool has_value_1 = false; - int count = 0; - for (auto& entry : entries) { - for (auto& metric : entry.metrics) { - auto surface = metric.surface; - if (surface.GetType() == - blink::IdentifiableSurface::Type::kReidScoreEstimator) { - EXPECT_EQ(metric.surface.ToUkmMetricHash(), 11332616172707669541u); - ++count; - uint64_t hash = static_cast<uint64_t>(metric.value.ToUkmMetricValue()); - uint32_t reid_bits = hash & 0xFFFFFFFF; - EXPECT_TRUE(reid_bits == 0 || reid_bits == 1); - if (reid_bits == 0) - has_value_0 = true; - else if (reid_bits == 1) - has_value_1 = true; - uint32_t salt = (hash >> 32); - EXPECT_EQ(salt, 0u); - } - } - } - EXPECT_EQ(count, num_iterations); - // Since the 1 bit should be random, the probability of it being always 0 or - // always 1 is 2/(2^num_iterations), hence it should be negligible. - EXPECT_TRUE(has_value_0); - EXPECT_TRUE(has_value_1); -} - -TEST_F(PrivacyBudgetReidScoreEstimatorStandaloneTest, - ReportReidFixedTokenFixedSaltAllNoise) { - auto settings = IdentifiabilityStudyGroupSettings::InitFrom( - /*enabled=*/true, - /*expected_surface_count=*/0, - /*surface_budget=*/0, - /*blocks=*/"", - /*blocks_weights=*/"", - /*allowed_random_types=*/"", - /*reid_blocks=*/"2077075229;1122849309", - /*reid_blocks_salts_ranges=*/"1", - /*reid_blocks_bits=*/"32", - /*reid_blocks_noise_probabilities=*/"1"); - - constexpr auto surface_1 = - blink::IdentifiableSurface::FromMetricHash(2077075229u); - constexpr auto surface_2 = - blink::IdentifiableSurface::FromMetricHash(1122849309u); - - int64_t token1 = 1234; - int64_t token2 = 12345; - constexpr int num_iterations = 50; - ukm::TestAutoSetUkmRecorder test_recorder; - base::RunLoop run_loop; - test_recorder.SetOnAddEntryCallback( - ukm::builders::Identifiability::kEntryName, - base::BarrierClosure(num_iterations, run_loop.QuitClosure())); - blink::test::ScopedIdentifiabilityTestSampleCollector collector; - for (int i = 0; i < num_iterations; ++i) { - PrivacyBudgetReidScoreEstimator reid_storage(&settings, pref_service()); - reid_storage.ResetPersistedState(); - reid_storage.Init(); - // Process values for 2 surfaces. - reid_storage.ProcessForReidScore(surface_1, - blink::IdentifiableToken(token1)); - reid_storage.ProcessForReidScore(surface_2, - blink::IdentifiableToken(token2)); - } - // This should let the async tasks run. - run_loop.Run(); - const auto& entries = collector.entries(); base::flat_set<uint32_t> reid_results; int count = 0; for (auto& entry : entries) { @@ -244,21 +123,41 @@ auto surface = metric.surface; if (surface.GetType() == blink::IdentifiableSurface::Type::kReidScoreEstimator) { - EXPECT_EQ(metric.surface.ToUkmMetricHash(), 11332616172707669541u); + EXPECT_EQ(metric.surface.ToUkmMetricHash(), kExpectedSurface); ++count; uint64_t hash = static_cast<uint64_t>(metric.value.ToUkmMetricValue()); uint32_t reid_bits = hash & 0xFFFFFFFF; - // Result should be noise i.e. didn't appeared before. - EXPECT_FALSE(reid_results.contains(reid_bits)); - reid_results.insert(reid_bits); + if (noise == 1) { + // Result should be noise i.e. didn't appear before. + EXPECT_FALSE(reid_results.contains(reid_bits)); + reid_results.insert(reid_bits); + } else { + EXPECT_TRUE(reid_bits == 0 || reid_bits == 1); + if (reid_bits == 0) + has_value_0 = true; + else if (reid_bits == 1) + has_value_1 = true; + } uint32_t salt = (hash >> 32); - EXPECT_EQ(salt, 0u); + EXPECT_TRUE((salt >= 0) && (salt < salt_ranges)); } } } - EXPECT_EQ(count, num_iterations); + EXPECT_EQ(count, kNumIterations); + // Since the 1 bit should be random, the probability of it being always 0 or + // always 1 is 2/(2^kNumIterations), hence it should be negligible. + if (noise != 1) { + EXPECT_TRUE(has_value_0); + EXPECT_TRUE(has_value_1); + } } +INSTANTIATE_TEST_SUITE_P(PrivacyBudgetReidScoreEstimatorParameterizedTest, + PrivacyBudgetReidScoreEstimatorStandaloneTest, + ::testing::Values(std::make_tuple(1000000, 1, 0, true), + std::make_tuple(1, 1, 0, false), + std::make_tuple(1, 32, 1, true))); + TEST_F(PrivacyBudgetReidScoreEstimatorStandaloneTest, ReidHashIsReportedOnlyOnce) { auto settings = IdentifiabilityStudyGroupSettings::InitFrom( @@ -273,11 +172,6 @@ /*reid_blocks_bits=*/"1", /*reid_blocks_noise_probabilities=*/"0"); - constexpr auto surface_1 = - blink::IdentifiableSurface::FromMetricHash(2077075229u); - constexpr auto surface_2 = - blink::IdentifiableSurface::FromMetricHash(1122849309u); - ukm::TestAutoSetUkmRecorder test_recorder; { @@ -291,8 +185,8 @@ blink::test::ScopedIdentifiabilityTestSampleCollector collector; // Process values for 2 surfaces. - reid_storage.ProcessForReidScore(surface_1, blink::IdentifiableToken(1)); - reid_storage.ProcessForReidScore(surface_2, blink::IdentifiableToken(2)); + reid_storage.ProcessForReidScore(kSurface_1, blink::IdentifiableToken(1)); + reid_storage.ProcessForReidScore(kSurface_2, blink::IdentifiableToken(2)); // This should let the async tasks run. run_loop.Run(); @@ -301,7 +195,7 @@ EXPECT_EQ(entries.size(), 1u); EXPECT_EQ(entries[0].metrics.size(), 1u); EXPECT_EQ(entries[0].metrics[0].surface.ToUkmMetricHash(), - 11332616172707669541u); + kExpectedSurface); } // Now check that the reid hash is not reported again if we see again the @@ -309,8 +203,8 @@ { blink::test::ScopedIdentifiabilityTestSampleCollector collector; - reid_storage.ProcessForReidScore(surface_1, blink::IdentifiableToken(1)); - reid_storage.ProcessForReidScore(surface_2, blink::IdentifiableToken(2)); + reid_storage.ProcessForReidScore(kSurface_1, blink::IdentifiableToken(1)); + reid_storage.ProcessForReidScore(kSurface_2, blink::IdentifiableToken(2)); RunUntilIdle(); const auto& entries = collector.entries(); @@ -327,8 +221,8 @@ { blink::test::ScopedIdentifiabilityTestSampleCollector collector; - reid_storage.ProcessForReidScore(surface_1, blink::IdentifiableToken(1)); - reid_storage.ProcessForReidScore(surface_2, blink::IdentifiableToken(2)); + reid_storage.ProcessForReidScore(kSurface_1, blink::IdentifiableToken(1)); + reid_storage.ProcessForReidScore(kSurface_2, blink::IdentifiableToken(2)); RunUntilIdle(); @@ -347,8 +241,8 @@ ukm::builders::Identifiability::kEntryName, run_loop.QuitClosure()); blink::test::ScopedIdentifiabilityTestSampleCollector collector; - reid_storage.ProcessForReidScore(surface_1, blink::IdentifiableToken(1)); - reid_storage.ProcessForReidScore(surface_2, blink::IdentifiableToken(2)); + reid_storage.ProcessForReidScore(kSurface_1, blink::IdentifiableToken(1)); + reid_storage.ProcessForReidScore(kSurface_2, blink::IdentifiableToken(2)); run_loop.Run(); @@ -356,7 +250,7 @@ EXPECT_EQ(entries.size(), 1u); EXPECT_EQ(entries[0].metrics.size(), 1u); EXPECT_EQ(entries[0].metrics[0].surface.ToUkmMetricHash(), - 11332616172707669541u); + kExpectedSurface); } } }
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.cc b/chrome/browser/profiles/profile_shortcut_manager_win.cc index 11f19c4..741bbd13 100644 --- a/chrome/browser/profiles/profile_shortcut_manager_win.cc +++ b/chrome/browser/profiles/profile_shortcut_manager_win.cc
@@ -76,7 +76,7 @@ // Incrementing this number will cause profile icons to be regenerated on // profile startup (it should be incremented whenever the product/avatar icons // change, etc). -const int kCurrentProfileIconVersion = 9; +const int kCurrentProfileIconVersion = 10; bool disabled_for_unit_tests = false; bool disable_unpinning_for_unit_tests = false;
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc index de0d1a13..993582aa0 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.cc +++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -78,7 +78,7 @@ #if BUILDFLAG(IS_ANDROID) #include "base/android/jni_android.h" #include "chrome/android/chrome_jni_headers/PushMessagingServiceObserver_jni.h" -#include "chrome/browser/android/shortcut_helper.h" +#include "chrome/browser/installable/installed_webapp_bridge.h" #include "components/permissions/android/android_permission_util.h" #include "components/prefs/pref_service.h" #endif @@ -441,28 +441,23 @@ return false; } - bool has_app_level_notification_permission = - enabled_app_level_notification_permission_for_testing_.has_value() - ? enabled_app_level_notification_permission_for_testing_.value() - : permissions::AreAppLevelNotificationsEnabled(); + bool webapp_can_display_notifications = + InstalledWebappBridge::GetPermission(ContentSettingsType::NOTIFICATIONS, + app_identifier.origin()) == + ContentSetting::CONTENT_SETTING_ALLOW; - bool contains_webapk = ShortcutHelper::DoesOriginContainAnyInstalledWebApk( - app_identifier.origin()); - bool contains_twa = - ShortcutHelper::DoesOriginContainAnyInstalledTrustedWebActivity( - app_identifier.origin()); - bool contains_installed_webapp = contains_twa || contains_webapk; - - // If Notifications permission delegation is enabled, for the - // `app_identifier.origin()`, we should not revoke permissions because - // Notifications permissions are automatically synced with an installed app. - if (contains_installed_webapp) + // An incoming push message will be displayed by an installed webapp. + if (webapp_can_display_notifications) return false; PrefService* prefs = prefs_for_testing_.has_value() ? prefs_for_testing_.value() : g_browser_process->local_state(); + bool has_app_level_notification_permission = + enabled_app_level_notification_permission_for_testing_.value_or( + permissions::AreAppLevelNotificationsEnabled()); + if (has_app_level_notification_permission) { // Chrome has app-level Notifications permission. Reset the grace period // flag and continue as normal.
diff --git a/chrome/browser/reputation/local_heuristics.cc b/chrome/browser/reputation/local_heuristics.cc index 581267a..dd83ff8 100644 --- a/chrome/browser/reputation/local_heuristics.cc +++ b/chrome/browser/reputation/local_heuristics.cc
@@ -111,8 +111,9 @@ reputation::HeuristicLaunchConfig::HEURISTIC_CHARACTER_SWAP_TOP_SITES, navigated_domain.domain_and_registry, chrome::GetChannel()); case LookalikeUrlMatchType::kComboSquatting: + return true; case LookalikeUrlMatchType::kComboSquattingSiteEngagement: - return false; + return true; case LookalikeUrlMatchType::kNone: NOTREACHED(); }
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html index 19592bf..4270d7b 100644 --- a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html +++ b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.html
@@ -77,7 +77,7 @@ <div slot="title">[[title_]]</div> <div slot="body"> <cr-input id="numberInput" label="$i18n{creditCardNumber}" - value="{{creditCard.cardNumber}}" autofocus> + value="{{cardNumber_}}" autofocus> </cr-input> <!-- aria-hidden for creditCardExpiration label since creditCardExpirationMonth and creditCardExpirationYear provide @@ -104,15 +104,15 @@ <div id="expiredError">$i18n{creditCardExpired}</div> <!-- Place cardholder name field and nickname field after expiration.--> <cr-input id="nameInput" label="$i18n{creditCardName}" - value="{{creditCard.name}}" spellcheck="false"> + value="{{name_}}" spellcheck="false"> </cr-input> <cr-input id="nicknameInput" label="$i18n{creditCardNickname}" - value="{{creditCard.nickname}}" spellcheck="false" maxlength="25" + value="{{nickname_}}" spellcheck="false" maxlength="25" on-input="validateNickname_" invalid="[[nicknameInvalid_]]" error-message="$i18n{creditCardNicknameInvalid}"> <div id="charCount" slot="suffix"> - [[computeNicknameCharCount_(creditCard.nickname)]]/25 + [[computeNicknameCharCount_(nickname_)]]/25 </div> </cr-input> <div id="saved-to-this-device-only-label"> @@ -124,8 +124,8 @@ on-click="onCancelButtonTap_">$i18n{cancel}</cr-button> <cr-button id="saveButton" class="action-button" on-click="onSaveButtonTap_" - disabled="[[!saveEnabled_(nicknameInvalid_, creditCard.*, - expired_)]]"> + disabled="[[!saveEnabled_(nicknameInvalid_, + expired_, name_, cardNumber_, nickname_)]]"> $i18n{save} </cr-button> </div>
diff --git a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts index 1527c0e..7682fb9 100644 --- a/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts +++ b/chrome/browser/resources/settings/autofill_page/credit_card_edit_dialog.ts
@@ -100,6 +100,9 @@ /** The list of years to show in the dropdown. */ yearList_: Array, + name_: String, + cardNumber_: String, + nickname_: String, expirationYear_: String, expirationMonth_: String, @@ -122,6 +125,9 @@ private title_: string; private monthList_: string[]; private yearList_: string[]; + private name_?: string; + private cardNumber_?: string; + private nickname_?: string; private expirationYear_?: string; private expirationMonth_?: string; private nicknameInvalid_: boolean; @@ -179,6 +185,9 @@ microTask.run(() => { this.expirationYear_ = selectedYear.toString(); this.expirationMonth_ = this.creditCard.expirationMonth; + this.name_ = this.creditCard.name; + this.cardNumber_ = this.creditCard.cardNumber; + this.nickname_ = this.creditCard.nickname; this.$.dialog.showModal(); }); } @@ -205,6 +214,9 @@ this.creditCard.expirationYear = this.expirationYear_; this.creditCard.expirationMonth = this.expirationMonth_; + this.creditCard.name = this.name_; + this.creditCard.cardNumber = this.cardNumber_; + this.creditCard.nickname = this.nickname_; this.trimCreditCard_(); this.dispatchEvent(new CustomEvent( 'save-credit-card', @@ -222,12 +234,11 @@ private saveEnabled_() { // The save button is enabled if: - // There is and name or number for the card + // There is a name or number for the card // and the expiration date is valid // and the nickname is valid if present. - return ((this.creditCard.name && this.creditCard.name.trim()) || - (this.creditCard.cardNumber && - this.creditCard.cardNumber.trim())) && + return ((this.name_ && this.name_.trim()) || + (this.cardNumber_ && this.cardNumber_.trim())) && !this.expired_ && !this.nicknameInvalid_; } @@ -258,8 +269,7 @@ * the save button when invalid. */ private validateNickname_() { - this.nicknameInvalid_ = - NICKNAME_INVALID_REGEX.test(this.creditCard.nickname!); + this.nicknameInvalid_ = NICKNAME_INVALID_REGEX.test(this.nickname_!); } /**
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js index a43b907..cb3d5e6 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js +++ b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js
@@ -607,10 +607,10 @@ /* * Returns the add esim button. If the device does not have an EUICC, no eSIM * slot, or policies prohibit users from adding a network, null is returned. - * @return {?CrIconButtonElement} + * @return {?HTMLElement} */ getAddEsimButton() { - return /** @type {?CrIconButtonElement} */ ( + return /** @type {?HTMLElement} */ ( this.shadowRoot.querySelector('#addESimButton')); }
diff --git a/chrome/browser/ui/android/fast_checkout/BUILD.gn b/chrome/browser/ui/android/fast_checkout/BUILD.gn index ff8eda15..b2d0155 100644 --- a/chrome/browser/ui/android/fast_checkout/BUILD.gn +++ b/chrome/browser/ui/android/fast_checkout/BUILD.gn
@@ -26,3 +26,23 @@ "java/src/org/chromium/chrome/browser/ui/fast_checkout/data/FastCheckoutCreditCard.java", ] } + +robolectric_library("junit") { + testonly = true + + sources = [ "junit/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutControllerTest.java" ] + deps = [ + ":java", + "//base:base_junit_test_support", + "//chrome/browser/ui/android/fast_checkout/internal:java", + "//components/autofill/android:main_autofill_java", + "//components/autofill_assistant/android:public_java", + "//components/browser_ui/bottomsheet/android:java", + "//third_party/androidx:androidx_recyclerview_recyclerview_java", + "//third_party/hamcrest:hamcrest_library_java", + "//third_party/junit:junit", + "//third_party/mockito:mockito_java", + "//ui/android:ui_java_test_support", + "//ui/android:ui_no_recycler_view_java", + ] +}
diff --git a/chrome/browser/ui/android/fast_checkout/junit/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutControllerTest.java b/chrome/browser/ui/android/fast_checkout/junit/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutControllerTest.java new file mode 100644 index 0000000..a9c4ac8 --- /dev/null +++ b/chrome/browser/ui/android/fast_checkout/junit/java/src/org/chromium/chrome/browser/ui/fast_checkout/FastCheckoutControllerTest.java
@@ -0,0 +1,119 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.ui.fast_checkout; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutModel.CURRENT_SCREEN; +import static org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutModel.VISIBLE; + +import androidx.recyclerview.widget.RecyclerView; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.ui.fast_checkout.FastCheckoutModel.ScreenType; +import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutAutofillProfile; +import org.chromium.chrome.browser.ui.fast_checkout.data.FastCheckoutCreditCard; +import org.chromium.components.autofill.VirtualCardEnrollmentState; +import org.chromium.components.autofill_assistant.AutofillAssistantPublicTags; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; +import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; +import org.chromium.ui.modelutil.PropertyModel; + +/** + * Controller tests verify that the Fast Checkout controller modifies the model if the API is used + * properly. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class FastCheckoutControllerTest { + private static final FastCheckoutAutofillProfile[] DUMMY_PROFILES = { + createDummyProfile("John Doe", "john@gmail.com")}; + private static final FastCheckoutCreditCard[] DUMMY_CARDS = { + createDummyCreditCard("https://example.com", "4111111111111111")}; + + @Mock + RecyclerView mMockParentView; + @Mock + private FastCheckoutComponent.Delegate mMockDelegate; + @Mock + private BottomSheetContent mMockBottomSheetContent; + @Mock + private BottomSheetController mMockBottomSheetController; + + private FastCheckoutMediator mMediator = new FastCheckoutMediator(); + + private final PropertyModel mModel = FastCheckoutModel.createDefaultModel(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mMediator.initialize(mMockDelegate, mModel, mMockBottomSheetController); + } + + @Test + public void testCreatesValidDefaultModel() { + assertThat(mModel.get(VISIBLE), is(false)); + assertThat(mModel.get(CURRENT_SCREEN), is(ScreenType.HOME_SCREEN)); + } + + @Test + public void testShowOptionsSetsVisibile() { + mMediator.showOptions(DUMMY_PROFILES, DUMMY_CARDS); + + verify(mMockBottomSheetController, never()).hideContent(any(), anyBoolean()); + assertThat(mModel.get(VISIBLE), is(true)); + } + + @Test + public void testAssistantOnboardingGetsHiddenIfShowing() { + when(mMockParentView.getTag()) + .thenReturn( + AutofillAssistantPublicTags.AUTOFILL_ASSISTANT_BOTTOM_SHEET_CONTENT_TAG); + when(mMockBottomSheetContent.getContentView()).thenReturn(mMockParentView); + when(mMockBottomSheetController.getCurrentSheetContent()) + .thenReturn(mMockBottomSheetContent); + + mMediator.showOptions(DUMMY_PROFILES, DUMMY_CARDS); + + verify(mMockBottomSheetController).hideContent(any(), eq(true)); + } + + private static FastCheckoutAutofillProfile createDummyProfile(String name, String email) { + return new FastCheckoutAutofillProfile(/* guid= */ "", /* origin= */ "", + /* isLocal= */ true, + /* honorificPrefix= */ "", name, + /* companyName= */ "", /* streetAddress= */ "", /* region= */ "", + /* locality= */ "", + /* dependentLocality= */ "", /* postalCode= */ "", /* sortingCode= */ "", + /* countryCode= */ "", /* countryName= */ "", /* phoneNumber= */ "", email, + /* languageCode= */ "en-US"); + } + + private static FastCheckoutCreditCard createDummyCreditCard(String origin, String number) { + return new FastCheckoutCreditCard(/* guid= */ "john", origin, /* isLocal= */ true, + /* isCached= */ true, "John Doe", number, "1111", "12", "2050", "visa", + /* billingAddressId= */ + "", + /* billingAddressId= */ "john", + /* serverId= */ "", + /* instrumentId= */ 0, /* nickname= */ "", /* cardArtUrl= */ null, + /* virtualCardEnrollmentState= */ VirtualCardEnrollmentState.UNSPECIFIED, + /* productDescription= */ ""); + } +}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc index b70e755f..6c9c130 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.cc +++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -21,6 +21,9 @@ #include "chrome/browser/ash/arc/arc_util.h" #include "chrome/browser/ash/arc/session/arc_session_manager.h" #include "chrome/browser/ash/assistant/assistant_util.h" +#include "chrome/browser/ash/crosapi/crosapi_ash.h" +#include "chrome/browser/ash/crosapi/crosapi_manager.h" +#include "chrome/browser/ash/crosapi/fullscreen_controller_ash.h" #include "chrome/browser/ash/file_manager/path_util.h" #include "chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.h" #include "chrome/browser/ash/profiles/profile_helper.h" @@ -358,3 +361,11 @@ std::string ChromeShellDelegate::GetVersionString() { return version_info::GetVersionNumber(); } + +void ChromeShellDelegate::ShouldExitFullscreenBeforeLock( + ChromeShellDelegate::ShouldExitFullscreenCallback callback) { + crosapi::CrosapiManager::Get() + ->crosapi_ash() + ->fullscreen_controller_ash() + ->ShouldExitFullscreenBeforeLock(std::move(callback)); +}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h index 56ee35b..db4519b 100644 --- a/chrome/browser/ui/ash/chrome_shell_delegate.h +++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -67,6 +67,8 @@ void ForceSkipWarningUserOnClose( const std::vector<aura::Window*>& windows) override; std::string GetVersionString() override; + void ShouldExitFullscreenBeforeLock( + ShouldExitFullscreenCallback callback) override; }; #endif // CHROME_BROWSER_UI_ASH_CHROME_SHELL_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc index 18d230a7..72400cd 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -126,8 +126,7 @@ } void ProjectorClientImpl::OpenProjectorApp() const { - auto* profile = ProfileManager::GetActiveUserProfile(); - ash::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::PROJECTOR); + LaunchProjectorAppWithFiles(/*files=*/{}); } void ProjectorClientImpl::MinimizeProjectorApp() const {
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc index b1c97d2d..3ff4956c 100644 --- a/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc +++ b/chrome/browser/ui/ash/projector/projector_client_impl_browsertest.cc
@@ -14,6 +14,7 @@ #include "ash/webui/projector_app/public/cpp/projector_app_constants.h" #include "base/bind.h" #include "base/callback_forward.h" +#include "base/files/file_path.h" #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h" @@ -27,8 +28,10 @@ #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/projector/projector_app_client_impl.h" +#include "chrome/browser/ui/ash/projector/projector_utils.h" #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app_id.h" @@ -198,6 +201,66 @@ content::PAGE_TYPE_NORMAL); } +// This test covers launching the Projector app with files for the first time. +IN_PROC_BROWSER_TEST_F(ProjectorClientTest, LaunchProjectorAppWithFiles) { + auto* profile = browser()->profile(); + SystemWebAppManager::GetForTest(profile)->InstallSystemAppsForTesting(); + + base::FilePath file1("test1"), file2("test2"); + LaunchProjectorAppWithFiles({file1, file2}); + FlushSystemWebAppLaunchesForTesting(profile); + + // Verify that Projector App is opened. + Browser* app_browser = + FindSystemWebAppBrowser(profile, SystemWebAppType::PROJECTOR); + ASSERT_TRUE(app_browser); + content::WebContents* tab = + app_browser->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(tab); + EXPECT_EQ(tab->GetController().GetVisibleEntry()->GetPageType(), + content::PAGE_TYPE_NORMAL); +} + +// This test covers launching the Projector app with files when the app is +// already open. The launch event should recycle the existing window and should +// not open a new window. +IN_PROC_BROWSER_TEST_F(ProjectorClientTest, + LaunchProjectorAppWithFilesWhenAppAlreadyOpen) { + const size_t starting_browser_count = chrome::GetTotalBrowserCount(); + + auto* profile = browser()->profile(); + SystemWebAppManager::GetForTest(profile)->InstallSystemAppsForTesting(); + + // Launch the app for the first time. + client()->OpenProjectorApp(); + FlushSystemWebAppLaunchesForTesting(profile); + + // Verify that Projector App is opened. + Browser* app_browser1 = + FindSystemWebAppBrowser(profile, SystemWebAppType::PROJECTOR); + ASSERT_TRUE(app_browser1); + EXPECT_EQ(chrome::GetTotalBrowserCount(), starting_browser_count + 1); + + base::FilePath file1("test1"), file2("test2"); + // Launch the app again with files. This operation should recycle the same + // window. + LaunchProjectorAppWithFiles({file1, file2}); + FlushSystemWebAppLaunchesForTesting(profile); + + // Verify that the Projector App is still open. + Browser* app_browser2 = + FindSystemWebAppBrowser(profile, SystemWebAppType::PROJECTOR); + // Launching the app with files should not open a new window. + EXPECT_EQ(app_browser1, app_browser2); + EXPECT_EQ(chrome::GetTotalBrowserCount(), starting_browser_count + 1); + + content::WebContents* tab = + app_browser2->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(tab); + EXPECT_EQ(tab->GetController().GetVisibleEntry()->GetPageType(), + content::PAGE_TYPE_NORMAL); +} + IN_PROC_BROWSER_TEST_F(ProjectorClientTest, MinimizeProjectorApp) { auto* profile = browser()->profile(); SystemWebAppManager::GetForTest(profile)->InstallSystemAppsForTesting();
diff --git a/chrome/browser/ui/ash/projector/projector_utils.cc b/chrome/browser/ui/ash/projector/projector_utils.cc index 4310463..44b4156 100644 --- a/chrome/browser/ui/ash/projector/projector_utils.cc +++ b/chrome/browser/ui/ash/projector/projector_utils.cc
@@ -6,10 +6,13 @@ #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "base/files/file_path.h" #include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" #include "components/prefs/pref_service.h" namespace { @@ -56,3 +59,11 @@ (ash::features::IsProjectorManagedUserIgnorePolicyEnabled() || profile->GetPrefs()->GetBoolean(ash::prefs::kProjectorAllowByPolicy)); } + +void LaunchProjectorAppWithFiles(std::vector<base::FilePath> files) { + auto* profile = ProfileManager::GetActiveUserProfile(); + ash::SystemAppLaunchParams params; + params.launch_paths = std::move(files); + ash::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::PROJECTOR, + params); +}
diff --git a/chrome/browser/ui/ash/projector/projector_utils.h b/chrome/browser/ui/ash/projector/projector_utils.h index dc20ffb..28b4d1a 100644 --- a/chrome/browser/ui/ash/projector/projector_utils.h +++ b/chrome/browser/ui/ash/projector/projector_utils.h
@@ -5,6 +5,12 @@ #ifndef CHROME_BROWSER_UI_ASH_PROJECTOR_PROJECTOR_UTILS_H_ #define CHROME_BROWSER_UI_ASH_PROJECTOR_PROJECTOR_UTILS_H_ +#include <vector> + +namespace base { +class FilePath; +} // namespace base + class Profile; // Returns whether Projector is allowed for given `profile`. @@ -13,4 +19,8 @@ // Returns whether the Projector app is enabled. bool IsProjectorAppEnabled(const Profile* profile); +// Launches the Projector SWA with the specified files. If the app is already +// open, then reuse the existing window. +void LaunchProjectorAppWithFiles(std::vector<base::FilePath> files); + #endif // CHROME_BROWSER_UI_ASH_PROJECTOR_PROJECTOR_UTILS_H_
diff --git a/chrome/browser/ui/ash/projector/screencast_manager.cc b/chrome/browser/ui/ash/projector/screencast_manager.cc index f5926da3..42f7fbf44 100644 --- a/chrome/browser/ui/ash/projector/screencast_manager.cc +++ b/chrome/browser/ui/ash/projector/screencast_manager.cc
@@ -14,6 +14,7 @@ #include "base/strings/stringprintf.h" #include "chrome/browser/ash/drive/drive_integration_service.h" #include "chrome/browser/ui/ash/projector/projector_drivefs_provider.h" +#include "chrome/browser/ui/ash/projector/projector_utils.h" namespace ash { @@ -51,7 +52,12 @@ return; } - // TODO(b/237089852): Launch the file. + const base::FilePath& mounted_path = + ProjectorDriveFsProvider::GetDriveFsMountPointPath(); + const base::FilePath& video_path = mounted_path.Append(relative_drivefs_path); + // TODO(b/205334821): Find the video duration and validate the media file. + LaunchProjectorAppWithFiles({video_path}); + std::move(callback).Run(std::move(video), /*error_message=*/std::string()); } @@ -66,10 +72,8 @@ ProjectorAppClient::OnGetVideoCallback callback) const { auto video = std::make_unique<ProjectorScreencastVideo>(); video->file_id = video_file_id; - // TODO(b/237089852): - // 1. Find the video duration. - // 2. Launch the app with the video file. - // 3. Handle the resource key once LocateFilesByItemIds() supports it. + // TODO(b/237089852): Handle the resource key once LocateFilesByItemIds() + // supports it. drive::DriveIntegrationService* integration_service = ProjectorDriveFsProvider::GetActiveDriveIntegrationService();
diff --git a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc index a433fa3..fa07691 100644 --- a/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc +++ b/chrome/browser/ui/ash/shelf/browser_app_shelf_controller_browsertest.cc
@@ -22,20 +22,18 @@ #include "chrome/browser/apps/app_service/browser_app_instance.h" #include "chrome/browser/apps/app_service/browser_app_instance_observer.h" #include "chrome/browser/apps/app_service/browser_app_instance_registry.h" +#include "chrome/browser/ash/crosapi/ash_requires_lacros_browsertestbase.h" #include "chrome/browser/ash/crosapi/browser_manager.h" -#include "chrome/browser/ash/crosapi/crosapi_manager.h" -#include "chrome/browser/ash/crosapi/test_controller_ash.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h" #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/web_applications/test/app_registration_waiter.h" #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/browser/web_applications/web_app_install_info.h" -#include "chrome/test/base/chromeos/ash_browser_test_starter.h" -#include "chrome/test/base/in_process_browser_test.h" #include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" #include "components/app_constants/constants.h" #include "components/services/app_service/public/cpp/app_registry_cache.h" @@ -170,7 +168,8 @@ return os << i.command_id << ", " << i.title; } -class BrowserAppShelfControllerBrowserTest : public InProcessBrowserTest { +class BrowserAppShelfControllerBrowserTest + : public crosapi::AshRequiresLacrosBrowserTestBase { public: BrowserAppShelfControllerBrowserTest() { scoped_feature_list_.InitAndEnableFeature(ash::features::kLacrosPrimary); @@ -193,23 +192,15 @@ GURL(start_url)); } - void SetUpInProcessBrowserTestFixture() override { - if (!ash_starter_.HasLacrosArgument()) { - return; - } - ASSERT_TRUE(ash_starter_.PrepareEnvironmentForLacros()); - } - void SetUpOnMainThread() override { - if (!ash_starter_.HasLacrosArgument()) { + crosapi::AshRequiresLacrosBrowserTestBase::SetUpOnMainThread(); + if (!HasLacrosArgument()) { return; } - auto* manager = crosapi::CrosapiManager::Get(); - test_controller_ash_ = std::make_unique<crosapi::TestControllerAsh>(); - manager->crosapi_ash()->SetTestControllerForTesting( - test_controller_ash_.get()); - ash_starter_.StartLacros(this); + web_app::AppTypeInitializationWaiter(browser()->profile(), + apps::AppType::kWeb) + .Await(); auto* registry = AppServiceProxy()->BrowserAppInstanceRegistry(); ASSERT_NE(registry, nullptr); @@ -232,15 +223,17 @@ std::string InstallWebApp(const std::string& start_url, apps::WindowMode mode) { crosapi::mojom::StandaloneBrowserTestControllerAsyncWaiter waiter( - test_controller_ash_->GetStandaloneBrowserTestController().get()); + GetStandaloneBrowserTestController()); std::string app_id; waiter.InstallWebApp(start_url, mode, &app_id); // Wait until the app is installed: app service publisher updates may arrive // out of order with the web app installation reply, so we wait until the // state of the app service is consistent. - WAIT_FOR(AppServiceProxy()->AppRegistryCache().GetAppType(app_id) == - apps::AppType::kWeb); + web_app::AppRegistrationWaiter(browser()->profile(), app_id).Await(); + EXPECT_EQ(AppServiceProxy()->AppRegistryCache().GetAppType(app_id), + apps::AppType::kWeb); + return app_id; } @@ -321,14 +314,11 @@ } base::test::ScopedFeatureList scoped_feature_list_; - test::AshBrowserTestStarter ash_starter_; apps::BrowserAppInstanceRegistry* registry_{nullptr}; - - std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_; }; IN_PROC_BROWSER_TEST_F(BrowserAppShelfControllerBrowserTest, TabbedApps) { - if (!ash_starter_.HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; } @@ -442,7 +432,7 @@ } IN_PROC_BROWSER_TEST_F(BrowserAppShelfControllerBrowserTest, WindowedApps) { - if (!ash_starter_.HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; } @@ -538,7 +528,7 @@ IN_PROC_BROWSER_TEST_F(BrowserAppShelfControllerBrowserTest, ActivateAndMinimizeTabs) { - if (!ash_starter_.HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; } @@ -617,7 +607,7 @@ IN_PROC_BROWSER_TEST_F(BrowserAppShelfControllerBrowserTest, ActivateAndMinimizeWindows) { - if (!ash_starter_.HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; } @@ -670,7 +660,7 @@ IN_PROC_BROWSER_TEST_F(BrowserAppShelfControllerBrowserTest, MultipleInstancesShowMenu) { - if (!ash_starter_.HasLacrosArgument()) { + if (!HasLacrosArgument()) { return; }
diff --git a/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.cc index ac08746..0cfa9d7 100644 --- a/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.cc +++ b/chrome/browser/ui/ash/shelf/standalone_browser_extension_app_shelf_item_controller.cc
@@ -41,9 +41,9 @@ activation_client_observation_.Observe(activation_client); // Lacros is mutually exclusive with multi-signin. As such, there can only be - // a single ash profile active. We grab it from the shelf. + // a single ash profile active. We grab it from the profile manager. apps::AppServiceProxy* proxy = apps::AppServiceProxyFactory::GetForProfile( - ChromeShelfController::instance()->profile()); + ProfileManager::GetPrimaryUserProfile()); icon_loader_releaser_ = proxy->LoadIconFromIconKey( apps::AppType::kStandaloneBrowserChromeApp, shelf_id.app_id,
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc index 0a9a2acd..03b1fc72 100644 --- a/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc +++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_item.cc
@@ -177,19 +177,20 @@ return; // If we have metadata from the media session, use that. - if (metadata_.has_value()) { - view_->UpdateWithMediaMetadata(*metadata_); - return; - } + media_session::MediaMetadata data = + metadata_.value_or(media_session::MediaMetadata{}); auto* web_contents = GetWebContentsFromPresentationRequest(request_); DCHECK(web_contents); - - media_session::MediaMetadata data; + // `request_` has more accurate origin info than `metadata_` e.g. when the + // request is from within an iframe. data.source_title = url_formatter::FormatOriginForSecurityDisplay( request_.frame_origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); - data.artist = web_contents->GetTitle(); - + // If not empty, then `metadata_.artist` is likely to contain information + // more relevant than the page title. + if (data.artist.empty()) { + data.artist = web_contents->GetTitle(); + } view_->UpdateWithMediaMetadata(data); }
diff --git a/chrome/browser/ui/global_media_controls/presentation_request_notification_item_unittest.cc b/chrome/browser/ui/global_media_controls/presentation_request_notification_item_unittest.cc index 7f6e479..a7fc488 100644 --- a/chrome/browser/ui/global_media_controls/presentation_request_notification_item_unittest.cc +++ b/chrome/browser/ui/global_media_controls/presentation_request_notification_item_unittest.cc
@@ -141,7 +141,9 @@ const std::u16string title = u"This is the page title"; web_contents()->UpdateTitleForEntry(controller().GetVisibleEntry(), title); - // The item should prioritize Media Session metadata. + // The item should prioritize Media Session metadata except for + // `source_title`, which should come from the Presentation Request. + data.source_title = u"google2.com"; EXPECT_CALL(view, UpdateWithMediaMetadata(data)); item->SetView(&view);
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu.cc b/chrome/browser/ui/toolbar/media_router_contextual_menu.cc index 26c5b3a0..ffdb6fb 100644 --- a/chrome/browser/ui/toolbar/media_router_contextual_menu.cc +++ b/chrome/browser/ui/toolbar/media_router_contextual_menu.cc
@@ -77,8 +77,7 @@ menu_model->AddCheckItemWithStringId(IDC_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING, IDS_MEDIA_ROUTER_TOGGLE_MEDIA_REMOTING); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) - if (!browser_->profile()->IsOffTheRecord() && - browser_->profile()->GetPrefs()->GetBoolean( + if (browser_->profile()->GetPrefs()->GetBoolean( prefs::kUserFeedbackAllowed)) { menu_model->AddSeparator(ui::NORMAL_SEPARATOR); menu_model->AddItemWithStringId(
diff --git a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc index 9eb66dc..b03ff349 100644 --- a/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc +++ b/chrome/browser/ui/toolbar/media_router_contextual_menu_unittest.cc
@@ -161,7 +161,10 @@ #if BUILDFLAG(GOOGLE_CHROME_BRANDING) // "Report an issue" should be present for normal profiles but not for // incognito. -TEST_F(MediaRouterContextualMenuUnitTest, EnableAndDisableReportIssue) { +// +// Disabled <https://crbug.com/1351616>. +TEST_F(MediaRouterContextualMenuUnitTest, + DISABLED_EnableAndDisableReportIssue) { MediaRouterContextualMenu menu(browser(), kShownByPolicy, &observer_); EXPECT_TRUE( menu.CreateMenuModel()
diff --git a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_contextual_menu.cc b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_contextual_menu.cc index f093a6f..68b2431 100644 --- a/chrome/browser/ui/views/global_media_controls/media_toolbar_button_contextual_menu.cc +++ b/chrome/browser/ui/views/global_media_controls/media_toolbar_button_contextual_menu.cc
@@ -50,8 +50,7 @@ IDS_MEDIA_TOOLBAR_CONTEXT_SHOW_OTHER_SESSIONS); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) - if (!browser_->profile()->IsOffTheRecord() && - browser_->profile()->GetPrefs()->GetBoolean( + if (browser_->profile()->GetPrefs()->GetBoolean( prefs::kUserFeedbackAllowed)) { menu_model->AddItemWithStringId( IDC_MEDIA_TOOLBAR_CONTEXT_REPORT_CAST_ISSUE,
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc index 32def367..fb38b53 100644 --- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -1321,32 +1321,115 @@ CheckHeuristicsUkmRecord({kNavigatedUrl, {true, false, false}}, 1); } -// Test that a Safety Tips is not shown and metrics are recorded when +// Test that a Safety Tip is shown and metrics are recorded when // a combo squatting url is flagged with a hard-coded brand name. +// This test case trigger `keyword` heuristic as well because of `google` +// in the URL. +// TODO(crbug.com/1343630): keyword (embedded keyword) heuristic should +// be removed from the code including CheckHeuristicsUkmRecord. IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, - DoesntTriggerOnComboSquatting) { + TriggerOnComboSquatting) { base::HistogramTester histograms; const GURL kNavigatedUrl = GetURL("google-login.com"); SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); - EXPECT_FALSE(IsUIShowing()); histograms.ExpectTotalCount(lookalikes::kHistogramName, 1); histograms.ExpectBucketCount(lookalikes::kHistogramName, NavigationSuggestionEvent::kComboSquatting, 1); - // TODO(crbug.com/1343630): keyword (embedded keyword) heuristic should - // be removed from the code. The second `false` value in - // the input of CheckHeuristicsUkmRecord is correlated to this heuristic. + // Make sure that the UI is now showing, and that no UKM data has been + // recorded yet. + ASSERT_TRUE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(0); + + // Once we close the warning, ensure that the UI is no longer showing, and + // that UKM data has now been recorded. + CloseWarningLeaveSite(browser()); + ASSERT_FALSE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(1); - CheckHeuristicsUkmRecord({kNavigatedUrl, {false, false, true}}, 0); + // Boolean values are /*blocklist*/ /*lookalike*/ /*keywords*/ + // This test case expects triggering with lookalike heuristic and + // keywords heuristic. + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, true}}, 0); + + // Navigate to the same site again, but close the warning with an ignore + // instead of an accept. This should still record UKM data. + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + + ASSERT_TRUE(IsUIShowing()); + + // Make sure the already collected UKM data still exists. + CheckRecordedHeuristicsUkmCount(1); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, true}}, 0); + + CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked); + ASSERT_FALSE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(2); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, true}}, 0); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, true}}, 1); } -// Test that a Safety Tips is not shown and metrics are recorded when -// a combo squatting url is flagged with a brand name from engaged sites. +// Test that a Safety Tip is shown and metrics are recorded when +// a combo squatting url is flagged with a hard-coded brand name. +// In contrast with `TriggerOnComboSquatting`, this test case only +// triggers `lookalike` heuristic. IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, - DoesntTriggerOnComboSquattingSiteEngagement) { + TriggerOnlyOnComboSquatting) { + base::HistogramTester histograms; + const GURL kNavigatedUrl = GetURL("costco-login.com"); + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + + histograms.ExpectTotalCount(lookalikes::kHistogramName, 1); + histograms.ExpectBucketCount(lookalikes::kHistogramName, + NavigationSuggestionEvent::kComboSquatting, 1); + + // Make sure that the UI is now showing, and that no UKM data has been + // recorded yet. + ASSERT_TRUE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(0); + + // Once we close the warning, ensure that the UI is no longer showing, and + // that UKM data has now been recorded. + CloseWarningLeaveSite(browser()); + ASSERT_FALSE(IsUIShowing()); + + CheckRecordedHeuristicsUkmCount(1); + // Boolean values are /*blocklist*/ /*lookalike*/ /*keywords*/ + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + + // Navigate to the same site again, but close the warning with an ignore + // instead of an accept. This should still record UKM data. + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + + ASSERT_TRUE(IsUIShowing()); + + // Make sure the already collected UKM data still exists. + CheckRecordedHeuristicsUkmCount(1); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + + CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked); + ASSERT_FALSE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(2); + // Boolean values are /*blocklist*/ /*lookalike*/ /*keywords*/ + // The last `false` is different from the previous test because + // `keywords heuristic` is not triggered by this test case. + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 1); + // TODO(crbug.com/1343630): keyword (embedded keyword) heuristic should + // be removed from the code including CheckHeuristicsUkmRecord. +} + +// Test that a Safety Tip is shown and metrics are recorded when +// a combo squatting url is flagged with a brand name from engaged sites. +// In this test case, engaged site is not one of the keywords in `keyword` +// heuristic. +IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewBrowserTest, + TriggerOnComboSquattingSiteEngagement) { base::HistogramTester histograms; const GURL kEngagedUrl = GetURL("example.com"); const GURL kNavigatedUrl = GetURL("example-login.com"); @@ -1354,17 +1437,41 @@ SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); - EXPECT_FALSE(IsUIShowing()); histograms.ExpectTotalCount(lookalikes::kHistogramName, 1); histograms.ExpectBucketCount( lookalikes::kHistogramName, NavigationSuggestionEvent::kComboSquattingSiteEngagement, 1); - // Heuristics with no UI don't record Safety Tips UKM. + // Make sure that the UI is now showing, and that no UKM data has been + // recorded yet. + ASSERT_TRUE(IsUIShowing()); CheckRecordedHeuristicsUkmCount(0); - // TODO(crbug.com/1337475): Add CheckHeuristicsUkmRecord after showing the - // safety tip for this heuristic. + + // Once we close the warning, ensure that the UI is no longer showing, and + // that UKM data has now been recorded. + CloseWarningLeaveSite(browser()); + ASSERT_FALSE(IsUIShowing()); + + CheckRecordedHeuristicsUkmCount(1); + // Boolean values are /*blocklist*/ /*lookalike*/ /*keywords*/ + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + + // Navigate to the same site again, but close the warning with an ignore + // instead of an accept. This should still record UKM data. + NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB); + + ASSERT_TRUE(IsUIShowing()); + + // Make sure the already collected UKM data still exists. + CheckRecordedHeuristicsUkmCount(1); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + + CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked); + ASSERT_FALSE(IsUIShowing()); + CheckRecordedHeuristicsUkmCount(2); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 0); + CheckHeuristicsUkmRecord({kNavigatedUrl, {false, true, false}}, 1); } // Tests for Digital Asset Links for lookalike checks.
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc index 157c1527..f432611 100644 --- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc +++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
@@ -106,10 +106,6 @@ return absl::nullopt; } -gfx::Rect FakeTabSlotController::GetTabAnimationTargetBounds(const Tab* tab) { - return tab->bounds(); -} - std::u16string FakeTabSlotController::GetAccessibleTabName( const Tab* tab) const { return std::u16string();
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h index 7a5c364..82761af2 100644 --- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h +++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
@@ -82,7 +82,6 @@ SkColor GetTabForegroundColor(TabActive active) const override; absl::optional<int> GetCustomBackgroundId( BrowserFrameActiveState active_state) const override; - gfx::Rect GetTabAnimationTargetBounds(const Tab* tab) override; std::u16string GetAccessibleTabName(const Tab* tab) const override; float GetHoverOpacityForTab(float range_parameter) const override; float GetHoverOpacityForRadialHighlight() const override;
diff --git a/chrome/browser/ui/views/tabs/tab_container.h b/chrome/browser/ui/views/tabs/tab_container.h index a7063da3..4c83fe7c 100644 --- a/chrome/browser/ui/views/tabs/tab_container.h +++ b/chrome/browser/ui/views/tabs/tab_container.h
@@ -18,7 +18,6 @@ #include "ui/views/view_model.h" class TabStrip; -class TabStripLayoutHelper; // A View that contains a sequence of Tabs for the TabStrip. @@ -43,6 +42,9 @@ virtual void MoveTab(int from_model_index, int to_model_index) = 0; virtual void RemoveTab(int index, bool was_active) = 0; virtual void SetTabPinned(int model_index, TabPinned pinned) = 0; + // Changes the active tab from |prev_active_index| to |new_active_index|. + virtual void SetActiveTab(absl::optional<size_t> prev_active_index, + absl::optional<size_t> new_active_index) = 0; // `view` is no longer being dragged. This TabContainer takes ownership of it // in the view hierarchy. @@ -67,16 +69,13 @@ virtual void NotifyTabGroupEditorBubbleClosed() = 0; virtual int GetModelIndexOf(const TabSlotView* slot_view) const = 0; - - // TODO (1346023): Move callers down into TabContainer so this - // encapsulation-breaking getter can be removed. - virtual views::ViewModelT<Tab>* GetTabsViewModel() = 0; - - // Returns the tab at `index` from the viewmodel. virtual Tab* GetTabAtModelIndex(int index) const = 0; - virtual int GetTabCount() const = 0; + // Returns the model index of the first tab after (or including) `tab` which + // is not closing. + virtual int GetModelIndexOfFirstNonClosingTab(Tab* tab) const = 0; + virtual void UpdateHoverCard( Tab* tab, TabSlotController::HoverCardUpdateType update_type) = 0; @@ -130,12 +129,22 @@ virtual bool InTabClose() = 0; - // TODO (1346023): Move callers down into TabContainer so these - // encapsulation-breaking getters can be removed. - virtual TabStripLayoutHelper* GetLayoutHelper() const = 0; virtual std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>& GetGroupViews() = 0; - virtual views::BoundsAnimator& GetBoundsAnimator() = 0; + + // Returns the current width of the active tab. + virtual int GetActiveTabWidth() const = 0; + + // Returns the current width of inactive tabs. + virtual int GetInactiveTabWidth() const = 0; + + // Returns ideal bounds for the tab at `model_index` in this TabContainer's + // coordinate space. + virtual gfx::Rect GetIdealBounds(int model_index) const = 0; + + // Returns ideal bounds for the group header associated with `group` in this + // TabContainer's coordinate space. + virtual gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const = 0; // Views::View: // We're changing visibility of this to public for TabContainer.
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc index 12d6993..da794a9 100644 --- a/chrome/browser/ui/views/tabs/tab_container_impl.cc +++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -184,9 +184,9 @@ SetEventTargeter(std::make_unique<views::ViewTargeter>(this)); if (!gfx::Animation::ShouldRenderRichAnimation()) - GetBoundsAnimator().SetAnimationDuration(base::TimeDelta()); + bounds_animator_.SetAnimationDuration(base::TimeDelta()); - GetBoundsAnimator().AddObserver(this); + bounds_animator_.AddObserver(this); if (g_drop_indicator_width == 0) { // Direction doesn't matter, both images are the same size. @@ -279,6 +279,11 @@ } } +void TabContainerImpl::SetActiveTab(absl::optional<size_t> prev_active_index, + absl::optional<size_t> new_active_index) { + layout_helper_->SetActiveTab(prev_active_index, new_active_index); +} + void TabContainerImpl::StoppedDraggingView(TabSlotView* view) { AddChildView(view); Tab* tab = views::AsViewClass<Tab>(view); @@ -439,10 +444,6 @@ return index.has_value() ? static_cast<int>(index.value()) : -1; } -views::ViewModelT<Tab>* TabContainerImpl::GetTabsViewModel() { - return &tabs_view_model_; -} - Tab* TabContainerImpl::GetTabAtModelIndex(int index) const { return tabs_view_model_.view_at(index); } @@ -451,6 +452,25 @@ return tabs_view_model_.view_size(); } +int TabContainerImpl::GetModelIndexOfFirstNonClosingTab(Tab* tab) const { + if (tab->closing()) { + // If the tab is already closing, close the next tab. We do this so that the + // user can rapidly close tabs by clicking the close button and not have + // the animations interfere with that. + std::vector<Tab*> all_tabs = layout_helper_->GetTabs(); + auto it = std::find(all_tabs.begin(), all_tabs.end(), tab); + while (it < all_tabs.end() && (*it)->closing()) { + it++; + } + + if (it == all_tabs.end()) + return TabStripModel::kNoTab; + tab = *it; + } + + return GetModelIndexOf(tab); +} + void TabContainerImpl::UpdateHoverCard( Tab* tab, TabSlotController::HoverCardUpdateType update_type) { @@ -607,17 +627,25 @@ return in_tab_close_; } -TabStripLayoutHelper* TabContainerImpl::GetLayoutHelper() const { - return layout_helper_.get(); -} - std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>& TabContainerImpl::GetGroupViews() { return group_views_; } -views::BoundsAnimator& TabContainerImpl::GetBoundsAnimator() { - return bounds_animator_; +int TabContainerImpl::GetActiveTabWidth() const { + return layout_helper_->active_tab_width(); +} + +int TabContainerImpl::GetInactiveTabWidth() const { + return layout_helper_->inactive_tab_width(); +} + +gfx::Rect TabContainerImpl::GetIdealBounds(int model_index) const { + return tabs_view_model_.ideal_bounds(model_index); +} + +gfx::Rect TabContainerImpl::GetIdealBounds(tab_groups::TabGroupId group) const { + return layout_helper_->group_header_ideal_bounds().at(group); } void TabContainerImpl::Layout() { @@ -894,6 +922,10 @@ arrow_window_ = nullptr; } +views::ViewModelT<Tab>* TabContainerImpl::GetTabsViewModel() { + return &tabs_view_model_; +} + void TabContainerImpl::UpdateIdealBounds() { if (GetTabCount() == 0) return; // Should only happen during creation/destruction, ignore.
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.h b/chrome/browser/ui/views/tabs/tab_container_impl.h index 5f37ca9..408a2a4 100644 --- a/chrome/browser/ui/views/tabs/tab_container_impl.h +++ b/chrome/browser/ui/views/tabs/tab_container_impl.h
@@ -57,6 +57,8 @@ void MoveTab(int from_model_index, int to_model_index) override; void RemoveTab(int index, bool was_active) override; void SetTabPinned(int model_index, TabPinned pinned) override; + void SetActiveTab(absl::optional<size_t> prev_active_index, + absl::optional<size_t> new_active_index) override; void StoppedDraggingView(TabSlotView* view) override; @@ -73,12 +75,9 @@ void NotifyTabGroupEditorBubbleClosed() override; int GetModelIndexOf(const TabSlotView* slot_view) const override; - - views::ViewModelT<Tab>* GetTabsViewModel() override; - Tab* GetTabAtModelIndex(int index) const override; - int GetTabCount() const override; + int GetModelIndexOfFirstNonClosingTab(Tab* tab) const override; void UpdateHoverCard( Tab* tab, @@ -108,12 +107,14 @@ bool InTabClose() override; - TabStripLayoutHelper* GetLayoutHelper() const override; - std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>& GetGroupViews() override; - views::BoundsAnimator& GetBoundsAnimator() override; + int GetActiveTabWidth() const override; + int GetInactiveTabWidth() const override; + + gfx::Rect GetIdealBounds(int model_index) const override; + gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override; // views::View void Layout() override; @@ -185,6 +186,8 @@ class RemoveTabDelegate; + views::ViewModelT<Tab>* GetTabsViewModel(); + // Generates and sets the ideal bounds for each of the tabs as well as the new // tab button. Note: Does not animate the tabs to those bounds so callers can // use this information for other purposes - see AnimateToIdealBounds.
diff --git a/chrome/browser/ui/views/tabs/tab_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_container_unittest.cc index c73b2a0c..f130c6a 100644 --- a/chrome/browser/ui/views/tabs/tab_container_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_container_unittest.cc
@@ -189,8 +189,8 @@ tab_strip_controller_->RemoveTabFromGroup(model_index); bool group_is_empty = true; - for (Tab* tab : tab_container_->GetLayoutHelper()->GetTabs()) { - if (tab->group() == old_group) + for (int i = 0; i < tab_container_->GetTabCount(); i++) { + if (tab_container_->GetTabAtModelIndex(i)->group() == old_group) group_is_empty = false; } @@ -304,8 +304,7 @@ // Create just enough tabs so tabs are not full size. const int standard_width = TabStyleViews::GetStandardWidth(); - while (tab_container_->GetLayoutHelper()->active_tab_width() == - standard_width) { + while (tab_container_->GetActiveTabWidth() == standard_width) { AddTab(0); tab_container_->CompleteAnimationAndLayout(); } @@ -322,15 +321,13 @@ // constraining tab widths to below full size. tab_container_->RemoveTab(tab_container_->GetTabCount() - 2, false); tab_container_->CompleteAnimationAndLayout(); - ASSERT_LT(tab_container_->GetLayoutHelper()->active_tab_width(), - standard_width); + ASSERT_LT(tab_container_->GetActiveTabWidth(), standard_width); // Close the last tab; tab closing mode should allow tabs to resize to full // size. tab_container_->RemoveTab(tab_container_->GetTabCount() - 1, false); tab_container_->CompleteAnimationAndLayout(); - EXPECT_EQ(tab_container_->GetLayoutHelper()->active_tab_width(), - standard_width); + EXPECT_EQ(tab_container_->GetActiveTabWidth(), standard_width); } // Verifies child view order matches model order.
diff --git a/chrome/browser/ui/views/tabs/tab_slot_controller.h b/chrome/browser/ui/views/tabs/tab_slot_controller.h index 0daeedd..3dc42d8c 100644 --- a/chrome/browser/ui/views/tabs/tab_slot_controller.h +++ b/chrome/browser/ui/views/tabs/tab_slot_controller.h
@@ -21,7 +21,6 @@ namespace gfx { class Point; -class Rect; } // namespace gfx namespace tab_groups { enum class TabGroupColorId; @@ -196,11 +195,6 @@ virtual absl::optional<int> GetCustomBackgroundId( BrowserFrameActiveState active_state) const = 0; - // If the given tab is animating to its target destination, this returns the - // target bounds. If the tab isn't moving this will return the current bounds - // of the given tab. - virtual gfx::Rect GetTabAnimationTargetBounds(const Tab* tab) = 0; - // Returns the accessible tab name for this tab. virtual std::u16string GetAccessibleTabName(const Tab* tab) const = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 7325943..940b036 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -318,7 +318,7 @@ } int GetPinnedTabCount() const override { - return tab_strip_->GetPinnedTabCount(); + return tab_strip_->GetModelPinnedTabCount(); } TabGroupHeader* GetTabGroupHeader( @@ -456,7 +456,8 @@ } if (!index) { - const int last_tab_right = ideal_bounds(GetTabCount() - 1).right(); + const int last_tab_right = + tab_strip_->tab_container_->GetIdealBounds(GetTabCount() - 1).right(); index = (dragged_bounds.right() > last_tab_right) ? GetTabCount() : 0; } @@ -562,10 +563,11 @@ tab_strip_->tab_container_->GetGroupViews()[header->group().value()] ->highlight() ->SetVisible(false); - ideal_bounds = tab_strip_->ideal_bounds(header->group().value()); - } else { ideal_bounds = - tab_strip_->ideal_bounds(tab_strip_->GetModelIndexOf(view)); + tab_strip_->tab_container_->GetIdealBounds(header->group().value()); + } else { + ideal_bounds = tab_strip_->tab_container_->GetIdealBounds( + tab_strip_->GetModelIndexOf(view)); } bounds_animator_.AnimateViewTo( @@ -683,12 +685,6 @@ const raw_ref<TabSlotView> slot_view_; }; - gfx::Rect ideal_bounds(int i) const { return tab_strip_->ideal_bounds(i); } - - gfx::Rect ideal_bounds(tab_groups::TabGroupId group) const { - return tab_strip_->ideal_bounds(group); - } - // Determines the index to move the dragged tabs to. The dragged tabs must // already be in the tabstrip. |dragged_bounds| is the union of the bounds // of the dragged tabs and group header, if any. |first_dragged_tab_index| is @@ -807,7 +803,8 @@ const int tab_overlap = TabStyle::GetTabOverlap(); // We'll insert just right of the tab at |candidate_index| - 1. - int ideal_x = ideal_bounds(candidate_index - 1).right(); + int ideal_x = + tab_strip_->tab_container_->GetIdealBounds(candidate_index - 1).right(); // If the dragged tabs are currently left of |candidate_index|, moving // them to |candidate_index| would move the tab at |candidate_index| - 1 @@ -850,19 +847,6 @@ return 0; } - // Sets the ideal bounds x-coordinates to |positions|. - void SetIdealBoundsFromPositions(const std::vector<int>& positions) { - if (static_cast<size_t>(GetTabCount()) != positions.size()) - return; - - for (int i = 0; i < GetTabCount(); ++i) { - gfx::Rect bounds(ideal_bounds(i)); - bounds.set_x(positions[i]); - tab_strip_->tab_container_->GetTabsViewModel()->set_ideal_bounds(i, - bounds); - } - } - const raw_ptr<TabStrip> tab_strip_; // Responsible for animating tabs during drag sessions. @@ -1155,8 +1139,8 @@ : CloseTabSource::CLOSE_TAB_FROM_TOUCH; tab_container_->EnterTabClosingMode( - ideal_bounds(GetModelCount() - 1).right() - current_group_width + - collapsed_group_width, + tab_container_->GetIdealBounds(GetModelCount() - 1).right() - + current_group_width + collapsed_group_width, source); } else { tab_container_->ExitTabClosingMode(); @@ -1235,8 +1219,8 @@ } new_active_tab->ActiveStateChanged(); - tab_container_->GetLayoutHelper()->SetActiveTab(selected_tabs_.active(), - new_selection.active()); + tab_container_->SetActiveTab(selected_tabs_.active(), + new_selection.active()); if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) { tab_container_->ScrollTabToVisible(new_selection.active().value()); } @@ -1318,12 +1302,18 @@ return controller_->GetCount(); } -TabDragContext* TabStrip::GetDragContext() { - return &*drag_context_; +int TabStrip::GetModelPinnedTabCount() const { + for (size_t i = 0; i < static_cast<size_t>(controller_->GetCount()); ++i) { + if (!controller_->IsTabPinned(static_cast<int>(i))) + return static_cast<int>(i); + } + + // All tabs are pinned. + return controller_->GetCount(); } -int TabStrip::GetPinnedTabCount() const { - return tab_container_->GetLayoutHelper()->GetPinnedTabCount(); +TabDragContext* TabStrip::GetDragContext() { + return &*drag_context_; } bool TabStrip::IsAnimating() const { @@ -1449,22 +1439,10 @@ } void TabStrip::CloseTab(Tab* tab, CloseTabSource source) { - if (tab->closing()) { - // If the tab is already closing, close the next tab. We do this so that the - // user can rapidly close tabs by clicking the close button and not have - // the animations interfere with that. - std::vector<Tab*> all_tabs = tab_container_->GetLayoutHelper()->GetTabs(); - auto it = std::find(all_tabs.begin(), all_tabs.end(), tab); - while (it < all_tabs.end() && (*it)->closing()) { - it++; - } + int index_to_close = tab_container_->GetModelIndexOfFirstNonClosingTab(tab); - if (it == all_tabs.end()) - return; - tab = *it; - } - - CloseTabInternal(GetModelIndexOf(tab), source); + if (IsValidModelIndex(index_to_close)) + CloseTabInternal(index_to_close, source); } void TabStrip::ToggleTabAudioMute(Tab* tab) { @@ -1758,10 +1736,6 @@ : absl::nullopt; } -gfx::Rect TabStrip::GetTabAnimationTargetBounds(const Tab* tab) { - return tab_container_->GetBoundsAnimator().GetTargetBounds(tab); -} - float TabStrip::GetHoverOpacityForTab(float range_parameter) const { return gfx::Tween::FloatValueBetween(range_parameter, hover_opacity_min_, hover_opacity_max_); @@ -1962,11 +1936,11 @@ } int TabStrip::GetActiveTabWidth() const { - return tab_container_->GetLayoutHelper()->active_tab_width(); + return tab_container_->GetActiveTabWidth(); } int TabStrip::GetInactiveTabWidth() const { - return tab_container_->GetLayoutHelper()->inactive_tab_width(); + return tab_container_->GetInactiveTabWidth(); } const Tab* TabStrip::GetLastVisibleTab() const { @@ -2185,11 +2159,6 @@ parent_->ShowContextMenuForTab(tab, point, source_type); } -const gfx::Rect& TabStrip::ideal_bounds(tab_groups::TabGroupId group) const { - return tab_container_->GetLayoutHelper()->group_header_ideal_bounds().at( - group); -} - void TabStrip::OnMouseEntered(const ui::MouseEvent& event) { mouse_entered_tabstrip_time_ = base::TimeTicks::Now(); } @@ -2231,8 +2200,11 @@ } void TabStrip::OnViewFocused(views::View* observed_view) { - auto index = - tab_container_->GetTabsViewModel()->GetIndexOfView(observed_view); + TabSlotView* slot_view = views::AsViewClass<TabSlotView>(observed_view); + if (!slot_view) + return; + + absl::optional<int> index = GetModelIndexOf(slot_view); if (index.has_value()) controller_->OnKeyboardFocusedTabChanged(index); } @@ -2275,7 +2247,7 @@ ADD_PROPERTY_METADATA(int, BackgroundOffset) ADD_READONLY_PROPERTY_METADATA(int, TabCount) ADD_READONLY_PROPERTY_METADATA(int, ModelCount) -ADD_READONLY_PROPERTY_METADATA(int, PinnedTabCount) +ADD_READONLY_PROPERTY_METADATA(int, ModelPinnedTabCount) ADD_READONLY_PROPERTY_METADATA(absl::optional<int>, FocusedTabIndex) ADD_READONLY_PROPERTY_METADATA(int, StrokeThickness) ADD_READONLY_PROPERTY_METADATA(SkColor,
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index baf088f0..7a9effd 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -206,13 +206,13 @@ // Cover method for TabStripController::GetCount. int GetModelCount() const; + // Returns the number of pinned tabs. + int GetModelPinnedTabCount() const; + TabStripController* controller() const { return controller_.get(); } TabDragContext* GetDragContext(); - // Returns the number of pinned tabs. - int GetPinnedTabCount() const; - // Returns true if Tabs in this TabStrip are currently changing size or // position. bool IsAnimating() const; @@ -307,7 +307,6 @@ std::u16string GetAccessibleTabName(const Tab* tab) const override; absl::optional<int> GetCustomBackgroundId( BrowserFrameActiveState active_state) const override; - gfx::Rect GetTabAnimationTargetBounds(const Tab* tab) override; float GetHoverOpacityForTab(float range_parameter) const override; float GetHoverOpacityForRadialHighlight() const override; std::u16string GetGroupTitle( @@ -409,14 +408,6 @@ // |offset| and moves it if possible. void ShiftGroupRelative(const tab_groups::TabGroupId& group, int offset); - // Retrieves the ideal bounds for the Tab at the specified index. - const gfx::Rect& ideal_bounds(int tab_data_index) const { - return tab_container_->GetTabsViewModel()->ideal_bounds(tab_data_index); - } - - // Retrieves the ideal bounds for the Tab Group Header at the specified group. - const gfx::Rect& ideal_bounds(tab_groups::TabGroupId group) const; - // views::View: void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc index b812a68..dc4c3af 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -172,10 +172,6 @@ tab_strip_parent_->Layout(); } - views::BoundsAnimator* bounds_animator() { - return &tab_strip_->tab_container_->GetBoundsAnimator(); - } - int GetActiveTabWidth() { return tab_strip_->GetActiveTabWidth(); } int GetInactiveTabWidth() { return tab_strip_->GetInactiveTabWidth(); }
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc index 385ed3e7..0499d76e 100644 --- a/chrome/browser/ui/views/tabs/tab_style_views.cc +++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -94,12 +94,6 @@ float GetHoverInterpolatedSeparatorOpacity(bool for_layout, const Tab* other_tab) const; - // Helper that returns an interpolated opacity if the tab is - // mid-bounds-animation. Used only for the first and last tabs, since those - // are the primary cases where separator opacity is likely to change during - // a bounds animation. - float GetBoundsInterpolatedSeparatorOpacity() const; - // Returns whether we shoould extend the hit test region for Fitts' Law. bool ShouldExtendHitTest() const; @@ -668,10 +662,6 @@ // sufficient contrast against the empty gap, so this contingency isn't // needed. Therefore, the separator is hidden only for tabs with visible // backgrounds. - // TODO(crbug.com/876599): This value should be interpolated because the - // separator may be going from shown (the default) to hidden (when animating - // past an empty gap like this). This should behave similarly to - // GetBoundsInterpolatedSeparatorOpacity(), but not just for the end slots. if (adjacent_tab->IsSelected()) return 0.0f; } @@ -686,11 +676,11 @@ } // If the tab does not have a visible background and is in the first slot, - // make sure the opacity is interpolated correctly when it animates into - // position, since the separator is likely going from shown (the default) to - // hidden (in the first slot). See GetBoundsInterpolatedSeparatorOpacity(). + // do not show the separator. This once was interpolated based on the tab's + // progress through animating into this slot, but that was removed because the + // visual impact was minimal and if (!adjacent_tab && leading) - return GetBoundsInterpolatedSeparatorOpacity(); + return 0.0f; return GetHoverInterpolatedSeparatorOpacity(for_layout, adjacent_tab); } @@ -713,22 +703,6 @@ return 1.0f - std::max(hover_value, adjacent_hover_value(other_tab)); } -float GM2TabStyle::GetBoundsInterpolatedSeparatorOpacity() const { - // When the bounds of a tab are animating, fade the separator based on how - // close to the target bounds this tab is. This function is only called - // when the target bounds are an end slot. That means this function will fade - // the separators in or out as a tab animtes into the end slot, but it will - // not be called if the tab is animating out of the end slot. In that case, - // the separator will snap to full opacity immediately, which is visually - // consistent with other bounds animations. - const gfx::Rect target_bounds = - tab_->controller()->GetTabAnimationTargetBounds(tab_); - const int tab_width = std::max(tab_->width(), target_bounds.width()); - return static_cast<float>( - std::min(std::abs(tab_->x() - target_bounds.x()), tab_width)) / - tab_width; -} - bool GM2TabStyle::ShouldExtendHitTest() const { const views::Widget* widget = tab_->GetWidget(); return widget->IsMaximized() || widget->IsFullscreen();
diff --git a/chrome/browser/ui/webui/BUILD.gn b/chrome/browser/ui/webui/BUILD.gn index 683e979d..d8d8de83 100644 --- a/chrome/browser/ui/webui/BUILD.gn +++ b/chrome/browser/ui/webui/BUILD.gn
@@ -55,6 +55,7 @@ "//ash/webui/file_manager:file_manager_untrusted_ui", "//ash/webui/help_app_ui", "//ash/webui/os_feedback_ui", + "//ash/webui/shortcut_customization_ui", "//chrome/browser/ash", ] if (!is_official_build) {
diff --git a/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc index b3b591f..f3155a4 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_configs_chromeos.cc
@@ -8,6 +8,7 @@ #include "content/public/browser/webui_config_map.h" #if BUILDFLAG(IS_CHROMEOS_ASH) +#include "ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h" #include "chrome/browser/ui/webui/chromeos/notification_tester/notification_tester_ui.h" #if !defined(OFFICIAL_BUILD) #include "ash/webui/facial_ml_app_ui/facial_ml_app_ui.h" @@ -20,6 +21,7 @@ void RegisterAshChromeWebUIConfigs() { // Add `WebUIConfig`s for Ash ChromeOS to the list here. auto& map = content::WebUIConfigMap::GetInstance(); + map.AddWebUIConfig(std::make_unique<ash::ShortcutCustomizationAppUIConfig>()); map.AddWebUIConfig(std::make_unique<chromeos::NotificationTesterUIConfig>()); #if !defined(OFFICIAL_BUILD) map.AddWebUIConfig(std::make_unique<ash::FacialMLAppUIConfig>());
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 7f93fed8..4db427035 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -209,8 +209,6 @@ #include "ash/webui/scanning/url_constants.h" #include "ash/webui/shimless_rma/shimless_rma.h" #include "ash/webui/shimless_rma/url_constants.h" -#include "ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h" -#include "ash/webui/shortcut_customization_ui/url_constants.h" #include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h" #include "ash/webui/system_extensions_internals_ui/url_constants.h" #include "base/system/sys_info.h" @@ -1028,10 +1026,6 @@ } if (url.host_piece() == ash::kChromeUIMediaAppHost) return &NewComponentUI<ash::MediaAppUI, ChromeMediaAppUIDelegate>; - if (features::IsShortcutCustomizationAppEnabled()) { - if (url.host_piece() == ash::kChromeUIShortcutCustomizationAppHost) - return &NewWebUI<ash::ShortcutCustomizationAppUI>; - } if (ash::features::IsFirmwareUpdaterAppEnabled()) { if (url.host_piece() == ash::kChromeUIFirmwareUpdateAppHost) return &NewWebUI<ash::FirmwareUpdateAppUI>;
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc index fde8484f..b697c60 100644 --- a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc +++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/webui/chromeos/in_session_password_change/base_lock_dialog.h" @@ -110,6 +111,24 @@ return CalculateOobeDialogSizeForPrimaryDisplay(); } +void LockScreenStartReauthDialog::RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { + // This is required for accessing the camera for SAML logins. + MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( + web_contents, request, std::move(callback), nullptr /* extension */); +} + +bool LockScreenStartReauthDialog::CheckMediaAccessPermission( + content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type) { + // This is required for accessing the camera for SAML logins. + return MediaCaptureDevicesDispatcher::GetInstance() + ->CheckMediaAccessPermission(render_frame_host, security_origin, type); +} + void LockScreenStartReauthDialog::Show() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (g_dialog) { @@ -161,6 +180,13 @@ return ret.width(); } +content::WebContents* LockScreenStartReauthDialog::GetWebContents() { + auto* web_ui = webui(); + if (!web_ui) + return nullptr; + return web_ui->GetWebContents(); +} + void LockScreenStartReauthDialog::DeleteLockScreenNetworkDialog() { if (!lock_screen_network_dialog_) return;
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.h b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.h index 9a8b0dd..d6e0d72 100644 --- a/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.h +++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/lock_screen_reauth_dialogs.h
@@ -35,10 +35,20 @@ LockScreenStartReauthDialog(LockScreenStartReauthDialog const&) = delete; ~LockScreenStartReauthDialog() override; + // content::WebDialogDelegate + void RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) override; + bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type) override; + void Show(); void Dismiss(); bool IsRunning(); int GetDialogWidth(); + content::WebContents* GetWebContents(); void DismissLockScreenNetworkDialog(); void DismissLockScreenCaptivePortalDialog();
diff --git a/chrome/browser/web_applications/web_app_install_utils.cc b/chrome/browser/web_applications/web_app_install_utils.cc index 402dfa1..066a9ff 100644 --- a/chrome/browser/web_applications/web_app_install_utils.cc +++ b/chrome/browser/web_applications/web_app_install_utils.cc
@@ -639,7 +639,7 @@ return urls; } -base::flat_set<GURL> RemoveDuplicates(const std::vector<GURL>& from_urls) { +base::flat_set<GURL> RemoveDuplicates(std::vector<GURL> from_urls) { return base::flat_set<GURL>{from_urls}; } @@ -659,7 +659,7 @@ RemoveInvalidUrls(std::ref(icon_urls)); - return RemoveDuplicates(icon_urls); + return RemoveDuplicates(std::move(icon_urls)); } void PopulateOtherIcons(WebAppInstallInfo* web_app_info,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 326dbf47..ae5a3ee4 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1660067998-d456c0c64daec27616a3a3491db0e29da7596b93.profdata +chrome-linux-main-1660111194-a2b8e0cbf26d1488c0e26d9c85572b713074f36e.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index ed7d5ac2..fa14328 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1660067998-752bed295d7eb2a9aa79bdd97f2eed52f0d5dcdd.profdata +chrome-mac-arm-main-1660111194-ac9093402317d5f777aad19b3f93f538de4d2f3e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 28431d4b..2ed6596 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1660046387-7c16af46336a6a1585ab4e4e5314fabde162f260.profdata +chrome-mac-main-1660111194-76c5298cbd7526455c48ad3e6b0489fac667fec5.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index b0bc0a0..56707d9 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1660057155-ee57f3d67dea7e308389127d554c2feddf31a266.profdata +chrome-win32-main-1660111194-fb87dd62de881d79520e85254af5218303b865fa.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index aa907439..f299d78 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1660057155-6e68224f2ea3eb2b7348e58fb8ab7d66520b9d7e.profdata +chrome-win64-main-1660111194-805e2cb9acd79076d09b76e83fde174352ab823e.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 86855bf1..06419f3 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -538,6 +538,9 @@ const base::Feature kHappinessTrackingPersonalizationWallpaper{ "HappinessTrackingPersonalizationWallpaper", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the Happiness Tracking System for Media App PDF survey. +const base::Feature kHappinessTrackingMediaAppPdf{ + "HappinessTrackingMediaAppPdf", base::FEATURE_DISABLED_BY_DEFAULT}; #endif // Hides the origin text from showing up briefly in WebApp windows.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 01f228e..7462850 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -378,6 +378,9 @@ COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kHappinessTrackingPersonalizationWallpaper; +COMPONENT_EXPORT(CHROME_FEATURES) +extern const base::Feature kHappinessTrackingMediaAppPdf; + #endif COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index eb28697..167ade6 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -870,6 +870,15 @@ const char kHatsPersonalizationWallpaperSurveyIsSelected[] = "hats_personalization_wallpaper_is_selected"; +// An int64 pref. This is the timestamp, microseconds after epoch, that +// indicates the end of the most recent Media App PDF survey cycle. +const char kHatsMediaAppPdfCycleEndTs[] = + "hats_media_app_pdf_cycle_end_timestamp"; + +// A boolean pref. Indicates if the device is selected for the Media App PDF +// survey. +const char kHatsMediaAppPdfIsSelected[] = "hats_media_app_pdf_is_selected"; + // A boolean pref. Indicates if we've already shown a notification to inform the // current user about the quick unlock feature. const char kPinUnlockFeatureNotificationShown[] =
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 4d8afaaa..fbfd94e3 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -302,6 +302,8 @@ extern const char kHatsPersonalizationScreensaverSurveyIsSelected[]; extern const char kHatsPersonalizationWallpaperSurveyCycleEndTs[]; extern const char kHatsPersonalizationWallpaperSurveyIsSelected[]; +extern const char kHatsMediaAppPdfCycleEndTs[]; +extern const char kHatsMediaAppPdfIsSelected[]; extern const char kEolStatus[]; extern const char kEndOfLifeDate[]; extern const char kEolNotificationDismissed[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 90df675a..1d3ecf9 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -7462,6 +7462,7 @@ "../browser/lacros/account_manager/signin_helper_lacros_unittest.cc", "../browser/lacros/cert/client_cert_store_lacros_unittest.cc", "../browser/lacros/force_installed_tracker_lacros_unittest.cc", + "../browser/lacros/fullscreen_controller_client_lacros_unittest.cc", "../browser/lacros/lacros_memory_pressure_evaluator_unittest.cc", "../browser/lacros/lacros_url_handling_unittest.cc", "../browser/lacros/metrics_reporting_observer_unittest.cc", @@ -7490,6 +7491,7 @@ "//chromeos/lacros:test_support", "//chromeos/startup", "//chromeos/ui/base", + "//chromeos/ui/wm", "//components/account_manager_core", "//components/account_manager_core:test_support", ]
diff --git a/chrome/test/base/test_chrome_web_ui_controller_factory.cc b/chrome/test/base/test_chrome_web_ui_controller_factory.cc index c6c6391..1247c84 100644 --- a/chrome/test/base/test_chrome_web_ui_controller_factory.cc +++ b/chrome/test/base/test_chrome_web_ui_controller_factory.cc
@@ -64,11 +64,9 @@ provider ? provider->NewWebUI(web_ui, webui_url) : ChromeWebUIControllerFactory::CreateWebUIControllerForURL( web_ui, webui_url); + // Add an empty callback since managed-footnote always sends this message. web_ui->RegisterMessageCallback("observeManagedUI", base::DoNothing()); - content::URLDataSource::Add(profile, - std::make_unique<TestDataSource>("webui")); - content::WebUIDataSource* source = webui::CreateWebUITestDataSource(); if (provider) provider->DataSourceOverrides(source);
diff --git a/chrome/test/base/web_ui_browser_test.cc b/chrome/test/base/web_ui_browser_test.cc index a94ffef..0c75e446 100644 --- a/chrome/test/base/web_ui_browser_test.cc +++ b/chrome/test/base/web_ui_browser_test.cc
@@ -30,6 +30,7 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/webui/test_data_source.h" #include "chrome/browser/ui/webui/web_ui_test_handler.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/url_constants.h" @@ -519,6 +520,17 @@ logging::SetLogMessageHandler(&LogHandler); + // Register URLDataSource that serves files used in tests at chrome://test/ + // e.g. `chrome://test/mocha.js`. + // + // For tests that run on the login screen, there is no Browser during + // SetUpOnMainThread() so skip adding TestDataSource. These tests don't need + // TestDataSource anyway. + if (browser()) { + content::URLDataSource::Add(browser()->profile(), + std::make_unique<TestDataSource>("webui")); + } + test_factory_ = std::make_unique<TestChromeWebUIControllerFactory>(); factory_registration_ = std::make_unique<content::ScopedWebUIControllerFactoryRegistration>(
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 15defc2..3d9d415 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -8004,13 +8004,14 @@ }, "KeepFullscreenWithoutNotificationUrlAllowList": { "os": [ - "chromeos_ash" + "chromeos_ash", + "chromeos_lacros" ], "policy_pref_mapping_tests": [ { "policies": {}, "prefs": { - "ash.keep_fullscreen_without_notification_url_allow_list": { + "keep_fullscreen_without_notification_url_allow_list": { "default_value": [] } } @@ -8022,7 +8023,7 @@ ] }, "prefs": { - "ash.keep_fullscreen_without_notification_url_allow_list": { + "keep_fullscreen_without_notification_url_allow_list": { "value": [ "*" ]
diff --git a/chrome/test/data/webui/settings/payments_section_test.ts b/chrome/test/data/webui/settings/payments_section_test.ts index 832fe59..035c1387 100644 --- a/chrome/test/data/webui/settings/payments_section_test.ts +++ b/chrome/test/data/webui/settings/payments_section_test.ts
@@ -416,7 +416,7 @@ assertTrue(creditCardDialog.$.saveButton.disabled); // Add a name. - creditCardDialog.set('creditCard.name', 'Jane Doe'); + creditCardDialog.set('name_', 'Jane Doe'); flush(); assertEquals('hidden', getComputedStyle(expiredError).visibility); @@ -432,6 +432,29 @@ }); }); + test('verifyNotEditedEntryAfterCancel', async function() { + const creditCard = createCreditCardEntry(); + let creditCardDialog = createCreditCardDialog(creditCard); + + await whenAttributeIs(creditCardDialog.$.dialog, 'open', ''); + + // Edit a entry. + creditCardDialog.set('name_', 'EditedName'); + creditCardDialog.set('nickname_', 'NickName'); + creditCardDialog.set('cardNumber_', '0000000000001234'); + flush(); + + creditCardDialog.$.cancelButton.click(); + await eventToPromise('close', creditCardDialog); + + creditCardDialog = createCreditCardDialog(creditCard); + await whenAttributeIs(creditCardDialog.$.dialog, 'open', ''); + + assertEquals(creditCardDialog.get('name_'), creditCard.name); + assertEquals(creditCardDialog.get('cardNumber_'), creditCard.cardNumber); + assertEquals(creditCardDialog.get('nickname_'), creditCard.nickname); + }); + test('verifyCancelCreditCardEdit', function(done) { const creditCard = createEmptyCreditCardEntry(); const creditCardDialog = createCreditCardDialog(creditCard);
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn index 64d9719..e01a20f 100644 --- a/chromeos/crosapi/mojom/BUILD.gn +++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -44,6 +44,7 @@ "file_manager.mojom", "file_system_provider.mojom", "force_installed_tracker.mojom", + "fullscreen_controller.mojom", "geolocation.mojom", "holding_space_service.mojom", "identity_manager.mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom index 9afeed4..1c153d0 100644 --- a/chromeos/crosapi/mojom/crosapi.mojom +++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -39,6 +39,7 @@ import "chromeos/crosapi/mojom/file_manager.mojom"; import "chromeos/crosapi/mojom/file_system_provider.mojom"; import "chromeos/crosapi/mojom/force_installed_tracker.mojom"; +import "chromeos/crosapi/mojom/fullscreen_controller.mojom"; import "chromeos/crosapi/mojom/geolocation.mojom"; import "chromeos/crosapi/mojom/holding_space_service.mojom"; import "chromeos/crosapi/mojom/identity_manager.mojom"; @@ -124,8 +125,8 @@ // please note the milestone when you added it, to help us reason about // compatibility between the client applications and older ash-chrome binaries. // -// Next version: 94 -// Next method id: 98 +// Next version: 95 +// Next method id: 99 [Stable, Uuid="8b79c34f-2bf8-4499-979a-b17cac522c1e", RenamedFrom="crosapi.mojom.AshChromeService"] interface Crosapi { @@ -327,6 +328,11 @@ BindForceInstalledTracker@57( pending_receiver<ForceInstalledTracker> receiver); + // Binds the full screen controller which determines whether to keep or exit + // full screen mode. + [MinVersion=94] BindFullscreenController@98( + pending_receiver<FullscreenController> receiver); + // Binds the GeolocationService interface for getting network access point // information. // Added in M95.
diff --git a/chromeos/crosapi/mojom/fullscreen_controller.mojom b/chromeos/crosapi/mojom/fullscreen_controller.mojom new file mode 100644 index 0000000..9b1be82 --- /dev/null +++ b/chromeos/crosapi/mojom/fullscreen_controller.mojom
@@ -0,0 +1,20 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module crosapi.mojom; + +// A client implemented by lacros-chrome. +[Stable] +interface FullscreenControllerClient { + // Returns whether full screen mode should be exited on session lock/unlock. + ShouldExitFullscreenBeforeLock@0() => (bool should_exit_fullscreen); +}; + +// This interface lets ash query lacros whether it should exit full screen mode. +// Implemented by ash-chrome. +[Stable, Uuid="49d56ccf-d93f-4fea-a9cd-ce84bc8ea4f5"] +interface FullscreenController { + // Registers the client that lives in lacros-chrome. + AddClient@0(pending_remote<FullscreenControllerClient> client); +};
diff --git a/chromeos/lacros/lacros_service.cc b/chromeos/lacros/lacros_service.cc index dd29a30..f8b1c5a6 100644 --- a/chromeos/lacros/lacros_service.cc +++ b/chromeos/lacros/lacros_service.cc
@@ -48,6 +48,7 @@ #include "chromeos/crosapi/mojom/file_manager.mojom.h" #include "chromeos/crosapi/mojom/file_system_provider.mojom.h" #include "chromeos/crosapi/mojom/force_installed_tracker.mojom.h" +#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h" #include "chromeos/crosapi/mojom/geolocation.mojom.h" #include "chromeos/crosapi/mojom/holding_space_service.mojom.h" #include "chromeos/crosapi/mojom/identity_manager.mojom.h" @@ -333,6 +334,9 @@ &crosapi::mojom::Crosapi::BindForceInstalledTracker, Crosapi::MethodMinVersions::kBindForceInstalledTrackerMinVersion>(); ConstructRemote< + crosapi::mojom::FullscreenController, &Crosapi::BindFullscreenController, + Crosapi::MethodMinVersions::kBindFullscreenControllerMinVersion>(); + ConstructRemote< crosapi::mojom::GeolocationService, &crosapi::mojom::Crosapi::BindGeolocationService, Crosapi::MethodMinVersions::kBindGeolocationServiceMinVersion>();
diff --git a/chromeos/ui/wm/BUILD.gn b/chromeos/ui/wm/BUILD.gn index 61fb2cc..a46f6ea 100644 --- a/chromeos/ui/wm/BUILD.gn +++ b/chromeos/ui/wm/BUILD.gn
@@ -16,14 +16,21 @@ "desks/desks_helper.h", "features.cc", "features.h", + "fullscreen/keep_fullscreen_for_url_checker.cc", + "fullscreen/keep_fullscreen_for_url_checker.h", + "fullscreen/pref_names.cc", + "fullscreen/pref_names.h", ] deps = [ "//base", "//build:chromeos_buildflags", "//chromeos/ui/base", + "//components/prefs", + "//components/url_matcher", "//ui/aura", "//ui/base", + "//url", ] if (is_chromeos_ash) {
diff --git a/chromeos/ui/wm/DEPS b/chromeos/ui/wm/DEPS index f86f4ee..8435167 100644 --- a/chromeos/ui/wm/DEPS +++ b/chromeos/ui/wm/DEPS
@@ -2,5 +2,7 @@ "+ui/aura", "+ui/base", "+ui/platform_window", + "+components/prefs", + "+components/url_matcher", "+ui/views", ]
diff --git a/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.cc b/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.cc new file mode 100644 index 0000000..7f6582fa --- /dev/null +++ b/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.cc
@@ -0,0 +1,52 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h" + +#include "chromeos/ui/wm/fullscreen/pref_names.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" +#include "components/url_matcher/url_matcher.h" +#include "components/url_matcher/url_util.h" +#include "url/gurl.h" + +namespace chromeos { + +KeepFullscreenForUrlChecker::KeepFullscreenForUrlChecker( + PrefService* pref_service) + : pref_service_(pref_service) { + pref_observer_.Init(pref_service); + pref_observer_.Add( + prefs::kKeepFullscreenWithoutNotificationUrlAllowList, + base::BindRepeating(&KeepFullscreenForUrlChecker::OnPrefChanged, + base::Unretained(this))); + + // Initialize with the current pref values. + OnPrefChanged(); +} + +KeepFullscreenForUrlChecker::~KeepFullscreenForUrlChecker() = default; + +bool KeepFullscreenForUrlChecker:: + IsKeepFullscreenWithoutNotificationPolicySet() { + return url_matcher_ != nullptr; +} + +bool KeepFullscreenForUrlChecker::ShouldExitFullscreenForUrl(GURL window_url) { + return url_matcher_ ? url_matcher_->MatchURL(window_url).empty() : true; +} + +void KeepFullscreenForUrlChecker::OnPrefChanged() { + const auto& url_allow_list = pref_service_->GetValueList( + prefs::kKeepFullscreenWithoutNotificationUrlAllowList); + if (url_allow_list.size() == 0) { + url_matcher_ = nullptr; + return; + } + + url_matcher_ = std::make_unique<url_matcher::URLMatcher>(); + url_matcher::util::AddAllowFilters(url_matcher_.get(), url_allow_list); +} + +} // namespace chromeos
diff --git a/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h b/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h new file mode 100644 index 0000000..19f779a --- /dev/null +++ b/chromeos/ui/wm/fullscreen/keep_fullscreen_for_url_checker.h
@@ -0,0 +1,57 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_UI_WM_FULLSCREEN_KEEP_FULLSCREEN_FOR_URL_CHECKER_H_ +#define CHROMEOS_UI_WM_FULLSCREEN_KEEP_FULLSCREEN_FOR_URL_CHECKER_H_ + +#include <memory> + +#include "base/memory/raw_ptr.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" +#include "components/url_matcher/url_matcher.h" + +class GURL; + +namespace chromeos { + +// Initialized with the given PrefService, the KeepFullscreenForUrlChecker is +// used to check if it is allowed by user pref to keep fullscreen after unlock. +class KeepFullscreenForUrlChecker { + public: + explicit KeepFullscreenForUrlChecker(PrefService* pref_service); + + KeepFullscreenForUrlChecker(const KeepFullscreenForUrlChecker&) = delete; + KeepFullscreenForUrlChecker& operator=(const KeepFullscreenForUrlChecker&) = + delete; + + ~KeepFullscreenForUrlChecker(); + + // Returns true if the KeepFullscreenWithoutNotification value is set in the + // corresponding PrefService, otherwise returns false. + bool IsKeepFullscreenWithoutNotificationPolicySet(); + + // Returns true if it is not allowed by user pref to keep full screen for the + // given window URL. Returns false if it is allowed to keep full screen, that + // is, if |window_url| matches a pattern in the + // KeepFullscreenWithoutNotification policy of the corresponding PrefService. + bool ShouldExitFullscreenForUrl(GURL window_url); + + private: + // Updates the filters of the URLMatcher when the + // KeepFullscreenWithoutNotificationUrlAllowList pref changed. + void OnPrefChanged(); + + PrefChangeRegistrar pref_observer_; + + raw_ptr<PrefService> pref_service_; + + std::unique_ptr<url_matcher::URLMatcher> url_matcher_; + + base::WeakPtrFactory<KeepFullscreenForUrlChecker> weak_ptr_factory_{this}; +}; + +} // namespace chromeos + +#endif // CHROMEOS_UI_WM_FULLSCREEN_KEEP_FULLSCREEN_FOR_URL_CHECKER_H_
diff --git a/chromeos/ui/wm/fullscreen/pref_names.cc b/chromeos/ui/wm/fullscreen/pref_names.cc new file mode 100644 index 0000000..a8d4da8 --- /dev/null +++ b/chromeos/ui/wm/fullscreen/pref_names.cc
@@ -0,0 +1,15 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/ui/wm/fullscreen/pref_names.h" + +namespace chromeos::prefs { + +// A list of URLs that are allowed to continue full screen mode after session +// unlock without a notification. To prevent fake login screens, the device +// normally exits full screen mode before locking a session. +const char kKeepFullscreenWithoutNotificationUrlAllowList[] = + "keep_fullscreen_without_notification_url_allow_list"; + +} // namespace chromeos::prefs
diff --git a/chromeos/ui/wm/fullscreen/pref_names.h b/chromeos/ui/wm/fullscreen/pref_names.h new file mode 100644 index 0000000..a8883f46 --- /dev/null +++ b/chromeos/ui/wm/fullscreen/pref_names.h
@@ -0,0 +1,14 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_UI_WM_FULLSCREEN_PREF_NAMES_H_ +#define CHROMEOS_UI_WM_FULLSCREEN_PREF_NAMES_H_ + +namespace chromeos::prefs { + +extern const char kKeepFullscreenWithoutNotificationUrlAllowList[]; + +} // namespace chromeos::prefs + +#endif // CHROMEOS_UI_WM_FULLSCREEN_PREF_NAMES_H_
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn index 17d667e..fdaf7cc 100644 --- a/components/autofill_assistant/browser/BUILD.gn +++ b/components/autofill_assistant/browser/BUILD.gn
@@ -405,6 +405,8 @@ "actions/wait_for_dom_test_base.h", "fake_common_dependencies.cc", "fake_common_dependencies.h", + "fake_platform_dependencies.cc", + "fake_platform_dependencies.h", "fake_script_executor_delegate.cc", "fake_script_executor_delegate.h", "fake_script_executor_ui_delegate.cc", @@ -526,6 +528,7 @@ "field_formatter_unittest.cc", "full_card_requester_unittest.cc", "generic_ui_replace_placeholders_unittest.cc", + "headless/headless_script_controller_impl_unittest.cc", "js_flow_util_unittest.cc", "mock_client_context.cc", "mock_client_context.h",
diff --git a/components/autofill_assistant/browser/android/client_android.cc b/components/autofill_assistant/browser/android/client_android.cc index 108d1fd..c3cbdc9 100644 --- a/components/autofill_assistant/browser/android/client_android.cc +++ b/components/autofill_assistant/browser/android/client_android.cc
@@ -708,7 +708,8 @@ GetWebContents(), /* client= */ this, base::DefaultTickClock::GetInstance(), RuntimeManager::GetForWebContents(GetWebContents())->GetWeakPtr(), - std::move(service), ukm::UkmRecorder::Get(), annotate_dom_model_service_); + std::move(service), /* web_controller= */ nullptr, + ukm::UkmRecorder::Get(), annotate_dom_model_service_); ui_controller_ = std::make_unique<UiController>( /* client= */ this, controller_.get(), std::move(tts_controller)); ui_controller_->StartListening();
diff --git a/components/autofill_assistant/browser/android/starter_delegate_android.cc b/components/autofill_assistant/browser/android/starter_delegate_android.cc index 89d647d..a1c01541 100644 --- a/components/autofill_assistant/browser/android/starter_delegate_android.cc +++ b/components/autofill_assistant/browser/android/starter_delegate_android.cc
@@ -15,6 +15,7 @@ #include "components/autofill_assistant/browser/android/trigger_script_bridge_android.h" #include "components/autofill_assistant/browser/android/ui_controller_android_utils.h" #include "components/autofill_assistant/browser/assistant_field_trial_util.h" +#include "components/autofill_assistant/browser/headless/client_headless.h" #include "components/autofill_assistant/browser/headless/headless_script_controller_impl.h" #include "components/autofill_assistant/browser/public/password_change/website_login_manager_impl.h" #include "components/autofill_assistant/browser/public/runtime_manager_impl.h" @@ -320,10 +321,17 @@ jinitial_url, GetIsCustomTab()); if (trigger_context->GetScriptParameters().GetRunHeadless()) { + auto client = std::make_unique<ClientHeadless>( + &GetWebContents(), starter_->GetCommonDependencies(), + /* action_extension_delegate= */ nullptr, GetWebsiteLoginManager(), + base::DefaultTickClock::GetInstance(), + RuntimeManager::GetForWebContents(&GetWebContents())->GetWeakPtr(), + ukm::UkmRecorder::Get(), + starter_->GetCommonDependencies()->GetOrCreateAnnotateDomModelService( + GetWebContents().GetBrowserContext())); headless_script_controller_ = std::make_unique<HeadlessScriptControllerImpl>( - &GetWebContents(), /*action_extension_delegate=*/nullptr, - GetWebsiteLoginManager()); + &GetWebContents(), starter_.get(), std::move(client)); headless_script_controller_->StartScript( // Note: this ignores device-only parameters.
diff --git a/components/autofill_assistant/browser/autofill_assistant_factory.cc b/components/autofill_assistant/browser/autofill_assistant_factory.cc index c3161c8..277de1a 100644 --- a/components/autofill_assistant/browser/autofill_assistant_factory.cc +++ b/components/autofill_assistant/browser/autofill_assistant_factory.cc
@@ -8,6 +8,7 @@ #include "components/autofill_assistant/browser/autofill_assistant_impl.h" #include "components/autofill_assistant/browser/common_dependencies.h" +#include "content/public/browser/browser_context.h" namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/autofill_assistant_impl.cc b/components/autofill_assistant/browser/autofill_assistant_impl.cc index 9c9ba8a..def9fd2 100644 --- a/components/autofill_assistant/browser/autofill_assistant_impl.cc +++ b/components/autofill_assistant/browser/autofill_assistant_impl.cc
@@ -7,8 +7,10 @@ #include <memory> #include <vector> +#include "base/time/default_tick_clock.h" #include "components/autofill_assistant/browser/common_dependencies.h" #include "components/autofill_assistant/browser/desktop/starter_delegate_desktop.h" +#include "components/autofill_assistant/browser/headless/client_headless.h" #include "components/autofill_assistant/browser/headless/headless_script_controller_impl.h" #include "components/autofill_assistant/browser/protocol_utils.h" #include "components/autofill_assistant/browser/public/password_change/website_login_manager.h" @@ -19,6 +21,7 @@ #include "components/autofill_assistant/browser/service/service_request_sender.h" #include "components/autofill_assistant/browser/service/service_request_sender_impl.h" #include "components/autofill_assistant/browser/service/simple_url_loader_factory.h" +#include "components/autofill_assistant/browser/starter.h" #include "components/version_info/version_info.h" #include "content/public/browser/browser_context.h" #include "net/http/http_status_code.h" @@ -140,8 +143,20 @@ content::WebContents* web_contents, ExternalActionDelegate* action_extension_delegate, WebsiteLoginManager* website_login_manager) { - return std::make_unique<HeadlessScriptControllerImpl>( - web_contents, action_extension_delegate, website_login_manager); + auto* starter = Starter::FromWebContents(web_contents); + if (!starter) { + return nullptr; + } + + auto client = std::make_unique<ClientHeadless>( + web_contents, starter->GetCommonDependencies(), action_extension_delegate, + website_login_manager, base::DefaultTickClock::GetInstance(), + RuntimeManager::GetForWebContents(web_contents)->GetWeakPtr(), + ukm::UkmRecorder::Get(), + starter->GetCommonDependencies()->GetOrCreateAnnotateDomModelService( + web_contents->GetBrowserContext())); + return std::make_unique<HeadlessScriptControllerImpl>(web_contents, starter, + std::move(client)); } } // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc index ac11864..720ba283 100644 --- a/components/autofill_assistant/browser/controller.cc +++ b/components/autofill_assistant/browser/controller.cc
@@ -75,6 +75,7 @@ const base::TickClock* tick_clock, base::WeakPtr<RuntimeManager> runtime_manager, std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, ukm::UkmRecorder* ukm_recorder, AnnotateDomModelService* annotate_dom_model_service) : content::WebContentsObserver(web_contents), @@ -84,6 +85,7 @@ service_(service ? std::move(service) : ServiceImpl::Create(web_contents->GetBrowserContext(), client_)), + web_controller_(std::move(web_controller)), navigating_to_new_document_(web_contents->IsWaitingForResponse()), ukm_recorder_(ukm_recorder), annotate_dom_model_service_(annotate_dom_model_service) {}
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h index 7c84e93..dab68a12 100644 --- a/components/autofill_assistant/browser/controller.h +++ b/components/autofill_assistant/browser/controller.h
@@ -63,13 +63,14 @@ public: // |web_contents|, |client|, |tick_clock| and |script_executor_ui_delegate| // must remain valid for the lifetime of the instance. Controller will take - // ownership of |service| if specified, otherwise will create and own the - // default service. + // ownership of |service| and |web_controller| if specified, otherwise will + // create and own a default instance. Controller(content::WebContents* web_contents, Client* client, const base::TickClock* tick_clock, base::WeakPtr<RuntimeManager> runtime_manager, std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, ukm::UkmRecorder* ukm_recorder, AnnotateDomModelService* annotate_dom_model_service); @@ -313,15 +314,15 @@ const raw_ptr<const base::TickClock> tick_clock_; base::WeakPtr<RuntimeManager> runtime_manager_; - // Lazily instantiate in GetWebController(). - std::unique_ptr<WebController> web_controller_; - // An instance to suppress keyboard. If this is not nullptr, the keyboard // is suppressed. std::unique_ptr<SuppressKeyboardRAII> suppress_keyboard_raii_; - // Lazily instantiate in GetService(). std::unique_ptr<Service> service_; + + // Lazily instantiate in GetWebController(). + std::unique_ptr<WebController> web_controller_; + std::unique_ptr<TriggerContext> trigger_context_; AutofillAssistantState state_ = AutofillAssistantState::INACTIVE;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc index 13763b5b..7b72f80 100644 --- a/components/autofill_assistant/browser/controller_unittest.cc +++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -113,11 +113,10 @@ mock_runtime_manager_ = std::make_unique<MockRuntimeManager>(); controller_ = std::make_unique<Controller>( web_contents(), &mock_client_, task_environment()->GetMockTickClock(), - mock_runtime_manager_->GetWeakPtr(), std::move(service), &ukm_recorder_, + mock_runtime_manager_->GetWeakPtr(), std::move(service), + std::move(web_controller), &ukm_recorder_, &mock_annotate_dom_model_service_); - SetWebControllerForTest(controller_.get(), std::move(web_controller)); - ON_CALL(mock_client_, AttachUI()).WillByDefault(Invoke([this]() { controller_->SetUiShown(true); })); @@ -2304,11 +2303,13 @@ TEST_F(ControllerTest, FlowFinishedMetricControllerDestroyedMidFlow) { auto service = std::make_unique<NiceMock<MockService>>(); + auto web_controller = std::make_unique<NiceMock<MockWebController>>(); auto* service_ptr = service.get(); auto controller = std::make_unique<Controller>( web_contents(), &mock_client_, task_environment()->GetMockTickClock(), - mock_runtime_manager_->GetWeakPtr(), std::move(service), &ukm_recorder_, + mock_runtime_manager_->GetWeakPtr(), std::move(service), + std::move(web_controller), &ukm_recorder_, /* annotate_dom_model_service= */ nullptr); SetWebControllerForTest(controller.get(), std::make_unique<NiceMock<MockWebController>>());
diff --git a/components/autofill_assistant/browser/fake_platform_dependencies.cc b/components/autofill_assistant/browser/fake_platform_dependencies.cc new file mode 100644 index 0000000..c637ac5 --- /dev/null +++ b/components/autofill_assistant/browser/fake_platform_dependencies.cc
@@ -0,0 +1,17 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/fake_platform_dependencies.h" + +namespace autofill_assistant { + +FakePlatformDependencies::FakePlatformDependencies() = default; +FakePlatformDependencies::~FakePlatformDependencies() = default; + +bool FakePlatformDependencies::IsCustomTab( + const content::WebContents& web_contents) const { + return is_custom_tab_; +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_platform_dependencies.h b/components/autofill_assistant/browser/fake_platform_dependencies.h new file mode 100644 index 0000000..7366269 --- /dev/null +++ b/components/autofill_assistant/browser/fake_platform_dependencies.h
@@ -0,0 +1,26 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FAKE_PLATFORM_DEPENDENCIES_H_ +#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FAKE_PLATFORM_DEPENDENCIES_H_ + +#include "components/autofill_assistant/browser/platform_dependencies.h" + +namespace autofill_assistant { + +class FakePlatformDependencies : public PlatformDependencies { + public: + FakePlatformDependencies(); + ~FakePlatformDependencies() override; + + // From PlatformDependencies: + bool IsCustomTab(const content::WebContents& web_contents) const override; + + // Intentionally public to allow tests direct access. + bool is_custom_tab_ = false; +}; + +} // namespace autofill_assistant + +#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FAKE_PLATFORM_DEPENDENCIES_H_
diff --git a/components/autofill_assistant/browser/fake_starter_platform_delegate.cc b/components/autofill_assistant/browser/fake_starter_platform_delegate.cc index 51952b0e..4205034 100644 --- a/components/autofill_assistant/browser/fake_starter_platform_delegate.cc +++ b/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
@@ -142,7 +142,7 @@ const PlatformDependencies* FakeStarterPlatformDelegate::GetPlatformDependencies() const { - return nullptr; + return &fake_platform_dependencies_; } base::WeakPtr<StarterPlatformDelegate>
diff --git a/components/autofill_assistant/browser/fake_starter_platform_delegate.h b/components/autofill_assistant/browser/fake_starter_platform_delegate.h index da7ea21..e4fd61f 100644 --- a/components/autofill_assistant/browser/fake_starter_platform_delegate.h +++ b/components/autofill_assistant/browser/fake_starter_platform_delegate.h
@@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/memory/raw_ptr.h" #include "components/autofill_assistant/browser/fake_common_dependencies.h" +#include "components/autofill_assistant/browser/fake_platform_dependencies.h" #include "components/autofill_assistant/browser/starter_platform_delegate.h" #include "testing/gmock/include/gmock/gmock.h" @@ -63,6 +64,7 @@ // Intentionally public to give tests direct access. FakeCommonDependencies fake_common_dependencies_; + FakePlatformDependencies fake_platform_dependencies_; std::unique_ptr<TriggerScriptCoordinator::UiDelegate> trigger_script_ui_delegate_; std::unique_ptr<ServiceRequestSender> trigger_script_request_sender_for_test_;
diff --git a/components/autofill_assistant/browser/headless/client_headless.cc b/components/autofill_assistant/browser/headless/client_headless.cc index 3cc3cb1..9bf22eb 100644 --- a/components/autofill_assistant/browser/headless/client_headless.cc +++ b/components/autofill_assistant/browser/headless/client_headless.cc
@@ -12,7 +12,6 @@ #include "base/containers/flat_set.h" #include "base/json/json_writer.h" #include "base/metrics/field_trial_params.h" -#include "base/time/default_tick_clock.h" #include "components/autofill_assistant/browser/autofill_assistant_tts_controller.h" #include "components/autofill_assistant/browser/controller.h" #include "components/autofill_assistant/browser/display_strings_util.h" @@ -45,10 +44,18 @@ content::WebContents* web_contents, const CommonDependencies* common_dependencies, ExternalActionDelegate* action_extension_delegate, - WebsiteLoginManager* website_login_manager) + WebsiteLoginManager* website_login_manager, + const base::TickClock* tick_clock, + base::WeakPtr<RuntimeManager> runtime_manager, + ukm::UkmRecorder* ukm_recorder, + AnnotateDomModelService* annotate_dom_model_service) : web_contents_(web_contents), common_dependencies_(common_dependencies), - website_login_manager_(website_login_manager) { + website_login_manager_(website_login_manager), + tick_clock_(tick_clock), + runtime_manager_(runtime_manager), + ukm_recorder_(ukm_recorder), + annotate_dom_model_service_(annotate_dom_model_service) { headless_ui_controller_ = std::make_unique<HeadlessUiController>(action_extension_delegate); } @@ -58,6 +65,8 @@ void ClientHeadless::Start( const GURL& url, std::unique_ptr<TriggerContext> trigger_context, + std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, base::OnceCallback<void(Metrics::DropOutReason reason)> script_ended_callback) { // Ignore the call if a script is already running. @@ -66,12 +75,9 @@ } script_ended_callback_ = std::move(script_ended_callback); controller_ = std::make_unique<Controller>( - web_contents_, /* client= */ this, base::DefaultTickClock::GetInstance(), - RuntimeManager::GetForWebContents(web_contents_)->GetWeakPtr(), - /* service= */ nullptr, ukm::UkmRecorder::Get(), - /* annotate_dom_model_service= */ - common_dependencies_->GetOrCreateAnnotateDomModelService( - GetWebContents()->GetBrowserContext())); + web_contents_, /* client= */ this, tick_clock_, runtime_manager_, + std::move(service), std::move(web_controller), ukm_recorder_, + annotate_dom_model_service_); controller_->AddObserver(headless_ui_controller_.get()); controller_->Start(url, std::move(trigger_context)); }
diff --git a/components/autofill_assistant/browser/headless/client_headless.h b/components/autofill_assistant/browser/headless/client_headless.h index b1b0c6f..e9c59a7 100644 --- a/components/autofill_assistant/browser/headless/client_headless.h +++ b/components/autofill_assistant/browser/headless/client_headless.h
@@ -35,7 +35,11 @@ explicit ClientHeadless(content::WebContents* web_contents, const CommonDependencies* common_dependencies, ExternalActionDelegate* action_extension_delegate, - WebsiteLoginManager* website_login_manager); + WebsiteLoginManager* website_login_manager, + const base::TickClock* tick_clock, + base::WeakPtr<RuntimeManager> runtime_manager, + ukm::UkmRecorder* ukm_recorder, + AnnotateDomModelService* annotate_dom_model_service); ClientHeadless(const ClientHeadless&) = delete; ClientHeadless& operator=(const ClientHeadless&) = delete; @@ -44,6 +48,8 @@ bool IsRunning() const; void Start(const GURL& url, std::unique_ptr<TriggerContext> trigger_context, + std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, base::OnceCallback<void(Metrics::DropOutReason reason)> script_ended_callback); @@ -91,15 +97,18 @@ signin::AccessTokenInfo access_token_info); void NotifyScriptEnded(Metrics::DropOutReason reason); - raw_ptr<content::WebContents> web_contents_; + const raw_ptr<content::WebContents> web_contents_; std::unique_ptr<Controller> controller_; const raw_ptr<const CommonDependencies> common_dependencies_; - raw_ptr<WebsiteLoginManager> website_login_manager_; + const raw_ptr<WebsiteLoginManager> website_login_manager_; std::unique_ptr<HeadlessUiController> headless_ui_controller_; - raw_ptr<signin::IdentityManager> identity_manager_ = nullptr; std::unique_ptr<signin::AccessTokenFetcher> access_token_fetcher_; base::OnceCallback<void(bool, const std::string&)> fetch_access_token_callback_; + const raw_ptr<const base::TickClock> tick_clock_; + base::WeakPtr<RuntimeManager> runtime_manager_; + const raw_ptr<ukm::UkmRecorder> ukm_recorder_; + const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_; // Only set while a script is running. base::OnceCallback<void(Metrics::DropOutReason reason)>
diff --git a/components/autofill_assistant/browser/headless/headless_script_controller_impl.cc b/components/autofill_assistant/browser/headless/headless_script_controller_impl.cc index 5187218..ca71d13 100644 --- a/components/autofill_assistant/browser/headless/headless_script_controller_impl.cc +++ b/components/autofill_assistant/browser/headless/headless_script_controller_impl.cc
@@ -7,24 +7,19 @@ #include "base/callback_helpers.h" #include "base/time/default_tick_clock.h" #include "components/autofill_assistant/browser/desktop/starter_delegate_desktop.h" -#include "components/autofill_assistant/browser/public/password_change/website_login_manager.h" +#include "components/autofill_assistant/browser/headless/client_headless.h" #include "components/autofill_assistant/browser/starter.h" namespace autofill_assistant { HeadlessScriptControllerImpl::HeadlessScriptControllerImpl( content::WebContents* web_contents, - ExternalActionDelegate* action_extension_delegate, - WebsiteLoginManager* website_login_manager) - : web_contents_(web_contents) { - DCHECK(web_contents_); - - auto* starter = Starter::FromWebContents(web_contents_); - if (starter) { - client_ = std::make_unique<ClientHeadless>( - web_contents, starter->GetCommonDependencies(), - action_extension_delegate, website_login_manager); - } + Starter* starter, + std::unique_ptr<ClientHeadless> client) + : web_contents_(web_contents), + starter_(starter), + client_(std::move(client)) { + DCHECK(web_contents_ && starter_ && client_); } HeadlessScriptControllerImpl::~HeadlessScriptControllerImpl() = default; @@ -41,19 +36,24 @@ base::OnceCallback<void(ScriptResult)> script_ended_callback, bool use_autofill_assistant_onboarding, base::OnceCallback<void()> onboarding_successful_callback) { + StartScript(script_parameters, std::move(script_ended_callback), + use_autofill_assistant_onboarding, + std::move(onboarding_successful_callback), + /* service= */ nullptr, /* web_controller= */ nullptr); +} +void HeadlessScriptControllerImpl::StartScript( + const base::flat_map<std::string, std::string>& script_parameters, + base::OnceCallback<void(ScriptResult)> script_ended_callback, + bool use_autofill_assistant_onboarding, + base::OnceCallback<void()> onboarding_successful_callback, + std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller) { // This HeadlessScriptController is currently executing a script, so we return // an error. if (script_ended_callback_) { std::move(script_ended_callback).Run({false}); return; } - auto* starter = Starter::FromWebContents(web_contents_); - // The starter has not yet been initialized or was not initialized at the - // time the constructor was called. - if (!starter || !client_) { - std::move(script_ended_callback).Run({false}); - return; - } script_ended_callback_ = std::move(script_ended_callback); onboarding_successful_callback_ = std::move(onboarding_successful_callback); @@ -61,7 +61,7 @@ auto trigger_context = std::make_unique<TriggerContext>( std::move(parameters), /* experiment_ids = */ "", - starter->GetPlatformDependencies()->IsCustomTab(*web_contents_), + starter_->GetPlatformDependencies()->IsCustomTab(*web_contents_), /*onboarding_shown = */ false, /*is_direct_action = */ false, /* initial_url = */ "", @@ -69,13 +69,16 @@ /* is_externally_triggered = */ true, /* skip_autofill_assistant_onboarding = */ !use_autofill_assistant_onboarding); - starter->CanStart( + starter_->CanStart( std::move(trigger_context), base::BindOnce(&HeadlessScriptControllerImpl::OnReadyToStart, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_.GetWeakPtr(), std::move(service), + std::move(web_controller))); } void HeadlessScriptControllerImpl::OnReadyToStart( + std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, bool can_start, absl::optional<GURL> url, std::unique_ptr<TriggerContext> trigger_context) { @@ -89,7 +92,8 @@ // TODO(b/201964911): At this point we should be sure no other Controller // exists on this tab. Add logic to the starter to check that's the case. client_->Start( - *url, std::move(trigger_context), + *url, std::move(trigger_context), std::move(service), + std::move(web_controller), base::BindOnce(&HeadlessScriptControllerImpl::NotifyScriptEnded, weak_ptr_factory_.GetWeakPtr())); }
diff --git a/components/autofill_assistant/browser/headless/headless_script_controller_impl.h b/components/autofill_assistant/browser/headless/headless_script_controller_impl.h index 55b8ffb2..7a15391c 100644 --- a/components/autofill_assistant/browser/headless/headless_script_controller_impl.h +++ b/components/autofill_assistant/browser/headless/headless_script_controller_impl.h
@@ -11,24 +11,24 @@ #include "base/callback_helpers.h" #include "base/memory/raw_ptr.h" -#include "components/autofill_assistant/browser/autofill_assistant_impl.h" -#include "components/autofill_assistant/browser/controller.h" -#include "components/autofill_assistant/browser/controller_observer.h" -#include "components/autofill_assistant/browser/execution_delegate.h" -#include "components/autofill_assistant/browser/headless/client_headless.h" #include "components/autofill_assistant/browser/metrics.h" -#include "components/autofill_assistant/browser/script_executor_ui_delegate.h" +#include "components/autofill_assistant/browser/public/headless_script_controller.h" namespace autofill_assistant { -class WebsiteLoginManager; +class ClientHeadless; +class Service; +class Starter; +class WebController; +class TriggerContext; +class HeadlessScriptControllerImplTest; class HeadlessScriptControllerImpl : public HeadlessScriptController { public: - HeadlessScriptControllerImpl( - content::WebContents* web_contents, - ExternalActionDelegate* action_extension_delegate, - WebsiteLoginManager* website_login_manager); + // |starter| must outlive this instance. + HeadlessScriptControllerImpl(content::WebContents* web_contents, + Starter* starter, + std::unique_ptr<ClientHeadless> client); HeadlessScriptControllerImpl(const HeadlessScriptControllerImpl&) = delete; HeadlessScriptControllerImpl& operator=(const HeadlessScriptControllerImpl&) = @@ -47,7 +47,19 @@ base::OnceCallback<void()> onboarding_successful_callback) override; private: - void OnReadyToStart(bool can_start, + friend HeadlessScriptControllerImplTest; + + void StartScript( + const base::flat_map<std::string, std::string>& script_parameters, + base::OnceCallback<void(ScriptResult)> script_ended_callback, + bool use_autofill_assistant_onboarding, + base::OnceCallback<void()> onboarding_successful_callback, + std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller); + + void OnReadyToStart(std::unique_ptr<Service> service, + std::unique_ptr<WebController> web_controller, + bool can_start, absl::optional<GURL> url, std::unique_ptr<TriggerContext> trigger_context); @@ -57,6 +69,7 @@ void NotifyScriptEnded(Metrics::DropOutReason reason); raw_ptr<content::WebContents> web_contents_; + raw_ptr<Starter> starter_; std::unique_ptr<ClientHeadless> client_; base::OnceCallback<void(ScriptResult)> script_ended_callback_;
diff --git a/components/autofill_assistant/browser/headless/headless_script_controller_impl_unittest.cc b/components/autofill_assistant/browser/headless/headless_script_controller_impl_unittest.cc new file mode 100644 index 0000000..d7c862b7 --- /dev/null +++ b/components/autofill_assistant/browser/headless/headless_script_controller_impl_unittest.cc
@@ -0,0 +1,241 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill_assistant/browser/headless/headless_script_controller_impl.h" + +#include <memory> +#include <utility> + +#include "base/callback_helpers.h" +#include "base/containers/flat_map.h" +#include "base/guid.h" +#include "base/memory/raw_ptr.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/gtest_util.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "base/time/time.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/field_types.h" +#include "components/autofill_assistant/browser/cud_condition.pb.h" +#include "components/autofill_assistant/browser/device_context.h" +#include "components/autofill_assistant/browser/fake_script_executor_ui_delegate.h" +#include "components/autofill_assistant/browser/fake_starter_platform_delegate.h" +#include "components/autofill_assistant/browser/features.h" +#include "components/autofill_assistant/browser/headless/client_headless.h" +#include "components/autofill_assistant/browser/mock_autofill_assistant_tts_controller.h" +#include "components/autofill_assistant/browser/mock_client.h" +#include "components/autofill_assistant/browser/mock_controller_observer.h" +#include "components/autofill_assistant/browser/mock_personal_data_manager.h" +#include "components/autofill_assistant/browser/public/mock_runtime_manager.h" +#include "components/autofill_assistant/browser/service/mock_service.h" +#include "components/autofill_assistant/browser/service/service.h" +#include "components/autofill_assistant/browser/starter.h" +#include "components/autofill_assistant/browser/switches.h" +#include "components/autofill_assistant/browser/test_util.h" +#include "components/autofill_assistant/browser/trigger_context.h" +#include "components/autofill_assistant/browser/ukm_test_util.h" +#include "components/autofill_assistant/browser/web/mock_web_controller.h" +#include "components/password_manager/core/browser/mock_password_change_success_tracker.h" +#include "components/strings/grit/components_strings.h" +#include "components/ukm/content/source_url_recorder.h" +#include "components/ukm/test_ukm_recorder.h" +#include "content/public/test/navigation_simulator.h" +#include "content/public/test/prerender_test_util.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_renderer_host.h" +#include "content/public/test/web_contents_tester.h" +#include "net/http/http_status_code.h" +#include "services/metrics/public/cpp/metrics_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/blink/public/common/features.h" +#include "ui/base/l10n/l10n_util.h" + +namespace autofill_assistant { + +using ::base::test::RunOnceCallback; +using ::testing::_; +using ::testing::AllOf; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::Field; +using ::testing::Gt; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::IsEmpty; +using ::testing::NiceMock; +using ::testing::Not; +using ::testing::NotNull; +using ::testing::Property; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::Sequence; +using ::testing::SizeIs; +using ::testing::StrEq; +using ::testing::UnorderedElementsAre; +using ::testing::WithArgs; + +const char kExampleDeeplink[] = "https://www.example.com"; + +class HeadlessScriptControllerImplTest : public testing::Test { + public: + void SetUp() override { + web_contents_ = content::WebContentsTester::CreateTestWebContents( + &browser_context_, nullptr); + mock_runtime_manager_ = std::make_unique<MockRuntimeManager>(); + + starter_ = std::make_unique<Starter>( + web_contents(), fake_platform_delegate_.GetWeakPtr(), &ukm_recorder_, + mock_runtime_manager_->GetWeakPtr(), + task_environment_.GetMockTickClock()); + + mock_service_to_inject_ = std::make_unique<NiceMock<MockService>>(); + mock_service_ = mock_service_to_inject_.get(); + // Fetching scripts succeeds for all URLs, but return nothing. + ON_CALL(*mock_service_, GetScriptsForUrl) + .WillByDefault(RunOnceCallback<2>( + net::HTTP_OK, "", ServiceRequestSender::ResponseInfo{})); + + // Scripts run, but have no actions. + ON_CALL(*mock_service_, GetActions) + .WillByDefault(RunOnceCallback<5>( + net::HTTP_OK, "", ServiceRequestSender::ResponseInfo{})); + ON_CALL(*mock_service_, GetNextActions) + .WillByDefault(RunOnceCallback<6>( + net::HTTP_OK, "", ServiceRequestSender::ResponseInfo{})); + + mock_web_controller_to_inject_ = + std::make_unique<NiceMock<MockWebController>>(); + mock_web_controller_ = mock_web_controller_to_inject_.get(); + ON_CALL(*mock_web_controller_, FindElement) + .WillByDefault(RunOnceCallback<2>(ClientStatus(), nullptr)); + + auto client = std::make_unique<ClientHeadless>( + web_contents_.get(), starter_->GetCommonDependencies(), nullptr, + nullptr, task_environment_.GetMockTickClock(), + mock_runtime_manager_->GetWeakPtr(), &ukm_recorder_, nullptr); + headless_script_controller_ = + std::make_unique<HeadlessScriptControllerImpl>( + web_contents_.get(), starter_.get(), std::move(client)); + } + + content::WebContents* web_contents() { return web_contents_.get(); } + + // Note that calling this method moves |service_| and |web_controller_| so + // it should not be called more than once per test. + void Start(const base::flat_map<std::string, std::string>& params, + bool expect_success) { + // Since the callback is often called in a PostTask, we use this to make + // sure the test does not fininsh before the callback is called. + base::RunLoop run_loop; + + EXPECT_CALL(mock_script_ended_callback_, Run) + .WillOnce([&run_loop, &expect_success]( + HeadlessScriptController::ScriptResult result) { + EXPECT_EQ(result.success, expect_success); + run_loop.Quit(); + }); + headless_script_controller_->StartScript( + params, mock_script_ended_callback_.Get(), + /* use_autofill_assistant_onboarding = */ false, base::DoNothing(), + std::move(mock_service_to_inject_), + std::move(mock_web_controller_to_inject_)); + run_loop.Run(); + } + + void SetupScripts(SupportsScriptResponseProto scripts) { + EXPECT_CALL(*mock_service_, GetScriptsForUrl) + .WillOnce(RunOnceCallback<2>(net::HTTP_OK, scripts.SerializeAsString(), + ServiceRequestSender::ResponseInfo{})); + } + + void SetupActionsForScript(const std::string& path, + ActionsResponseProto actions_response) { + EXPECT_CALL(*mock_service_, GetActions(StrEq(path), _, _, _, _, _)) + .WillOnce(RunOnceCallback<5>(net::HTTP_OK, + actions_response.SerializeAsString(), + ServiceRequestSender::ResponseInfo{})); + } + + static SupportedScriptProto* AddRunnableScript( + SupportsScriptResponseProto* response, + const std::string& name_and_path, + bool direct_action = true) { + SupportedScriptProto* script = response->add_scripts(); + script->set_path(name_and_path); + if (direct_action) { + script->mutable_presentation()->mutable_direct_action()->add_names( + name_and_path); + } + return script; + } + + protected: + content::BrowserTaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + content::RenderViewHostTestEnabler rvh_test_enabler_; + content::TestBrowserContext browser_context_; + std::unique_ptr<content::WebContents> web_contents_; + base::MockCallback< + base::OnceCallback<void(HeadlessScriptController::ScriptResult)>> + mock_script_ended_callback_; + FakeStarterPlatformDelegate fake_platform_delegate_; + std::unique_ptr<MockRuntimeManager> mock_runtime_manager_; + std::unique_ptr<Starter> starter_; + std::unique_ptr<HeadlessScriptControllerImpl> headless_script_controller_; + ukm::TestAutoSetUkmRecorder ukm_recorder_; + raw_ptr<MockService> mock_service_; + raw_ptr<MockWebController> mock_web_controller_; + + private: + // These will be moved when the |Start| method is called, so expectations + // should be written using |mock_service| and |mock_web_controller_| instead. + std::unique_ptr<MockService> mock_service_to_inject_; + std::unique_ptr<MockWebController> mock_web_controller_to_inject_; +}; + +TEST_F(HeadlessScriptControllerImplTest, + StartFailsWithoutMandatoryScriptParameter) { + // The startup will fail because we are missing the initial URL. + base::flat_map<std::string, std::string> params = { + {"ENABLED", "true"}, {"START_IMMEDIATELY", "true"}}; + Start(params, /* expect_success= */ false); +} + +TEST_F(HeadlessScriptControllerImplTest, StartFailsIfNoScriptsAvailable) { + EXPECT_CALL(*mock_service_, GetScriptsForUrl) + .WillOnce(RunOnceCallback<2>(net::HTTP_OK, "", + ServiceRequestSender::ResponseInfo{})); + base::flat_map<std::string, std::string> params = { + {"ENABLED", "true"}, + {"START_IMMEDIATELY", "true"}, + {"ORIGINAL_DEEPLINK", kExampleDeeplink}}; + Start(params, /* expect_success= */ false); +} + +TEST_F(HeadlessScriptControllerImplTest, SuccessfulRun) { + SupportsScriptResponseProto script_response; + auto* script = AddRunnableScript(&script_response, "script"); + script->mutable_presentation()->set_autostart(true); + SetupScripts(script_response); + + ActionsResponseProto script_actions; + script_actions.add_actions()->mutable_stop(); + SetupActionsForScript("script", script_actions); + + base::flat_map<std::string, std::string> params = { + {"ENABLED", "true"}, + {"START_IMMEDIATELY", "true"}, + {"ORIGINAL_DEEPLINK", kExampleDeeplink}}; + Start(params, /* expect_success= */ true); +} + +} // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/public/BUILD.gn b/components/autofill_assistant/browser/public/BUILD.gn index 3477ce98..790170a 100644 --- a/components/autofill_assistant/browser/public/BUILD.gn +++ b/components/autofill_assistant/browser/public/BUILD.gn
@@ -28,9 +28,13 @@ deps = [ ":proto", - "//base", "//components/autofill_assistant/browser/public/fast_checkout/proto:proto", "//components/autofill_assistant/browser/public/password_change/proto:proto", + "//url", + ] + + public_deps = [ + "//base", "//components/version_info:channel", "//content/public/browser", ]
diff --git a/components/autofill_assistant/browser/public/autofill_assistant.cc b/components/autofill_assistant/browser/public/autofill_assistant.cc index 0d3b10b..388f625 100644 --- a/components/autofill_assistant/browser/public/autofill_assistant.cc +++ b/components/autofill_assistant/browser/public/autofill_assistant.cc
@@ -10,6 +10,7 @@ #include "base/containers/span.h" #include "base/hash/legacy_hash.h" #include "base/strings/string_util.h" +#include "url/gurl.h" #include "url/origin.h" namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/public/autofill_assistant_factory.h b/components/autofill_assistant/browser/public/autofill_assistant_factory.h index 34a8afaf..090c7cc 100644 --- a/components/autofill_assistant/browser/public/autofill_assistant_factory.h +++ b/components/autofill_assistant/browser/public/autofill_assistant_factory.h
@@ -9,7 +9,10 @@ #include "components/autofill_assistant/browser/public/autofill_assistant.h" #include "components/version_info/channel.h" -#include "content/public/browser/browser_context.h" + +namespace content { +class BrowserContext; +} // namespace content namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/public/headless_script_controller.h b/components/autofill_assistant/browser/public/headless_script_controller.h index e8cf067..6a110dfb 100644 --- a/components/autofill_assistant/browser/public/headless_script_controller.h +++ b/components/autofill_assistant/browser/public/headless_script_controller.h
@@ -5,11 +5,11 @@ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_HEADLESS_SCRIPT_CONTROLLER_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_HEADLESS_SCRIPT_CONTROLLER_H_ +#include "base/containers/flat_map.h" #include "base/memory/weak_ptr.h" #include "components/autofill_assistant/browser/public/external_action.pb.h" #include "components/autofill_assistant/browser/public/runtime_observer.h" #include "components/autofill_assistant/browser/public/ui_state.h" -#include "content/public/browser/web_contents.h" namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/public/runtime_manager.cc b/components/autofill_assistant/browser/public/runtime_manager.cc index 75431db..3f60376 100644 --- a/components/autofill_assistant/browser/public/runtime_manager.cc +++ b/components/autofill_assistant/browser/public/runtime_manager.cc
@@ -3,7 +3,9 @@ // found in the LICENSE file. #include "components/autofill_assistant/browser/public/runtime_manager.h" + #include "components/autofill_assistant/browser/public/runtime_manager_impl.h" +#include "content/public/browser/web_contents.h" namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/public/runtime_manager.h b/components/autofill_assistant/browser/public/runtime_manager.h index acacfbd..9fbfce8 100644 --- a/components/autofill_assistant/browser/public/runtime_manager.h +++ b/components/autofill_assistant/browser/public/runtime_manager.h
@@ -8,7 +8,10 @@ #include "base/memory/weak_ptr.h" #include "components/autofill_assistant/browser/public/runtime_observer.h" #include "components/autofill_assistant/browser/public/ui_state.h" -#include "content/public/browser/web_contents.h" + +namespace content { +class WebContents; +} // namespace content namespace autofill_assistant { // Notifies subscribed observers when the UI state changes.
diff --git a/components/autofill_assistant/browser/public/runtime_manager_impl.h b/components/autofill_assistant/browser/public/runtime_manager_impl.h index 52a009d..ee50393 100644 --- a/components/autofill_assistant/browser/public/runtime_manager_impl.h +++ b/components/autofill_assistant/browser/public/runtime_manager_impl.h
@@ -13,7 +13,9 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_user_data.h" -// TODO: Move implementation to internal/. +// TODO: Move implementation to internal/. When that is done, the public +// dependency on content for the build target autofill_assistant/browser:public +// can be removed. namespace autofill_assistant { class RuntimeManagerImpl : public RuntimeManager,
diff --git a/components/browser_ui/media/android/BUILD.gn b/components/browser_ui/media/android/BUILD.gn index 3ea9e3d..747d30d 100644 --- a/components/browser_ui/media/android/BUILD.gn +++ b/components/browser_ui/media/android/BUILD.gn
@@ -40,59 +40,14 @@ sources = [ "java/res/drawable-hdpi/audio_playing.png", "java/res/drawable-hdpi/audio_playing_square.png", - "java/res/drawable-hdpi/ic_call_end_white_36dp.png", - "java/res/drawable-hdpi/ic_fast_forward_white_36dp.png", - "java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png", - "java/res/drawable-hdpi/ic_mic_off_white_36dp.png", - "java/res/drawable-hdpi/ic_mic_white_36dp.png", - "java/res/drawable-hdpi/ic_skip_next_white_36dp.png", - "java/res/drawable-hdpi/ic_skip_previous_white_36dp.png", - "java/res/drawable-hdpi/ic_videocam_off_white_36dp.png", - "java/res/drawable-hdpi/ic_videocam_white_36dp.png", "java/res/drawable-mdpi/audio_playing.png", "java/res/drawable-mdpi/audio_playing_square.png", - "java/res/drawable-mdpi/ic_call_end_white_36dp.png", - "java/res/drawable-mdpi/ic_fast_forward_white_36dp.png", - "java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png", - "java/res/drawable-mdpi/ic_mic_off_white_36dp.png", - "java/res/drawable-mdpi/ic_mic_white_36dp.png", - "java/res/drawable-mdpi/ic_skip_next_white_36dp.png", - "java/res/drawable-mdpi/ic_skip_previous_white_36dp.png", - "java/res/drawable-mdpi/ic_videocam_off_white_36dp.png", - "java/res/drawable-mdpi/ic_videocam_white_36dp.png", "java/res/drawable-xhdpi/audio_playing.png", "java/res/drawable-xhdpi/audio_playing_square.png", - "java/res/drawable-xhdpi/ic_call_end_white_36dp.png", - "java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png", - "java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png", - "java/res/drawable-xhdpi/ic_mic_off_white_36dp.png", - "java/res/drawable-xhdpi/ic_mic_white_36dp.png", - "java/res/drawable-xhdpi/ic_skip_next_white_36dp.png", - "java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png", - "java/res/drawable-xhdpi/ic_videocam_off_white_36dp.png", - "java/res/drawable-xhdpi/ic_videocam_white_36dp.png", "java/res/drawable-xxhdpi/audio_playing.png", "java/res/drawable-xxhdpi/audio_playing_square.png", - "java/res/drawable-xxhdpi/ic_call_end_white_36dp.png", - "java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png", - "java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png", - "java/res/drawable-xxhdpi/ic_mic_off_white_36dp.png", - "java/res/drawable-xxhdpi/ic_mic_white_36dp.png", - "java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png", - "java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png", - "java/res/drawable-xxhdpi/ic_videocam_off_white_36dp.png", - "java/res/drawable-xxhdpi/ic_videocam_white_36dp.png", "java/res/drawable-xxxhdpi/audio_playing.png", "java/res/drawable-xxxhdpi/audio_playing_square.png", - "java/res/drawable-xxxhdpi/ic_call_end_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_mic_off_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_mic_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_videocam_off_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_videocam_white_36dp.png", ] deps = [ "//components/browser_ui/strings/android:browser_ui_strings_grd",
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_call_end_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_call_end_white_36dp.png deleted file mode 100644 index 90773818..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_call_end_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png deleted file mode 100644 index 6a7db4b..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_forward_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png deleted file mode 100644 index 656d022..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_fast_rewind_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_off_white_36dp.png deleted file mode 100644 index c0e773b..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_white_36dp.png deleted file mode 100644 index 2b377a74..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_mic_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png deleted file mode 100644 index cf68df8..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_next_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png deleted file mode 100644 index da1c1c95..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_skip_previous_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_off_white_36dp.png deleted file mode 100644 index fafc3a3..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_white_36dp.png deleted file mode 100644 index 5c99f19..0000000 --- a/components/browser_ui/media/android/java/res/drawable-hdpi/ic_videocam_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_call_end_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_call_end_white_36dp.png deleted file mode 100644 index 8fb6ffd..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_call_end_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png deleted file mode 100644 index f890f113..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_forward_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png deleted file mode 100644 index 9d02d43..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_fast_rewind_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_off_white_36dp.png deleted file mode 100644 index 153d979f..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_white_36dp.png deleted file mode 100644 index d3d9dc2b..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_mic_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png deleted file mode 100644 index 9032328..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_next_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png deleted file mode 100644 index 23faeeb..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_skip_previous_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_off_white_36dp.png deleted file mode 100644 index b09d4dd33..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_white_36dp.png deleted file mode 100644 index f4e905c55..0000000 --- a/components/browser_ui/media/android/java/res/drawable-mdpi/ic_videocam_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_call_end_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_call_end_white_36dp.png deleted file mode 100644 index ff84f1f..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_call_end_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png deleted file mode 100644 index f7d810f..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_forward_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png deleted file mode 100644 index 12ff39a..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_fast_rewind_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_off_white_36dp.png deleted file mode 100644 index 89ec023..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_white_36dp.png deleted file mode 100644 index d79f5bb..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_mic_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png deleted file mode 100644 index 972192d..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_next_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png deleted file mode 100644 index 1181ec9..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_skip_previous_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_off_white_36dp.png deleted file mode 100644 index b305b70..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_white_36dp.png deleted file mode 100644 index 646115b..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xhdpi/ic_videocam_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_call_end_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_call_end_white_36dp.png deleted file mode 100644 index 3213989..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_call_end_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png deleted file mode 100644 index b41b3de4..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_forward_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png deleted file mode 100644 index 253833b..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_fast_rewind_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_off_white_36dp.png deleted file mode 100644 index 03cb6a6..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_white_36dp.png deleted file mode 100644 index fc3b9246..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_mic_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png deleted file mode 100644 index 4652215..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_next_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png deleted file mode 100644 index c8db47f..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_skip_previous_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_off_white_36dp.png deleted file mode 100644 index 54378c0..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_white_36dp.png deleted file mode 100644 index 60f37bc..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxhdpi/ic_videocam_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_call_end_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_call_end_white_36dp.png deleted file mode 100644 index ad9f949f..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_call_end_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png deleted file mode 100644 index b111f7d..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_forward_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png deleted file mode 100644 index e1baaa3..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_fast_rewind_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_off_white_36dp.png deleted file mode 100644 index 533c60e..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_white_36dp.png deleted file mode 100644 index 5ec10394..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_mic_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png deleted file mode 100644 index 00b29dd..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_next_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png deleted file mode 100644 index 9e52d500..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_skip_previous_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_off_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_off_white_36dp.png deleted file mode 100644 index 59bc5fe065..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_off_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_white_36dp.png b/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_white_36dp.png deleted file mode 100644 index 3372697..0000000 --- a/components/browser_ui/media/android/java/res/drawable-xxxhdpi/ic_videocam_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java index adf3ba6d..87161f3 100644 --- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java +++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationController.java
@@ -331,28 +331,28 @@ mActionToButtonInfo = new SparseArray<>(); mActionToButtonInfo.put(MediaSessionAction.PLAY, - new MediaButtonInfo(R.drawable.ic_play_arrow_white_36dp, + new MediaButtonInfo(R.drawable.ic_play_arrow_white_24dp, R.string.accessibility_play, ACTION_PLAY, MEDIA_ACTION_PLAY)); mActionToButtonInfo.put(MediaSessionAction.PAUSE, - new MediaButtonInfo(R.drawable.ic_pause_white_36dp, R.string.accessibility_pause, + new MediaButtonInfo(R.drawable.ic_pause_white_24dp, R.string.accessibility_pause, ACTION_PAUSE, MEDIA_ACTION_PAUSE)); mActionToButtonInfo.put(MediaSessionAction.STOP, - new MediaButtonInfo(R.drawable.ic_stop_white_36dp, R.string.accessibility_stop, + new MediaButtonInfo(R.drawable.ic_stop_white_24dp, R.string.accessibility_stop, ACTION_STOP, MEDIA_ACTION_STOP)); mActionToButtonInfo.put(MediaSessionAction.PREVIOUS_TRACK, - new MediaButtonInfo(R.drawable.ic_skip_previous_white_36dp, + new MediaButtonInfo(R.drawable.ic_skip_previous_white_24dp, R.string.accessibility_previous_track, ACTION_PREVIOUS_TRACK, MEDIA_ACTION_PREVIOUS_TRACK)); mActionToButtonInfo.put(MediaSessionAction.NEXT_TRACK, - new MediaButtonInfo(R.drawable.ic_skip_next_white_36dp, + new MediaButtonInfo(R.drawable.ic_skip_next_white_24dp, R.string.accessibility_next_track, ACTION_NEXT_TRACK, MEDIA_ACTION_NEXT_TRACK)); mActionToButtonInfo.put(MediaSessionAction.SEEK_FORWARD, - new MediaButtonInfo(R.drawable.ic_fast_forward_white_36dp, + new MediaButtonInfo(R.drawable.ic_fast_forward_white_24dp, R.string.accessibility_seek_forward, ACTION_SEEK_FORWARD, MEDIA_ACTION_SEEK_FORWARD)); mActionToButtonInfo.put(MediaSessionAction.SEEK_BACKWARD, - new MediaButtonInfo(R.drawable.ic_fast_rewind_white_36dp, + new MediaButtonInfo(R.drawable.ic_fast_rewind_white_24dp, R.string.accessibility_seek_backward, ACTION_SEEK_BACKWARD, MEDIA_ACTION_SEEK_BACKWARD));
diff --git a/components/browser_ui/photo_picker/android/java/res/layout/video_player.xml b/components/browser_ui/photo_picker/android/java/res/layout/video_player.xml index a8db9fe..49ac0531 100644 --- a/components/browser_ui/photo_picker/android/java/res/layout/video_player.xml +++ b/components/browser_ui/photo_picker/android/java/res/layout/video_player.xml
@@ -66,7 +66,7 @@ android:layout_height="24dp" android:layout_marginEnd="8dp" android:contentDescription="@string/accessibility_pause_video" - app:srcCompat="@drawable/ic_fast_rewind_white_36dp" /> + app:srcCompat="@drawable/ic_fast_rewind_white_24dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -78,7 +78,7 @@ android:layout_height="24dp" android:layout_marginStart="8dp" android:contentDescription="@string/accessibility_pause_video" - app:srcCompat="@drawable/ic_fast_forward_white_36dp" /> + app:srcCompat="@drawable/ic_fast_forward_white_24dp" /> </LinearLayout> <ImageView
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn index b40575d7..3b7d43c 100644 --- a/components/browser_ui/styles/android/BUILD.gn +++ b/components/browser_ui/styles/android/BUILD.gn
@@ -58,14 +58,11 @@ "java/res/drawable-hdpi/ic_delete_white_24dp.png", "java/res/drawable-hdpi/ic_edit_24dp.png", "java/res/drawable-hdpi/ic_logo_googleg_24dp.png", - "java/res/drawable-hdpi/ic_pause_white_24dp.png", "java/res/drawable-hdpi/ic_pause_white_36dp.png", - "java/res/drawable-hdpi/ic_play_arrow_white_24dp.png", "java/res/drawable-hdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-hdpi/ic_refresh_white_24dp.png", "java/res/drawable-hdpi/ic_refresh_white_36dp.png", "java/res/drawable-hdpi/ic_share_white_24dp.png", - "java/res/drawable-hdpi/ic_stop_white_36dp.png", "java/res/drawable-hdpi/ic_storage.png", "java/res/drawable-hdpi/modern_toolbar_shadow.png", "java/res/drawable-hdpi/navigation_bubble_shadow.9.png", @@ -84,14 +81,11 @@ "java/res/drawable-mdpi/ic_delete_white_24dp.png", "java/res/drawable-mdpi/ic_edit_24dp.png", "java/res/drawable-mdpi/ic_logo_googleg_24dp.png", - "java/res/drawable-mdpi/ic_pause_white_24dp.png", "java/res/drawable-mdpi/ic_pause_white_36dp.png", - "java/res/drawable-mdpi/ic_play_arrow_white_24dp.png", "java/res/drawable-mdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-mdpi/ic_refresh_white_24dp.png", "java/res/drawable-mdpi/ic_refresh_white_36dp.png", "java/res/drawable-mdpi/ic_share_white_24dp.png", - "java/res/drawable-mdpi/ic_stop_white_36dp.png", "java/res/drawable-mdpi/ic_storage.png", "java/res/drawable-mdpi/modern_toolbar_shadow.png", "java/res/drawable-mdpi/navigation_bubble_shadow.9.png", @@ -105,14 +99,11 @@ "java/res/drawable-xhdpi/ic_delete_white_24dp.png", "java/res/drawable-xhdpi/ic_edit_24dp.png", "java/res/drawable-xhdpi/ic_logo_googleg_24dp.png", - "java/res/drawable-xhdpi/ic_pause_white_24dp.png", "java/res/drawable-xhdpi/ic_pause_white_36dp.png", - "java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png", "java/res/drawable-xhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xhdpi/ic_refresh_white_24dp.png", "java/res/drawable-xhdpi/ic_refresh_white_36dp.png", "java/res/drawable-xhdpi/ic_share_white_24dp.png", - "java/res/drawable-xhdpi/ic_stop_white_36dp.png", "java/res/drawable-xhdpi/ic_storage.png", "java/res/drawable-xhdpi/modern_toolbar_shadow.png", "java/res/drawable-xhdpi/navigation_bubble_shadow.9.png", @@ -126,14 +117,11 @@ "java/res/drawable-xxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxhdpi/ic_edit_24dp.png", "java/res/drawable-xxhdpi/ic_logo_googleg_24dp.png", - "java/res/drawable-xxhdpi/ic_pause_white_24dp.png", "java/res/drawable-xxhdpi/ic_pause_white_36dp.png", - "java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png", "java/res/drawable-xxhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xxhdpi/ic_refresh_white_24dp.png", "java/res/drawable-xxhdpi/ic_refresh_white_36dp.png", "java/res/drawable-xxhdpi/ic_share_white_24dp.png", - "java/res/drawable-xxhdpi/ic_stop_white_36dp.png", "java/res/drawable-xxhdpi/ic_storage.png", "java/res/drawable-xxhdpi/modern_toolbar_shadow.png", "java/res/drawable-xxhdpi/navigation_bubble_shadow.9.png", @@ -147,14 +135,11 @@ "java/res/drawable-xxxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxxhdpi/ic_edit_24dp.png", "java/res/drawable-xxxhdpi/ic_logo_googleg_24dp.png", - "java/res/drawable-xxxhdpi/ic_pause_white_24dp.png", "java/res/drawable-xxxhdpi/ic_pause_white_36dp.png", - "java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png", "java/res/drawable-xxxhdpi/ic_play_arrow_white_36dp.png", "java/res/drawable-xxxhdpi/ic_refresh_white_24dp.png", "java/res/drawable-xxxhdpi/ic_refresh_white_36dp.png", "java/res/drawable-xxxhdpi/ic_share_white_24dp.png", - "java/res/drawable-xxxhdpi/ic_stop_white_36dp.png", "java/res/drawable-xxxhdpi/ic_storage.png", "java/res/drawable-xxxhdpi/navigation_bubble_shadow.9.png", "java/res/drawable-xxxhdpi/plus.png", @@ -171,6 +156,7 @@ "java/res/drawable/ic_bar_chart_24dp.xml", "java/res/drawable/ic_brightness_medium_24dp.xml", "java/res/drawable/ic_business.xml", + "java/res/drawable/ic_call_end_white_24dp.xml", "java/res/drawable/ic_collections_grey.xml", "java/res/drawable/ic_data_viz_grey.xml", "java/res/drawable/ic_desktop_windows.xml", @@ -180,6 +166,8 @@ "java/res/drawable/ic_drive_file_24dp.xml", "java/res/drawable/ic_drive_image_24dp.xml", "java/res/drawable/ic_eye_crossed.xml", + "java/res/drawable/ic_fast_forward_white_24dp.xml", + "java/res/drawable/ic_fast_rewind_white_24dp.xml", "java/res/drawable/ic_file_download_24dp.xml", "java/res/drawable/ic_file_download_36dp.xml", "java/res/drawable/ic_flash_on_24dp.xml", @@ -192,21 +180,29 @@ "java/res/drawable/ic_lightbulb_24dp.xml", "java/res/drawable/ic_link_24dp.xml", "java/res/drawable/ic_logo_assistant_24dp.xml", + "java/res/drawable/ic_mic_off_white_24dp.xml", + "java/res/drawable/ic_mic_white_24dp.xml", "java/res/drawable/ic_music_note_24dp.xml", "java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml", "java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml", "java/res/drawable/ic_outline_sms_24dp.xml", + "java/res/drawable/ic_pause_white_24dp.xml", "java/res/drawable/ic_photo_camera_black.xml", + "java/res/drawable/ic_play_arrow_white_24dp.xml", "java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml", "java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml", "java/res/drawable/ic_remove.xml", "java/res/drawable/ic_replay_white_24dp.xml", "java/res/drawable/ic_security_grey.xml", "java/res/drawable/ic_settings_black.xml", + "java/res/drawable/ic_skip_next_white_24dp.xml", + "java/res/drawable/ic_skip_previous_white_24dp.xml", "java/res/drawable/ic_spam_reduction_24dp.xml", + "java/res/drawable/ic_stop_white_24dp.xml", "java/res/drawable/ic_tune_24dp.xml", "java/res/drawable/ic_update_grey.xml", "java/res/drawable/ic_videocam_24dp.xml", + "java/res/drawable/ic_videocam_off_white_24dp.xml", "java/res/drawable/ic_visibility_black.xml", "java/res/drawable/ic_visibility_off_black.xml", "java/res/drawable/ic_volume_off_white_24dp.xml",
diff --git a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png deleted file mode 100644 index 1701f34..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_pause_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png deleted file mode 100644 index 35f2e7f..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_play_arrow_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_stop_white_36dp.png b/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_stop_white_36dp.png deleted file mode 100644 index 1fcf519..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-hdpi/ic_stop_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png deleted file mode 100644 index e1169e5..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_pause_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png deleted file mode 100644 index cc060f1a..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_play_arrow_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_stop_white_36dp.png b/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_stop_white_36dp.png deleted file mode 100644 index dfff26c..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-mdpi/ic_stop_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png deleted file mode 100644 index f49aed7..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_pause_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png deleted file mode 100644 index a3c80e7..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_play_arrow_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png b/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png deleted file mode 100644 index 801d341..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xhdpi/ic_stop_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png deleted file mode 100644 index 7192ad4..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_pause_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png deleted file mode 100644 index 547ef30..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png b/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png deleted file mode 100644 index adef631..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxhdpi/ic_stop_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png deleted file mode 100644 index 660ac658..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_pause_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png b/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png deleted file mode 100644 index be5c062..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_play_arrow_white_24dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png b/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png deleted file mode 100644 index 035ca181..0000000 --- a/components/browser_ui/styles/android/java/res/drawable-xxxhdpi/ic_stop_white_36dp.png +++ /dev/null Binary files differ
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_call_end_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_call_end_white_24dp.xml new file mode 100644 index 0000000..da6f9ca --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_call_end_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_fast_forward_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_fast_forward_white_24dp.xml new file mode 100644 index 0000000..d7ca2e9 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_fast_forward_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_fast_rewind_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_fast_rewind_white_24dp.xml new file mode 100644 index 0000000..215c990d --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_fast_rewind_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_mic_off_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_mic_off_white_24dp.xml new file mode 100644 index 0000000..7abb054 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_mic_off_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M19,11h-1.7c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zM14.98,11.17c0,-0.06 0.02,-0.11 0.02,-0.17L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v0.18l5.98,5.99zM4.27,3L3,4.27l6.01,6.01L9.01,11c0,1.66 1.33,3 2.99,3 0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52 -2.76,0 -5.3,-2.1 -5.3,-5.1L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9L19.73,21 21,19.73 4.27,3z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_mic_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_mic_white_24dp.xml new file mode 100644 index 0000000..1f107d7 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_mic_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_pause_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_pause_white_24dp.xml new file mode 100644 index 0000000..1120faa --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_pause_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_play_arrow_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_play_arrow_white_24dp.xml new file mode 100644 index 0000000..5ea1636 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_play_arrow_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M8,5v14l11,-7z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_skip_next_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_skip_next_white_24dp.xml new file mode 100644 index 0000000..b4b33e5 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_skip_next_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_skip_previous_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_skip_previous_white_24dp.xml new file mode 100644 index 0000000..36dcff5 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_skip_previous_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_stop_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_stop_white_24dp.xml new file mode 100644 index 0000000..a0d5bff --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_stop_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M6,6h12v12H6z"/> +</vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_videocam_off_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_videocam_off_white_24dp.xml new file mode 100644 index 0000000..c337358 --- /dev/null +++ b/components/browser_ui/styles/android/java/res/drawable/ic_videocam_off_white_24dp.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2022 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M21,6.5l-4,4V7c0,-0.55 -0.45,-1 -1,-1H9.82L21,17.18V6.5zM3.27,2L2,3.27 4.73,6H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.21,0 0.39,-0.08 0.54,-0.18L19.73,21 21,19.73 3.27,2z"/> +</vector>
diff --git a/components/endpoint_fetcher/BUILD.gn b/components/endpoint_fetcher/BUILD.gn index f26fc85d..01bc668 100644 --- a/components/endpoint_fetcher/BUILD.gn +++ b/components/endpoint_fetcher/BUILD.gn
@@ -15,7 +15,7 @@ "//components/signin/public/identity_manager", "//components/version_info:channel", "//google_apis", - "//net/traffic_annotation", + "//net", "//services/data_decoder/public/cpp", "//services/network/public/cpp", ]
diff --git a/components/endpoint_fetcher/endpoint_fetcher.cc b/components/endpoint_fetcher/endpoint_fetcher.cc index 24a751a64..908bb90 100644 --- a/components/endpoint_fetcher/endpoint_fetcher.cc +++ b/components/endpoint_fetcher/endpoint_fetcher.cc
@@ -11,9 +11,11 @@ #include "components/version_info/channel.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/google_api_keys.h" +#include "net/http/http_status_code.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/url_response_head.mojom.h" namespace { const char kContentTypeKey[] = "Content-Type"; @@ -152,6 +154,8 @@ auto response = std::make_unique<EndpointResponse>(); VLOG(1) << __func__ << " No primary accounts found"; response->response = "No primary accounts found"; + response->error_type = + absl::make_optional<FetchErrorType>(FetchErrorType::kAuthError); // TODO(crbug.com/993393) Add more detailed error messaging std::move(endpoint_fetcher_callback).Run(std::move(response)); return; @@ -177,6 +181,8 @@ if (error.state() != GoogleServiceAuthError::NONE) { auto response = std::make_unique<EndpointResponse>(); response->response = "There was an authentication error"; + response->error_type = + absl::make_optional<FetchErrorType>(FetchErrorType::kAuthError); // TODO(crbug.com/993393) Add more detailed error messaging std::move(endpoint_fetcher_callback).Run(std::move(response)); return; @@ -246,26 +252,37 @@ void EndpointFetcher::OnResponseFetched( EndpointFetcherCallback endpoint_fetcher_callback, std::unique_ptr<std::string> response_body) { + int http_status_code = -1; + if (simple_url_loader_->ResponseInfo() && + simple_url_loader_->ResponseInfo()->headers) { + http_status_code = + simple_url_loader_->ResponseInfo()->headers->response_code(); + } int net_error_code = simple_url_loader_->NetError(); // The EndpointFetcher and its members will be destroyed after // any of the below callbacks. Do not access The EndpointFetcher // or its members after the callbacks. simple_url_loader_.reset(); + + auto response = std::make_unique<EndpointResponse>(); + response->http_status_code = http_status_code; + if (net_error_code != net::OK) { + response->error_type = + absl::make_optional<FetchErrorType>(FetchErrorType::kNetError); + } + if (response_body) { if (sanitize_response_) { data_decoder::JsonSanitizer::Sanitize( std::move(*response_body), base::BindOnce(&EndpointFetcher::OnSanitizationResult, - weak_ptr_factory_.GetWeakPtr(), + weak_ptr_factory_.GetWeakPtr(), std::move(response), std::move(endpoint_fetcher_callback))); } else { - auto response = std::make_unique<EndpointResponse>(); response->response = *response_body; std::move(endpoint_fetcher_callback).Run(std::move(response)); } } else { - auto response = std::make_unique<EndpointResponse>(); - // TODO(crbug.com/993393) Add more detailed error messaging std::string net_error = net::ErrorToString(net_error_code); VLOG(1) << __func__ << " with response error: " << net_error; response->response = "There was a response error"; @@ -274,16 +291,21 @@ } void EndpointFetcher::OnSanitizationResult( + std::unique_ptr<EndpointResponse> response, EndpointFetcherCallback endpoint_fetcher_callback, data_decoder::JsonSanitizer::Result result) { - auto response = std::make_unique<EndpointResponse>(); - if (result.value.has_value()) + if (result.value.has_value()) { response->response = result.value.value(); - else if (result.error.has_value()) + } else if (result.error.has_value()) { + response->error_type = + absl::make_optional<FetchErrorType>(FetchErrorType::kResultParseError); response->response = "There was a sanitization error: " + result.error.value(); - else + } else { + response->error_type = + absl::make_optional<FetchErrorType>(FetchErrorType::kResultParseError); response->response = "There was an unknown sanitization error"; + } // The EndpointFetcher and its members will be destroyed after // any the below callback. Do not access The EndpointFetcher // or its members after the callback.
diff --git a/components/endpoint_fetcher/endpoint_fetcher.h b/components/endpoint_fetcher/endpoint_fetcher.h index 3843c8e..e3e7f5f 100644 --- a/components/endpoint_fetcher/endpoint_fetcher.h +++ b/components/endpoint_fetcher/endpoint_fetcher.h
@@ -15,6 +15,7 @@ #include "components/signin/public/identity_manager/scope_set.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/data_decoder/public/cpp/json_sanitizer.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace network { struct ResourceRequest; @@ -29,9 +30,16 @@ class GoogleServiceAuthError; class GURL; +enum class FetchErrorType { + kAuthError = 0, + kNetError = 1, + kResultParseError = 2, +}; + struct EndpointResponse { std::string response; - // TODO(crbug.com/993393) Add more detailed error messaging + int http_status_code{-1}; + absl::optional<FetchErrorType> error_type; }; using EndpointFetcherCallback = @@ -133,7 +141,8 @@ signin::AccessTokenInfo access_token_info); void OnResponseFetched(EndpointFetcherCallback callback, std::unique_ptr<std::string> response_body); - void OnSanitizationResult(EndpointFetcherCallback endpoint_fetcher_callback, + void OnSanitizationResult(std::unique_ptr<EndpointResponse> response, + EndpointFetcherCallback endpoint_fetcher_callback, data_decoder::JsonSanitizer::Result result); enum AuthType { CHROME_API_KEY, OAUTH, NO_AUTH };
diff --git a/components/endpoint_fetcher/endpoint_fetcher_unittest.cc b/components/endpoint_fetcher/endpoint_fetcher_unittest.cc index 45823261..de741fe 100644 --- a/components/endpoint_fetcher/endpoint_fetcher_unittest.cc +++ b/components/endpoint_fetcher/endpoint_fetcher_unittest.cc
@@ -117,9 +117,11 @@ TEST_F(EndpointFetcherTest, FetchResponse) { SignIn(); SetMockResponse(GURL(kEndpoint), kExpectedResponse, net::HTTP_OK, net::OK); - EXPECT_CALL( - endpoint_fetcher_callback(), - Run(Pointee(Field(&EndpointResponse::response, kExpectedResponse)))); + EXPECT_CALL(endpoint_fetcher_callback(), + Run(Pointee(AllOf( + Field(&EndpointResponse::response, kExpectedResponse), + Field(&EndpointResponse::http_status_code, net::HTTP_OK), + Field(&EndpointResponse::error_type, absl::nullopt))))); endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); base::RunLoop().RunUntilIdle(); } @@ -127,10 +129,13 @@ TEST_F(EndpointFetcherTest, FetchMalformedResponse) { SignIn(); SetMockResponse(GURL(kEndpoint), kMalformedResponse, net::HTTP_OK, net::OK); - EXPECT_CALL( - endpoint_fetcher_callback(), - Run(Pointee(Field(&EndpointResponse::response, - testing::StartsWith(kExpectedSanitizationError))))); + EXPECT_CALL(endpoint_fetcher_callback(), + Run(Pointee(AllOf( + Field(&EndpointResponse::response, + testing::StartsWith(kExpectedSanitizationError)), + Field(&EndpointResponse::http_status_code, net::HTTP_OK), + Field(&EndpointResponse::error_type, + FetchErrorType::kResultParseError))))); endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); base::RunLoop().RunUntilIdle(); } @@ -141,7 +146,22 @@ net::ERR_FAILED); EXPECT_CALL( endpoint_fetcher_callback(), - Run(Pointee(Field(&EndpointResponse::response, kExpectedResponseError)))); + Run(Pointee(AllOf( + Field(&EndpointResponse::response, kExpectedResponseError), + Field(&EndpointResponse::http_status_code, -1), + Field(&EndpointResponse::error_type, FetchErrorType::kNetError))))); + endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(EndpointFetcherTest, FetchRedirectionResponse) { + SignIn(); + SetMockResponse(GURL(kEndpoint), kExpectedResponse, net::HTTP_FOUND, net::OK); + EXPECT_CALL(endpoint_fetcher_callback(), + Run(Pointee(AllOf( + Field(&EndpointResponse::response, kExpectedResponse), + Field(&EndpointResponse::http_status_code, net::HTTP_FOUND), + Field(&EndpointResponse::error_type, absl::nullopt))))); endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); base::RunLoop().RunUntilIdle(); } @@ -151,7 +171,10 @@ identity_test_env().SetAutomaticIssueOfAccessTokens(false); EXPECT_CALL( endpoint_fetcher_callback(), - Run(Pointee(Field(&EndpointResponse::response, kExpectedAuthError)))); + Run(Pointee(AllOf( + Field(&EndpointResponse::response, kExpectedAuthError), + Field(&EndpointResponse::http_status_code, -1), + Field(&EndpointResponse::error_type, FetchErrorType::kAuthError))))); endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); identity_test_env().WaitForAccessTokenRequestIfNecessaryAndRespondWithError( GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); @@ -159,9 +182,12 @@ } TEST_F(EndpointFetcherTest, FetchOAuthNoPrimaryAccount) { - EXPECT_CALL(endpoint_fetcher_callback(), - Run(Pointee(Field(&EndpointResponse::response, - kExpectedPrimaryAccountError)))); + EXPECT_CALL( + endpoint_fetcher_callback(), + Run(Pointee(AllOf( + Field(&EndpointResponse::response, kExpectedPrimaryAccountError), + Field(&EndpointResponse::http_status_code, -1), + Field(&EndpointResponse::error_type, FetchErrorType::kAuthError))))); endpoint_fetcher()->Fetch(endpoint_fetcher_callback().Get()); base::RunLoop().RunUntilIdle(); }
diff --git a/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc b/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc index 97b764e..2bb592b 100644 --- a/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc +++ b/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.cc
@@ -54,9 +54,15 @@ // If visits can't be clustered, either because `backend_` is null, or all // unclustered visits have already been clustered and returned, then return // persisted clusters. - weak_history_clusters_service_->NotifyDebugMessage( - "HistoryClustersService::QueryClusters Error: ClusteringBackend is " - "nullptr. Returning empty cluster vector."); + if (!backend_) { + weak_history_clusters_service_->NotifyDebugMessage( + "HistoryClustersServiceTaskGetMostRecentClusters::Start() Error: " + "ClusteringBackend is nullptr. Returning most recent clusters."); + } else { + weak_history_clusters_service_->NotifyDebugMessage( + "HistoryClustersServiceTaskGetMostRecentClusters::Start() exhausted " + "unclustered visits. Returning most recent clusters."); + } ReturnMostRecentPersistedClusters(continuation_params_.continuation_time); } else {
diff --git a/components/sync/engine/loopback_server/loopback_server.cc b/components/sync/engine/loopback_server/loopback_server.cc index dc0a3fb3..af8f045 100644 --- a/components/sync/engine/loopback_server/loopback_server.cc +++ b/components/sync/engine/loopback_server/loopback_server.cc
@@ -10,6 +10,7 @@ #include <utility> #include "base/containers/cxx20_erase.h" +#include "base/feature_list.h" #include "base/files/file_util.h" #include "base/format_macros.h" #include "base/guid.h" @@ -63,6 +64,14 @@ static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks"; static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks"; +// Returns entity's version without increasing it by one for tombstones. The +// version is updated and set in SaveEntity() and there is no need to increment +// it again in CommitResponse. Otherwise, it would be possible that the next +// commit request would return the same version. +const base::Feature kSyncReturnRealVersionOnCommitInLoopbackServer{ + "SyncReturnRealVersionOnCommitInLoopbackServer", + base::FEATURE_ENABLED_BY_DEFAULT}; + int GetServerMigrationVersion( const std::map<ModelType, int>& server_migration_versions, ModelType type) { @@ -581,7 +590,9 @@ : sync_pb::CommitResponse::SUCCESS); entry_response->set_id_string(entity.GetId()); - if (entity.IsDeleted()) { + if (entity.IsDeleted() && + !base::FeatureList::IsEnabled( + kSyncReturnRealVersionOnCommitInLoopbackServer)) { entry_response->set_version(entity.GetVersion() + 1); } else { entry_response->set_version(entity.GetVersion());
diff --git a/components/viz/service/display/overlay_candidate_factory.cc b/components/viz/service/display/overlay_candidate_factory.cc index e76e6f19..7ed78b0 100644 --- a/components/viz/service/display/overlay_candidate_factory.cc +++ b/components/viz/service/display/overlay_candidate_factory.cc
@@ -466,7 +466,10 @@ // |size_in_pixels| as 'set_resource_size_in_pixels' is not called as these // quads are not intended to become overlays. if (!quad->resource_size_in_pixels().IsEmpty()) - candidate.priority_hint = gfx::OverlayPriorityHint::kRegular; + candidate.priority_hint = + candidate.requires_overlay + ? gfx::OverlayPriorityHint::kHardwareProtection + : gfx::OverlayPriorityHint::kRegular; #if BUILDFLAG(IS_ANDROID) if (quad->is_stream_video) {
diff --git a/components/webrtc/android/java/src/org/chromium/components/webrtc/MediaCaptureNotificationUtil.java b/components/webrtc/android/java/src/org/chromium/components/webrtc/MediaCaptureNotificationUtil.java index 826c28a08..cd3419e 100644 --- a/components/webrtc/android/java/src/org/chromium/components/webrtc/MediaCaptureNotificationUtil.java +++ b/components/webrtc/android/java/src/org/chromium/components/webrtc/MediaCaptureNotificationUtil.java
@@ -54,7 +54,7 @@ if (stopIntent != null) { builder.setPriorityBeforeO(NotificationCompat.PRIORITY_HIGH); builder.setVibrate(new long[0]); - builder.addAction(R.drawable.ic_stop_white_36dp, + builder.addAction(R.drawable.ic_stop_white_24dp, appContext.getString(R.string.accessibility_stop), stopIntent); } else { assert mediaType != MediaType.SCREEN_CAPTURE : "SCREEN_CAPTURE requires a stop action";
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index 09af9f4..c606df43 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1222,10 +1222,10 @@ } else if (_owner->IsTextField() && _owner->HasState(ax::mojom::State::kMultiline)) { cocoa_role = NSAccessibilityTextAreaRole; - } else if (role == ax::mojom::Role::kImage && _owner->GetChildCount()) { + } else if (ui::IsImage(_owner->GetRole()) && _owner->GetChildCount()) { // An image map is an image with children, and exposed on Mac as a group. cocoa_role = NSAccessibilityGroupRole; - } else if (role == ax::mojom::Role::kImage && + } else if (ui::IsImage(_owner->GetRole()) && _owner->HasExplicitlyEmptyName()) { cocoa_role = NSAccessibilityUnknownRole; } else if (_owner->IsRootWebAreaForPresentationalIframe()) {
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index 1726c31c..f8cac33 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -30,6 +30,12 @@ #include "ui/accessibility/ax_tree_serializer.h" #include "ui/base/buildflags.h" +#if defined(AX_FAIL_FAST_BUILD) +#include "base/command_line.h" +#include "content/public/browser/ax_inspect_factory.h" +#include "content/public/common/content_switches.h" +#endif + namespace content { namespace { @@ -729,6 +735,30 @@ // Allow derived classes to do event post-processing. FinalizeAccessibilityEvents(); + +#if defined(AX_FAIL_FAST_BUILD) + // When running a debugging/sanitizer build with + // --force-renderer-accessibility, exercise the properties for every node, to + // ensure no crashes or assertions are triggered. This helpfully runs for all + // web tests on builder linux-blink-web-tests-force-accessibility-rel, as well + // as for some clusterfuzz runs. + static int g_max_ax_tree_exercise_iterations = 3; // Avoid timeouts. + static int count = 0; + if (GetRoot()->GetChildCount() > 0 && + !GetRoot()->GetBoolAttribute(ax::mojom::BoolAttribute::kBusy) && + ++count <= g_max_ax_tree_exercise_iterations) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(::switches::kForceRendererAccessibility)) { + std::unique_ptr<ui::AXTreeFormatter> formatter( + AXInspectFactory::CreatePlatformFormatter()); + formatter->SetPropertyFilters({{"*", ui::AXPropertyFilter::ALLOW}}); + std::string formatted_tree = formatter->Format(GetRoot()); + VLOG(1) << "\n\n******** Formatted tree ********\n\n" + << formatted_tree << "\n*********************************\n\n"; + } + } +#endif + return true; }
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc index 3b60d30..159b9ef4 100644 --- a/content/browser/file_system_access/file_system_access_manager_impl.cc +++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -513,6 +513,7 @@ void FileSystemAccessManagerImpl::Shutdown() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + operation_runner_.Reset(); permission_context_ = nullptr; }
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc index 46fd15c..a0c561fa 100644 --- a/content/browser/preloading/prerender/prerender_browsertest.cc +++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -585,6 +585,135 @@ EXPECT_TRUE(prerender_observer.was_activated()); } +// Tests that clicking a link annotated with "target=_blank" cannot activate a +// prerender. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ActivateOnLinkClick_TargetBlank) { + const GURL kInitialUrl = GetUrl("/simple_links.html"); + const GURL kPrerenderingUrl = GetUrl("/title2.html"); + + // Navigate to an initial page which has a link to `kPrerenderingUrl`. + ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); + + // Start prerendering `kPrerenderingUrl`. + int prerender_host_id = AddPrerender(kPrerenderingUrl); + test::PrerenderHostObserver prerender_observer(*web_contents(), + prerender_host_id); + + // Click the link annotated with "target=_blank". This should not activate the + // prerendered page. + TestNavigationObserver nav_observer(kPrerenderingUrl); + nav_observer.StartWatchingNewWebContents(); + const std::string kLinkClickScript = R"( + clickSameSiteNewWindowLink(); + )"; + EXPECT_TRUE(ExecJs(web_contents(), kLinkClickScript)); + nav_observer.WaitForNavigationFinished(); + EXPECT_EQ(nav_observer.last_navigation_url(), kPrerenderingUrl); + EXPECT_FALSE(prerender_observer.was_activated()); + + // The navigation occurred in a new WebContents, so the original WebContents + // should still be showing the initial trigger page. + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kInitialUrl); + // Also, the prerendered page should still be alive. + EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl)); + + // Navigate to `kPrerenderingUrl` on the original WebContents. This should + // activate the prerendered page. + NavigatePrimaryPage(kPrerenderingUrl); + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kPrerenderingUrl); + EXPECT_TRUE(prerender_observer.was_activated()); + ExpectFinalStatusForSpeculationRule(PrerenderHost::FinalStatus::kActivated); +} + +// Tests that clicking a link annotated with "target=_blank rel=noopener" cannot +// activate a prerender. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, + ActivateOnLinkClick_TargetBlankWithNoopener) { + const GURL kInitialUrl = GetUrl("/simple_links.html"); + const GURL kPrerenderingUrl = GetUrl("/title2.html"); + + // Navigate to an initial page which has a link to `kPrerenderingUrl`. + ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); + + // Start prerendering `kPrerenderingUrl`. + int prerender_host_id = AddPrerender(kPrerenderingUrl); + test::PrerenderHostObserver prerender_observer(*web_contents(), + prerender_host_id); + + // Click the link annotated with "target=_blank rel=noopener". This should not + // activate the prerendered page. + TestNavigationObserver nav_observer(kPrerenderingUrl); + nav_observer.StartWatchingNewWebContents(); + const std::string kLinkClickScript = R"( + clickSameSiteNewWindowWithNoopenerLink(); + )"; + EXPECT_TRUE(ExecJs(web_contents(), kLinkClickScript)); + nav_observer.WaitForNavigationFinished(); + EXPECT_EQ(nav_observer.last_navigation_url(), kPrerenderingUrl); + EXPECT_FALSE(prerender_observer.was_activated()); + + // The navigation occurred in a new WebContents, so the original WebContents + // should still be showing the initial trigger page. + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kInitialUrl); + // Also, the prerendered page should still be alive. + EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl)); + + // Navigate to `kPrerenderingUrl` on the original WebContents. This should + // activate the prerendered page. + NavigatePrimaryPage(kPrerenderingUrl); + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kPrerenderingUrl); + EXPECT_TRUE(prerender_observer.was_activated()); + ExpectFinalStatusForSpeculationRule(PrerenderHost::FinalStatus::kActivated); +} + +// Tests that clicking a link annotated with "target=_blank rel=opener" cannot +// activate a prerender. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, + ActivateOnLinkClick_TargetBlankWithOpener) { + const GURL kInitialUrl = GetUrl("/simple_links.html"); + const GURL kPrerenderingUrl = GetUrl("/title2.html"); + + // Navigate to an initial page which has a link to `kPrerenderingUrl`. + ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); + + // Start prerendering `kPrerenderingUrl`. + int prerender_host_id = AddPrerender(kPrerenderingUrl); + test::PrerenderHostObserver prerender_observer(*web_contents(), + prerender_host_id); + + // Click the link annotated with "target=_blank rel=opener". This should not + // activate the prerendered page. + TestNavigationObserver nav_observer(kPrerenderingUrl); + nav_observer.StartWatchingNewWebContents(); + const std::string kLinkClickScript = R"( + clickSameSiteNewWindowWithOpenerLink(); + )"; + EXPECT_TRUE(ExecJs(web_contents(), kLinkClickScript)); + nav_observer.WaitForNavigationFinished(); + EXPECT_EQ(nav_observer.last_navigation_url(), kPrerenderingUrl); + EXPECT_FALSE(prerender_observer.was_activated()); + + // The navigation occurred in a new WebContents, so the original WebContents + // should still be showing the initial trigger page. + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kInitialUrl); + // Also, the prerendered page should still be alive. + EXPECT_TRUE(HasHostForUrl(kPrerenderingUrl)); + + // Navigate to `kPrerenderingUrl` on the original WebContents. The page opened + // with "rel=opener" should prevent it from activating the prerendered page. + NavigatePrimaryPage(kPrerenderingUrl); + EXPECT_EQ(web_contents()->GetLastCommittedURL(), kPrerenderingUrl); + EXPECT_FALSE(prerender_observer.was_activated()); + + // The prerendered page should be destroyed as the trigger page navigated + // away. + prerender_observer.WaitForDestroyed(); + EXPECT_EQ(GetHostForUrl(kPrerenderingUrl), + RenderFrameHost::kNoFrameTreeNodeId); + ExpectFinalStatusForSpeculationRule( + PrerenderHost::FinalStatus::kTriggerDestroyed); +} + IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ResponseHeaders) { const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/set-header?X-Foo: bar"); @@ -611,8 +740,6 @@ // Tests that prerendering is cancelled if a network request for the // navigation results in an empty response with 404 status. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCancelledOnEmptyBody404) { - base::HistogramTester histogram_tester; - const GURL kInitialUrl = GetUrl("/empty.html"); // Specify a URL for which we don't have a corresponding file in the data dir. const GURL kPrerenderingUrl = GetUrl("/404"); @@ -637,8 +764,6 @@ // navigation results in an non-empty response with 404 status. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCancelledOnNonEmptyBody404) { - base::HistogramTester histogram_tester; - const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/page404.html"); @@ -659,8 +784,6 @@ // Tests that prerendering is cancelled if a network request for the // navigation results in an non-empty response with 500 status. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCancelledOn500Page) { - base::HistogramTester histogram_tester; - const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/page500.html"); @@ -679,8 +802,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCancelledOn204Page) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -702,8 +823,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCancelledOn205Page) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -725,8 +844,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderAllowedOn204Iframe) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -752,8 +869,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CancelOnAuthRequested) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -798,8 +913,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CancelOnAuthRequestedSubframe) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -829,8 +942,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CancelOnAuthRequestedSubResource) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -1222,8 +1333,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CrossOriginRedirection) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -1699,7 +1808,6 @@ // Test main frame navigation in prerendering page cancels the prerendering. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, MainFrameNavigationCancelsPrerendering) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); const GURL kHungUrl = GetUrl("/hung"); @@ -1724,7 +1832,6 @@ // Regression test for https://crbug.com/1198051 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, MainFrameFragmentNavigation) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/navigation_controller/hash_anchor_with_iframe.html"); @@ -2097,8 +2204,6 @@ // Tests that prerendering will be cancelled if a prerendering page wants to set // a WebContents-level preferred size. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CancelOnPreferredSizeChanged) { - base::HistogramTester histogram_tester; - const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/title1.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -2659,8 +2764,6 @@ // prernedering should be canceled. IN_PROC_BROWSER_TEST_P(SSLPrerenderBrowserTest, CertificateValidation_Navigation) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -2686,8 +2789,6 @@ // prernedering should be canceled. IN_PROC_BROWSER_TEST_P(SSLPrerenderBrowserTest, CertificateValidation_Subresource) { - base::HistogramTester histogram_tester; - // Navigate to an initial page. const GURL kInitialUrl = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -2725,8 +2826,6 @@ // resource request is intercepted and sent by a service worker. IN_PROC_BROWSER_TEST_P(SSLPrerenderBrowserTest, CertificateValidation_SWMainResource) { - base::HistogramTester histogram_tester; - // Register a service worker that intercepts resource requests. const GURL kInitialUrl = GetUrl("/workers/service_worker_setup.html"); ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl)); @@ -2765,8 +2864,6 @@ if (GetParam() == SSLPrerenderTestErrorBlockType::kCertError) return; - base::HistogramTester histogram_tester; - // Load an initial page and register a service worker that intercepts // resources requests. const GURL kInitialUrl = GetUrl("/workers/service_worker_setup.html"); @@ -2947,8 +3044,6 @@ // Tests that prerendering is gated behind CSP:prefetch-src IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CSPPrefetchSrc) { - base::HistogramTester histogram_tester; - GURL initial_url = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), initial_url)); const std::string kCSPScript = R"( @@ -3007,8 +3102,6 @@ // Tests that prerendering is gated behind CSP:default-src. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, CSPDefaultSrc) { - base::HistogramTester histogram_tester; - GURL initial_url = GetUrl("/empty.html"); ASSERT_TRUE(NavigateToURL(shell(), initial_url)); std::string kCSPScript = R"( @@ -3306,7 +3399,6 @@ // Tests that we will get the exception from the prerendering if the // prerendering page attempts to use notification. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, NotificationConstructorAndroid) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -3330,7 +3422,6 @@ // TODO(crbug.com/1215073): Make a WPT when we have a stable way to wait // cancellation runs. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, DownloadByScript) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerendering"); @@ -3357,7 +3448,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, DownloadInMainFrame) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); // Navigate to an initial page. @@ -3374,7 +3464,6 @@ } IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, DownloadInSubframe) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerendering"); @@ -3405,7 +3494,6 @@ // here, because browser cannot defer this request as the renderer's main thread // blocks while it waits for the response. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, RequestAudioOutputDevice) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/title1.html"); @@ -3433,7 +3521,6 @@ // Tests that an activated page is allowed to request output devices. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, RequestAudioOutputDeviceAfterActivation) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/title1.html"); @@ -3561,7 +3648,6 @@ // Tests that prerendering doesn't run for low-end devices. IN_PROC_BROWSER_TEST_F(PrerenderLowMemoryBrowserTest, NoPrerender) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -3601,7 +3687,6 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, IsInactiveAndDisallowActivationCancelsPrerendering) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -3799,7 +3884,6 @@ // Ensure that WebContentsObserver::DidFailLoad is not invoked and cancels // prerendering when invoked inside prerender frame tree. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, DidFailLoadCancelsPrerendering) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/page_with_iframe.html"); @@ -3898,7 +3982,6 @@ // prerender navigation when activation has already started. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, MainFrameNavigationDuringActivation) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?1"); const GURL kPrerenderingUrl2 = GetUrl("/empty.html?2"); @@ -4562,7 +4645,6 @@ // Test if the host is abandoned when the renderer page crashes. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, AbandonIfRendererProcessCrashes) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -4604,7 +4686,6 @@ // Test if the host is abandoned when the renderer page is killed. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, AbandonIfRendererProcessIsKilled) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -4928,7 +5009,6 @@ // forward and goes into the BFCache. IN_PROC_BROWSER_TEST_P(PrerenderWithBackForwardCacheBrowserTest, CancelOnAfterTriggerIsStoredInBackForwardCache_Forward) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kNextUrl = GetUrl("/empty.html?next"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -4976,7 +5056,6 @@ // and goes into the BFCache. IN_PROC_BROWSER_TEST_P(PrerenderWithBackForwardCacheBrowserTest, CancelOnAfterTriggerIsStoredInBackForwardCache_Back) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kNextUrl = GetUrl("/empty.html?next"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerender"); @@ -5365,7 +5444,6 @@ // Tests that cross-origin urls cannot be prerendered. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, SkipCrossOriginPrerender) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetCrossOriginUrl("/empty.html?crossorigin"); @@ -5812,7 +5890,6 @@ // Tests that prerendering is cancelled when a mixed content subframe is // detected. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, MixedContent) { - base::HistogramTester histogram_tester; const GURL kInitialUrl = GetUrl("/empty.html"); const GURL kPrerenderingUrl = GetUrl("/empty.html?prerendering");
diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc index 21d937cd..386639ab 100644 --- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc
@@ -1157,17 +1157,16 @@ class MediaStreamManagerTestForTransfers : public MediaStreamManagerTest { public: - void CustomSetUp(bool create_original_device = true) { + void SetUp() override { scoped_feature_list_.InitAndEnableFeature( features::kMediaStreamTrackTransfer); media_stream_manager_->UseFakeUIFactoryForTests(base::BindRepeating([]() { return std::make_unique<FakeMediaStreamUIProxy>( /*tests_use_fake_render_frame_hosts=*/true); })); + } - if (!create_original_device) { - return; - } + void RequestDeviceCaptureTypeAudioDevice() { // Generate stream on first renderer. original_device_ = CreateOrSearchAudioDeviceStream( blink::mojom::StreamSelectionStrategy::FORCE_NEW_STREAM, absl::nullopt); @@ -1178,6 +1177,44 @@ EXPECT_NE(transferred_device_.id, original_device_.id); } + void RequestDisplayCaptureTypeDevice(bool request_audio = true, + bool request_video = true, + bool transfer_audio = true) { + base::RunLoop run_loop; + + blink::StreamControls controls(request_audio, request_video); + if (request_audio) + controls.audio.stream_type = + blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE; + if (request_video) + controls.video.stream_type = + blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE; + + blink::MediaStreamDevice video_device; + blink::MediaStreamDevice audio_device; + MediaStreamManager::GenerateStreamsCallback generate_stream_callback = + base::BindOnce(GenerateStreamsCallback, &run_loop, request_audio, + request_video, &audio_device, &video_device); + + media_stream_manager_->GenerateStreams( + render_process_id_, render_frame_id_, requester_id_, + /*page_request_id=*/1, controls, MediaDeviceSaltAndOrigin(), + /*user_gesture=*/false, + StreamSelectionInfo::New( + blink::mojom::StreamSelectionStrategy::SEARCH_BY_DEVICE_ID, + absl::nullopt), + std::move(generate_stream_callback), + /*device_stopped_cb=*/base::DoNothing(), + /*device_changed_cb=*/base::DoNothing(), + /*device_request_state_change_cb=*/base::DoNothing(), + /*device_capture_handle_change_cb=*/base::DoNothing()); + run_loop.Run(); + + original_device_ = transfer_audio ? audio_device : video_device; + existing_device_session_id_ = original_device_.session_id(); + EXPECT_NE(transferred_device_.id, original_device_.id); + } + void GetOpenDevice() { MediaStreamManager::GetOpenDeviceCallback get_open_device_cb = base::BindLambdaForTesting( @@ -1199,6 +1236,7 @@ /*device_changed_cb=*/base::DoNothing(), /*device_request_state_change_cb=*/base::DoNothing(), /*device_capture_handle_change_cb=*/base::DoNothing()); + run_loop_.Run(); } void KeepDeviceAlive(bool device_should_be_found = true) { @@ -1247,12 +1285,54 @@ TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceForExistingDeviceReturnsDevice) { - CustomSetUp(); + RequestDeviceCaptureTypeAudioDevice(); + // TODO(https://crbug.com/1288839): GetOpenDevice request for stream device + // of type DEVICE_CAPTURE should fail, once it is set as unsupported in the + // implementation. GetOpenDevice(); KeepDeviceAlive(); StopDevice(); - run_loop_.Run(); + EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); + EXPECT_EQ(transferred_device_.id, original_device_.id); + EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); +} + +TEST_F(MediaStreamManagerTestForTransfers, + GetDisplayMediaAudioAndVideoAndGetOpenDeviceAudioReturnsDevice) { + RequestDisplayCaptureTypeDevice(); + GetOpenDevice(); + KeepDeviceAlive(); + StopDevice(); + + EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); + EXPECT_EQ(transferred_device_.id, original_device_.id); + EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); +} + +TEST_F(MediaStreamManagerTestForTransfers, + GetDisplayMediaAudioAndVideoAndGetOpenDeviceVideoReturnsDevice) { + RequestDisplayCaptureTypeDevice(/*request_audio=*/true, + /*request_video=*/true, + /*transfer_audio=*/false); + GetOpenDevice(); + KeepDeviceAlive(); + StopDevice(); + + EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); + EXPECT_EQ(transferred_device_.id, original_device_.id); + EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); +} + +TEST_F(MediaStreamManagerTestForTransfers, + GetDisplayMediaVideoAndGetOpenDeviceVideoReturnsDevice) { + RequestDisplayCaptureTypeDevice(/*request_audio=*/false, + /*request_video=*/true, + /*transfer_audio=*/false); + GetOpenDevice(); + KeepDeviceAlive(); + StopDevice(); + EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); EXPECT_EQ(transferred_device_.id, original_device_.id); EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); @@ -1260,23 +1340,21 @@ TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceWhenKeepAliveAfterStopDoesNotReturnDevice) { - CustomSetUp(); + RequestDisplayCaptureTypeDevice(); StopDevice(); KeepDeviceAlive(/*device_should_be_found=*/false); GetOpenDevice(); - run_loop_.Run(); EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); } TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceWhenKeepAliveBeforeStopReturnsDevice) { - CustomSetUp(); + RequestDisplayCaptureTypeDevice(); KeepDeviceAlive(); StopDevice(); GetOpenDevice(); - run_loop_.Run(); EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); EXPECT_EQ(transferred_device_.id, original_device_.id); EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); @@ -1284,11 +1362,10 @@ TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceWithoutKeepAliveReturnsDeviceButDoesNotStop) { - CustomSetUp(); + RequestDisplayCaptureTypeDevice(); GetOpenDevice(); StopDevice(/*should_stop=*/false); - run_loop_.Run(); EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); EXPECT_EQ(transferred_device_.id, original_device_.id); EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); @@ -1296,12 +1373,11 @@ TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceWithKeepAliveAfterStopReturnsDevice) { - CustomSetUp(); + RequestDisplayCaptureTypeDevice(); GetOpenDevice(); StopDevice(); KeepDeviceAlive(); - run_loop_.Run(); EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::OK); EXPECT_EQ(transferred_device_.id, original_device_.id); EXPECT_NE(transferred_device_.session_id(), existing_device_session_id_); @@ -1309,10 +1385,8 @@ TEST_F(MediaStreamManagerTestForTransfers, GetOpenDeviceForNonExistentDeviceReturnsInvalidState) { - CustomSetUp(/*create_original_device=*/false); GetOpenDevice(); - run_loop_.Run(); EXPECT_EQ(result_, blink::mojom::MediaStreamRequestResult::INVALID_STATE); }
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 4c19dbd0..778c7715 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -336,8 +336,6 @@ {"AutofillShadowDOM", blink::features::kAutofillShadowDOM}, {"AndroidDownloadableFontsMatching", features::kAndroidDownloadableFontsMatching}, - {"CancelFormSubmissionInDefaultHandler", - blink::features::kCancelFormSubmissionInDefaultHandler}, {"BatchFetchRequests", blink::features::kBatchFetchRequests}, {"ClipboardCustomFormats", blink::features::kClipboardCustomFormats}, {"CSSContainerQueries", blink::features::kCSSContainerQueries},
diff --git a/content/public/browser/url_data_source.cc b/content/public/browser/url_data_source.cc index 0ac7cac..fc37ef1 100644 --- a/content/public/browser/url_data_source.cc +++ b/content/public/browser/url_data_source.cc
@@ -54,15 +54,6 @@ return std::string(); } -std::string URLDataSource::GetMimeType(const GURL& url) { - return GetMimeType(URLDataSource::URLToRequestPath(url)); -} - -std::string URLDataSource::GetMimeType(const std::string& path) { - NOTREACHED(); - return std::string(); -} - bool URLDataSource::ShouldReplaceExistingSource() { return true; }
diff --git a/content/public/browser/url_data_source.h b/content/public/browser/url_data_source.h index cdda3262..513a2ff 100644 --- a/content/public/browser/url_data_source.h +++ b/content/public/browser/url_data_source.h
@@ -69,12 +69,7 @@ // Return the mimetype that should be sent with this response, or empty // string to specify no mime type. - virtual std::string GetMimeType(const GURL& url); - - // Deprecated. Prefer method above. - // TODO(crbug.com/1344742): Remove after migrating to - // `GetMimeType(const GURL& url)`. - virtual std::string GetMimeType(const std::string& path); + virtual std::string GetMimeType(const GURL& url) = 0; // Returns true if the URLDataSource should replace an existing URLDataSource // with the same name that has already been registered. The default is true.
diff --git a/content/public/browser/webui_config_map.cc b/content/public/browser/webui_config_map.cc index 9b38026..10f44ca 100644 --- a/content/public/browser/webui_config_map.cc +++ b/content/public/browser/webui_config_map.cc
@@ -106,7 +106,7 @@ return config.get(); } -std::unique_ptr<WebUIConfig> WebUIConfigMap::RemoveForTesting( +std::unique_ptr<WebUIConfig> WebUIConfigMap::RemoveConfig( const url::Origin& origin) { auto it = configs_map_.find(origin); if (it == configs_map_.end())
diff --git a/content/public/browser/webui_config_map.h b/content/public/browser/webui_config_map.h index af6c1a58..51eb1f1 100644 --- a/content/public/browser/webui_config_map.h +++ b/content/public/browser/webui_config_map.h
@@ -55,7 +55,7 @@ // Removes and returns the WebUIConfig with |origin|. Returns nullptr if // there is no WebUIConfig with |origin|. - std::unique_ptr<WebUIConfig> RemoveForTesting(const url::Origin& origin); + std::unique_ptr<WebUIConfig> RemoveConfig(const url::Origin& origin); // Returns the size of the map, i.e. how many WebUIConfigs are registered. size_t GetSizeForTesting() { return configs_map_.size(); }
diff --git a/content/public/test/scoped_web_ui_controller_factory_registration.cc b/content/public/test/scoped_web_ui_controller_factory_registration.cc index 2495086e..c8ce1ad 100644 --- a/content/public/test/scoped_web_ui_controller_factory_registration.cc +++ b/content/public/test/scoped_web_ui_controller_factory_registration.cc
@@ -73,14 +73,14 @@ std::unique_ptr<WebUIConfig> webui_config) : webui_config_origin_(url::Origin::Create(webui_origin)) { auto& config_map = WebUIConfigMap::GetInstance(); - replaced_webui_config_ = config_map.RemoveForTesting(webui_config_origin_); + replaced_webui_config_ = config_map.RemoveConfig(webui_config_origin_); if (webui_config != nullptr) AddWebUIConfig(std::move(webui_config)); } ScopedWebUIConfigRegistration::~ScopedWebUIConfigRegistration() { - WebUIConfigMap::GetInstance().RemoveForTesting(webui_config_origin_); + WebUIConfigMap::GetInstance().RemoveConfig(webui_config_origin_); // If we replaced a WebUIConfig, re-register it to keep the global state // clean for future tests.
diff --git a/content/renderer/browser_render_view_browsertest.cc b/content/renderer/browser_render_view_browsertest.cc index 1e32972..9e2a28e 100644 --- a/content/renderer/browser_render_view_browsertest.cc +++ b/content/renderer/browser_render_view_browsertest.cc
@@ -142,14 +142,12 @@ }; // https://crbug.com/788788 -// TODO(crbug.com/1349962): Flaky on linux-tsan too. -#if (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) || \ - (BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)) +#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER) #define MAYBE_ConfirmCacheInformationPlumbed \ DISABLED_ConfirmCacheInformationPlumbed #else #define MAYBE_ConfirmCacheInformationPlumbed ConfirmCacheInformationPlumbed -#endif +#endif // BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER) IN_PROC_BROWSER_TEST_F(RenderViewBrowserTest, MAYBE_ConfirmCacheInformationPlumbed) { ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/content/renderer/render_thread_impl_discardable_memory_browsertest.cc b/content/renderer/render_thread_impl_discardable_memory_browsertest.cc index 5f15087..c096a41 100644 --- a/content/renderer/render_thread_impl_discardable_memory_browsertest.cc +++ b/content/renderer/render_thread_impl_discardable_memory_browsertest.cc
@@ -92,14 +92,8 @@ base::DiscardableMemoryAllocator* discardable_memory_allocator_; }; -// Flaky. http://crbug.com/1350563 -#if defined(THREAD_SANITIZER) -#define MAYBE_LockDiscardableMemory DISABLED_LockDiscardableMemory -#else -#define MAYBE_LockDiscardableMemory LockDiscardableMemory -#endif IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest, - MAYBE_LockDiscardableMemory) { + LockDiscardableMemory) { const size_t kSize = 1024 * 1024; // 1MiB. std::unique_ptr<base::DiscardableMemory> memory = @@ -154,14 +148,8 @@ } #endif -// Flaky. http://crbug.com/1350563 -#if defined(THREAD_SANITIZER) -#define MAYBE_ReleaseFreeDiscardableMemory DISABLED_ReleaseFreeDiscardableMemory -#else -#define MAYBE_ReleaseFreeDiscardableMemory ReleaseFreeDiscardableMemory -#endif IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest, - MAYBE_ReleaseFreeDiscardableMemory) { + ReleaseFreeDiscardableMemory) { const size_t kSize = 1024 * 1024; // 1MiB. base::DiscardableMemoryBacking impl = base::GetDiscardableMemoryBacking(); @@ -220,14 +208,8 @@ EXPECT_EQ(0U, discardable_memory_allocator()->GetBytesAllocated()); } -// Flaky. http://crbug.com/1350563 -#if defined(THREAD_SANITIZER) -#define MAYBE_CheckReleaseMemory DISABLED_CheckReleaseMemory -#else -#define MAYBE_CheckReleaseMemory CheckReleaseMemory -#endif IN_PROC_BROWSER_TEST_F(RenderThreadImplDiscardableMemoryBrowserTest, - MAYBE_CheckReleaseMemory) { + CheckReleaseMemory) { std::vector<std::unique_ptr<base::DiscardableMemory>> all_memory; auto* allocator = static_cast<discardable_memory::ClientDiscardableSharedMemoryManager*>(
diff --git a/content/test/data/accessibility/html/svg-expected-mac.txt b/content/test/data/accessibility/html/svg-expected-mac.txt index 0febc6e..22bff96 100644 --- a/content/test/data/accessibility/html/svg-expected-mac.txt +++ b/content/test/data/accessibility/html/svg-expected-mac.txt
@@ -1,5 +1,5 @@ AXWebArea ++AXGroup -++++AXImage AXDescription='svg' AXHelp='SVG Title Tag' +++++AXGroup AXDescription='svg' AXHelp='SVG Title Tag' ++++++AXGroup ++++++++AXStaticText AXValue='Test'
diff --git a/content/test/data/simple_links.html b/content/test/data/simple_links.html index 381ed5e..30fc53e 100644 --- a/content/test/data/simple_links.html +++ b/content/test/data/simple_links.html
@@ -31,6 +31,14 @@ return simulateClick(document.getElementById("same_site_new_window_link")); } + function clickSameSiteNewWindowWithNoopenerLink() { + return simulateClick(document.getElementById("same_site_new_window_with_noopener_link")); + } + + function clickSameSiteNewWindowWithOpenerLink() { + return simulateClick(document.getElementById("same_site_new_window_with_opener_link")); + } + function clickCrossSiteLink() { return simulateClick(document.getElementById("cross_site_link")); } @@ -60,7 +68,9 @@ <a href="title2.html" id="same_site_link">same-site</a><br> <a href="http://foo.com/title2.html" id="cross_site_link">cross-site</a><br> <a href="view-source:about:blank" id="view_source_link">view-source:</a><br> -<a href="title2.html" id="same_site_new_window_link" rel="opener" target="_blank">same-site new window</a> +<a href="title2.html" id="same_site_new_window_link" target="_blank">same-site new window</a> +<a href="title2.html" id="same_site_new_window_with_noopener_link" rel="noopener" target="_blank">same-site new window with noopener</a> +<a href="title2.html" id="same_site_new_window_with_opener_link" rel="opener" target="_blank">same-site new window with opener</a> <a href="http://foo.com/title2.html" id="cross_site_new_window_link" rel="opener" target="_blank">cross-site new window</a> <a href="http://foo.com/title2.html" id="cross_site_new_window_no_opener_link" rel="noopener" target="_blank">cross-site new window no opener</a> <a href="" id="linkToSelf" rel="opener" target="_blank">self new window</a>
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md index a1cfdb68..ce0326a 100644 --- a/docs/updater/functional_spec.md +++ b/docs/updater/functional_spec.md
@@ -382,6 +382,10 @@ Policies may be set by platform-specific means (group policy on Windows, managed preferences on macOS), or by communication with the device management server. +For device management, the enterprise policies for Google applications are +downloaded from the device management server periodically and stored at +`%ProgramFiles(x86)%\Google\Policies` in the Windows file system. + TODO(crbug.com/1339451): Document how conflicts between multiple policy sources are resolved.
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc index 0563edf..7ebf328 100644 --- a/extensions/browser/api/socket/socket_api.cc +++ b/extensions/browser/api/socket/socket_api.cc
@@ -23,6 +23,7 @@ #include "extensions/browser/api/socket/tls_socket.h" #include "extensions/browser/api/socket/udp_socket.h" #include "extensions/browser/extension_system.h" +#include "extensions/common/api/sockets/sockets_manifest_data.h" #include "extensions/common/extension.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/permissions/socket_permission.h" @@ -65,6 +66,7 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) const char kFirewallFailure[] = "Failed to open firewall port"; +const char kCrOSTerminal[] = "chrome-untrusted://terminal"; #endif // BUILDFLAG(IS_CHROMEOS_ASH) bool IsPortValid(int port) { @@ -85,19 +87,19 @@ } Socket* SocketApiFunction::GetSocket(int api_resource_id) { - return manager_->Get(extension_id(), api_resource_id); + return manager_->Get(GetOriginId(), api_resource_id); } void SocketApiFunction::ReplaceSocket(int api_resource_id, Socket* socket) { - manager_->Replace(extension_id(), api_resource_id, socket); + manager_->Replace(GetOriginId(), api_resource_id, socket); } std::unordered_set<int>* SocketApiFunction::GetSocketIds() { - return manager_->GetResourceIds(extension_id()); + return manager_->GetResourceIds(GetOriginId()); } void SocketApiFunction::RemoveSocket(int api_resource_id) { - manager_->Remove(extension_id(), api_resource_id); + manager_->Remove(GetOriginId(), api_resource_id); } std::unique_ptr<SocketResourceManagerInterface> @@ -125,7 +127,7 @@ AppFirewallHoleManager* manager = AppFirewallHoleManager::Get(browser_context()); std::unique_ptr<AppFirewallHole> hole( - manager->Open(type, local_address.port(), extension_id()).release()); + manager->Open(type, local_address.port(), GetOriginId()).release()); if (!hole) { Respond(ErrorWithCode(-1, kFirewallFailure)); @@ -157,6 +159,43 @@ return ErrorWithArguments(std::move(args), error); } +std::string SocketApiFunction::GetOriginId() const { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Terminal app is the only non-extension to use sockets (crbug.com/1350479). + if (!extension()) { + auto origin = url::Origin::Create(source_url()).Serialize(); + CHECK_EQ(origin, kCrOSTerminal); + return origin; + } +#endif + return extension_id(); +} + +bool SocketApiFunction::CheckPermission( + const APIPermission::CheckParam& param) const { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Terminal app is the only non-extension to use sockets (crbug.com/1350479). + if (!extension()) { + CHECK_EQ(url::Origin::Create(source_url()).Serialize(), kCrOSTerminal); + return true; + } +#endif + return extension()->permissions_data()->CheckAPIPermissionWithParam( + APIPermissionID::kSocket, ¶m); +} + +bool SocketApiFunction::CheckRequest( + const content::SocketPermissionRequest& param) const { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Terminal app is the only non-extension to use sockets (crbug.com/1350479). + if (!extension()) { + CHECK_EQ(url::Origin::Create(source_url()).Serialize(), kCrOSTerminal); + return true; + } +#endif + return SocketsManifestData::CheckRequest(extension(), param); +} + SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() = default; @@ -177,7 +216,8 @@ DCHECK(pending_host_resolver_); host_resolver_.Bind(std::move(pending_host_resolver_)); - url::Origin origin = extension_->origin(); + url::Origin origin = + extension() ? extension()->origin() : url::Origin::Create(source_url()); network::mojom::ResolveHostParametersPtr params = network::mojom::ResolveHostParameters::New(); params->dns_query_type = dns_query_type; @@ -220,7 +260,7 @@ Socket* socket = nullptr; switch (params->type) { case extensions::api::socket::SOCKET_TYPE_TCP: - socket = new TCPSocket(browser_context(), extension_id()); + socket = new TCPSocket(browser_context(), GetOriginId()); break; case extensions::api::socket::SOCKET_TYPE_UDP: { @@ -236,7 +276,7 @@ std::move(listener_remote)); socket = new UDPSocket(std::move(udp_socket), - std::move(socket_listener_receiver), extension_id()); + std::move(socket_listener_receiver), GetOriginId()); break; } case extensions::api::socket::SOCKET_TYPE_NONE: @@ -302,8 +342,7 @@ } SocketPermission::CheckParam param(operation_type, hostname_, port_); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermissionID::kSocket, ¶m)) { + if (!CheckPermission(param)) { return RespondNow(ErrorWithCode(-1, kPermissionError)); } @@ -488,7 +527,7 @@ if (result_code == net::OK) { Socket* client_socket = new TCPSocket(std::move(socket), std::move(receive_pipe_handle), - std::move(send_pipe_handle), remote_addr, extension_id()); + std::move(send_pipe_handle), remote_addr, GetOriginId()); result.SetIntKey(kSocketIdKey, AddSocket(client_socket)); } Respond(OneArgument(std::move(result))); @@ -640,8 +679,7 @@ if (socket->GetSocketType() == Socket::TYPE_UDP) { SocketPermission::CheckParam param( SocketPermissionRequest::UDP_SEND_TO, hostname_, port_); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermissionID::kSocket, ¶m)) { + if (!CheckPermission(param)) { return RespondNow(ErrorWithCode(-1, kPermissionError)); } } @@ -824,8 +862,7 @@ kWildcardAddress, kWildcardPort); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermissionID::kSocket, ¶m)) { + if (!CheckPermission(param)) { return RespondNow(ErrorWithCode(-1, kPermissionError)); } @@ -866,8 +903,7 @@ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kWildcardAddress, kWildcardPort); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermissionID::kSocket, ¶m)) { + if (!CheckPermission(param)) { return RespondNow(ErrorWithCode(-1, kPermissionError)); } @@ -966,8 +1002,7 @@ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, kWildcardAddress, kWildcardPort); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermissionID::kSocket, ¶m)) { + if (!CheckPermission(param)) { return RespondNow(ErrorWithCode(-1, kPermissionError)); } @@ -1028,7 +1063,7 @@ auto socket = std::make_unique<TLSSocket>(std::move(tls_socket), local_addr, peer_addr, std::move(receive_pipe_handle), - std::move(send_pipe_handle), extension_id()); + std::move(send_pipe_handle), GetOriginId()); ReplaceSocket(params_->socket_id, socket.release()); Respond(OneArgument(base::Value(result))); }
diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h index 0d0a635..ba622a6 100644 --- a/extensions/browser/api/socket/socket_api.h +++ b/extensions/browser/api/socket/socket_api.h
@@ -18,10 +18,12 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/socket_permission_request.h" #include "extensions/browser/api/api_resource_manager.h" #include "extensions/browser/api/async_api_function.h" #include "extensions/browser/extension_function.h" #include "extensions/common/api/socket.h" +#include "extensions/common/permissions/api_permission.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -138,6 +140,16 @@ // one integer value. ResponseValue ErrorWithCode(int error_code, const std::string& error); + // Either extension_id() or url origin for CrOS Terminal. + std::string GetOriginId() const; + + // Checks extension()->permissions_data(), or returns true for CrOS Terminal. + bool CheckPermission(const APIPermission::CheckParam& param) const; + + // Checks SocketsManifestData::CheckRequest() if extension(), or returns true + // for CrOS Terminal. + bool CheckRequest(const content::SocketPermissionRequest& param) const; + virtual std::unique_ptr<SocketResourceManagerInterface> CreateSocketResourceManager();
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc index ef163fe..50937379 100644 --- a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc +++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
@@ -18,7 +18,6 @@ #include "extensions/browser/api/socket/tcp_socket.h" #include "extensions/browser/api/socket/tls_socket.h" #include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h" -#include "extensions/common/api/sockets/sockets_manifest_data.h" #include "extensions/common/api/sockets_tcp.h" #include "net/base/net_errors.h" @@ -129,7 +128,7 @@ EXTENSION_FUNCTION_VALIDATE(params.get()); ResumableTCPSocket* socket = - new ResumableTCPSocket(browser_context(), extension_id()); + new ResumableTCPSocket(browser_context(), GetOriginId()); sockets_tcp::SocketProperties* properties = params->properties.get(); if (properties) { @@ -185,8 +184,7 @@ if (socket->paused() != params->paused) { socket->set_paused(params->paused); if (socket->IsConnected() && !params->paused) { - socket_event_dispatcher->OnSocketResume(extension_id(), - params->socket_id); + socket_event_dispatcher->OnSocketResume(GetOriginId(), params->socket_id); } } @@ -291,7 +289,7 @@ content::SocketPermissionRequest param(SocketPermissionRequest::TCP_CONNECT, params_->peer_address, params_->peer_port); - if (!SocketsManifestData::CheckRequest(extension(), param)) { + if (!CheckRequest(param)) { return RespondNow(Error(kPermissionError)); } @@ -322,7 +320,7 @@ void SocketsTcpConnectFunction::OnCompleted(int net_result) { if (net_result == net::OK) { - socket_event_dispatcher_->OnSocketConnect(extension_id(), + socket_event_dispatcher_->OnSocketConnect(GetOriginId(), params_->socket_id); } @@ -525,7 +523,7 @@ auto socket = std::make_unique<TLSSocket>(std::move(tls_socket), local_addr, peer_addr, std::move(receive_pipe_handle), - std::move(send_pipe_handle), extension_id()); + std::move(send_pipe_handle), GetOriginId()); socket->set_persistent(persistent_); socket->set_paused(paused_); ReplaceSocket(params_->socket_id, socket.release());
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h index b0d66d3..1d3b30b 100644 --- a/extensions/browser/app_window/app_window.h +++ b/extensions/browser/app_window/app_window.h
@@ -18,6 +18,7 @@ #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" +#include "extensions/browser/app_window/native_app_window.h" #include "extensions/browser/extension_function_dispatcher.h" #include "extensions/browser/extension_registry_observer.h" #include "ui/base/ui_base_types.h" // WindowShowState @@ -42,7 +43,6 @@ class AppDelegate; class AppWebContentsHelper; class Extension; -class NativeAppWindow; class PlatformAppBrowserTest; struct DraggableRegion; @@ -385,6 +385,11 @@ app_window_contents_ = std::move(contents); } + void SetNativeAppWindowForTesting( + std::unique_ptr<NativeAppWindow> native_app_window) { + native_app_window_ = std::move(native_app_window); + } + bool DidFinishFirstNavigation() { return did_finish_first_navigation_; } protected:
diff --git a/extensions/browser/network_permissions_updater.cc b/extensions/browser/network_permissions_updater.cc index 257e91f..17c7f91 100644 --- a/extensions/browser/network_permissions_updater.cc +++ b/extensions/browser/network_permissions_updater.cc
@@ -27,6 +27,7 @@ void NetworkPermissionsUpdater::UpdateExtension( content::BrowserContext& browser_context, const Extension& extension, + ContextSet context_set, base::OnceClosure completion_callback) { auto updater = std::make_unique<NetworkPermissionsUpdater>( PassKey(), browser_context, std::move(completion_callback)); @@ -35,7 +36,7 @@ // The callback takes ownership of `updater`, ensuring it's deleted when // the update completes. updater_raw->UpdateExtension( - extension, + extension, context_set, base::BindOnce(&NetworkPermissionsUpdater::OnOriginAccessUpdated, std::move(updater))); } @@ -58,20 +59,30 @@ base::BindOnce(&NetworkPermissionsUpdater::OnOriginAccessUpdated, std::move(updater))); - for (const auto& extension : extensions) - updater_raw->UpdateExtension(*extension, barrier_closure); + // When updating all extensions, we always use "all related contexts". + constexpr ContextSet kContextSet = ContextSet::kAllRelatedContexts; + + for (const auto& extension : extensions) { + updater_raw->UpdateExtension(*extension, kContextSet, barrier_closure); + } } void NetworkPermissionsUpdater::UpdateExtension( const Extension& extension, + ContextSet context_set, base::OnceClosure completion_callback) { - // Non-tab-specific extension permissions are shared across profiles (even for - // split-mode extensions), so we update all profiles the extension is enabled - // for. - util::SetCorsOriginAccessListForExtension( - ExtensionsBrowserClient::Get()->GetRelatedContextsForExtension( - browser_context_, extension), - extension, std::move(completion_callback)); + std::vector<content::BrowserContext*> target_contexts; + if (context_set == ContextSet::kCurrentContextOnly) { + target_contexts = {browser_context_.get()}; + } else { + DCHECK_EQ(ContextSet::kAllRelatedContexts, context_set); + target_contexts = + ExtensionsBrowserClient::Get()->GetRelatedContextsForExtension( + browser_context_, extension); + } + + util::SetCorsOriginAccessListForExtension(target_contexts, extension, + std::move(completion_callback)); } // static
diff --git a/extensions/browser/network_permissions_updater.h b/extensions/browser/network_permissions_updater.h index d7a95888..e95b8b2 100644 --- a/extensions/browser/network_permissions_updater.h +++ b/extensions/browser/network_permissions_updater.h
@@ -28,6 +28,17 @@ public: using PassKey = base::PassKey<NetworkPermissionsUpdater>; + // The contexts to include for when updating the extension. + enum class ContextSet { + // Only the current context will be updated. Use this when the permission + // is related to a specific context (like a specific tab). + kCurrentContextOnly, + // All related contexts the extension is allowed to run in will be updated. + // Use this when the permission is related to both contexts (like a + // permission grant on the extension). + kAllRelatedContexts, + }; + // Pseudo-private ctor. This is public so that it can be used with // std::make_unique<>, but guarded via the PassKey. Consumers should only use // the static methods below. @@ -40,11 +51,15 @@ // `completion_callback` when the operation is complete. static void UpdateExtension(content::BrowserContext& browser_context, const Extension& extension, + ContextSet context_set, base::OnceClosure completion_callback); // Updates the permissions of all extensions related to the (original) // `browser_context`. Invokes `completion_callback` when the operation is // complete. + // Updating all extensions always uses `ContextSet::kAllRelatedContexts` as + // there (currently) are no situations in which all extensions should be + // updated for a context-specific reason. static void UpdateAllExtensions(content::BrowserContext& browser_context, base::OnceClosure completion_callback); @@ -52,6 +67,7 @@ // Updates a single extension in the network layer, invoking // `completion_callback` when the operation is complete. void UpdateExtension(const Extension& extension, + ContextSet context_set, base::OnceClosure completion_callback); // Invoked when all updates are complete in order to dispatch
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc index 7a482889..97fbee44 100644 --- a/extensions/browser/renderer_startup_helper.cc +++ b/extensions/browser/renderer_startup_helper.cc
@@ -22,9 +22,9 @@ #include "extensions/browser/extension_util.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/guest_view/web_view/web_view_guest.h" +#include "extensions/browser/network_permissions_updater.h" #include "extensions/browser/service_worker_task_queue.h" #include "extensions/common/activation_sequence.h" -#include "extensions/common/cors_util.h" #include "extensions/common/extension_messages.h" #include "extensions/common/extension_set.h" #include "extensions/common/extensions_client.h" @@ -255,10 +255,13 @@ // always be called before creating URLLoaderFactory for any extension frames // that might be eventually hosted inside the renderer `process` (this // Browser-side ordering will be replicated within the NetworkService because - // SetCorsOriginAccessListsForOrigin and CreateURLLoaderFactory are 2 methods + // `SetCorsOriginAccessListsForOrigin()`, which is used in + // NetworkPermissionsUpdater, and `CreateURLLoaderFactory()` are 2 methods // of the same mojom::NetworkContext interface). - util::SetCorsOriginAccessListForExtension({process->GetBrowserContext()}, - extension, base::DoNothing()); + NetworkPermissionsUpdater::UpdateExtension( + *process->GetBrowserContext(), extension, + NetworkPermissionsUpdater::ContextSet::kCurrentContextOnly, + base::DoNothing()); auto remote = process_mojo_map_.find(process); if (remote != process_mojo_map_.end()) {
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index bb5bae5..aca17d0 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json
@@ -584,10 +584,16 @@ "dependencies": ["permission:socket"], "contexts": ["blessed_extension"] }, - "sockets.tcp": { + "sockets.tcp": [{ "dependencies": ["manifest:sockets"], "contexts": ["blessed_extension"] - }, + },{ + "channel": "stable", + "contexts": ["webui_untrusted"], + "matches": [ + "chrome-untrusted://terminal/*" + ] + }], "sockets.tcpServer": { "dependencies": ["manifest:sockets"], "contexts": ["blessed_extension"]
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc index 60567f3..ea3139b 100644 --- a/extensions/common/extension_features.cc +++ b/extensions/common/extension_features.cc
@@ -46,9 +46,14 @@ const base::Feature kAllowSharedArrayBuffersUnconditionally{ "AllowSharedArrayBuffersUnconditionally", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enables the CryptoToken component extension, which implements the deprecated -// U2F Security Key API. Once this flag is default disabled sites can continue -// to use CryptoToken via a Deprecation Trail with the same name. +// Loads the CryptoToken component extension, which implements the deprecated +// U2F Security Key API. +// TODO(1224886): Delete together with CryptoToken code. +const base::Feature kLoadCryptoTokenExtension{ + "LoadCryptoTokenExtension", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Enables the CryptoToken component extension to receive messages. This flag +// has no effect unless `kLoadCryptoTokenExtension` is also enabled. // TODO(1224886): Delete together with CryptoToken code. const base::Feature kU2FSecurityKeyAPI{"U2FSecurityKeyAPI", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h index 100295c4..9e7c281 100644 --- a/extensions/common/extension_features.h +++ b/extensions/common/extension_features.h
@@ -22,6 +22,8 @@ extern const base::Feature kAllowSharedArrayBuffersUnconditionally; +extern const base::Feature kLoadCryptoTokenExtension; + extern const base::Feature kU2FSecurityKeyAPI; extern const base::Feature kStructuredCloningForMV3Messaging;
diff --git a/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json index 550ddce..a7e9bb75 100644 --- a/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json +++ b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
@@ -59,5 +59,5 @@ }, "builder_group": "chromium.fyi", "recipe": "chromium", - "xcode_build_version": "14a5284g" + "xcode_build_version": "14a5294e" } \ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json index 833d817..0347bc18 100644 --- a/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json +++ b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
@@ -53,5 +53,5 @@ }, "builder_group": "tryserver.chromium.mac", "recipe": "chromium_trybot", - "xcode_build_version": "14a5284g" + "xcode_build_version": "14a5294e" } \ No newline at end of file
diff --git a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.json b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.json index 4a1ddcf..c1f79de 100644 --- a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.json +++ b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.json
@@ -13,5 +13,5 @@ }, "builder_group": "chromium.webrtc.fyi", "recipe": "chromium", - "xcode_build_version": "13c100" + "xcode_build_version": "14a5284g" } \ No newline at end of file
diff --git a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.json b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.json index 4a1ddcf..c1f79de 100644 --- a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.json +++ b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.json
@@ -13,5 +13,5 @@ }, "builder_group": "chromium.webrtc.fyi", "recipe": "chromium", - "xcode_build_version": "13c100" + "xcode_build_version": "14a5284g" } \ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 3903ce0d..c2194c9 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -33697,8 +33697,8 @@ '}' execution_timeout_secs: 36000 caches { - name: "xcode_ios_14a5284g" - path: "xcode_ios_14a5284g.app" + name: "xcode_ios_14a5294e" + path: "xcode_ios_14a5294e.app" } build_numbers: YES service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" @@ -66197,8 +66197,8 @@ path: "win_toolchain" } caches { - name: "xcode_ios_14a5284g" - path: "xcode_ios_14a5284g.app" + name: "xcode_ios_14a5294e" + path: "xcode_ios_14a5294e.app" } build_numbers: YES service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" @@ -82205,8 +82205,8 @@ '}' execution_timeout_secs: 7200 caches { - name: "xcode_ios_13c100" - path: "xcode_ios_13c100.app" + name: "xcode_ios_14a5284g" + path: "xcode_ios_14a5284g.app" } build_numbers: YES service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" @@ -82256,8 +82256,8 @@ '}' execution_timeout_secs: 7200 caches { - name: "xcode_ios_13c100" - path: "xcode_ios_13c100.app" + name: "xcode_ios_14a5284g" + path: "xcode_ios_14a5284g.app" } build_numbers: YES service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star index cc387dc..7dea943 100644 --- a/infra/config/lib/builders.star +++ b/infra/config/lib/builders.star
@@ -188,9 +188,7 @@ # Xcode14 beta 4 will be used to build Main iOS x14main = xcode_enum("14a5284g"), # A newer Xcode 14 version used on beta bots. - x14betabots = xcode_enum("14a5284g"), - # Xcode14 beta 5 used on beta bots. - x14beta5bots = xcode_enum("14a5294e"), + x14betabots = xcode_enum("14a5294e"), # in use by ios-webkit-tot x13wk = xcode_enum("13a1030dwk"), )
diff --git a/infra/config/subprojects/webrtc/webrtc.fyi.star b/infra/config/subprojects/webrtc/webrtc.fyi.star index 2212402..146dadf 100644 --- a/infra/config/subprojects/webrtc/webrtc.fyi.star +++ b/infra/config/subprojects/webrtc/webrtc.fyi.star
@@ -133,12 +133,12 @@ name = "WebRTC Chromium FYI ios-device", goma_backend = goma.backend.RBE_PROD, os = os.MAC_ANY, - xcode = xcode.x13main, + xcode = xcode.x14main, ) builder( name = "WebRTC Chromium FYI ios-simulator", goma_backend = goma.backend.RBE_PROD, os = os.MAC_ANY, - xcode = xcode.x13main, + xcode = xcode.x14main, )
diff --git a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm index a996495..77f90cb4 100644 --- a/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm +++ b/ios/chrome/browser/ui/authentication/signin/forced_signin/forced_signin_coordinator.mm
@@ -148,13 +148,13 @@ // This is called before finishing the presentation of a screen. // Stops the child coordinator and prepares the next screen to present. -- (void)willFinishPresenting { +- (void)screenWillFinishPresenting { [self.childCoordinator stop]; self.childCoordinator = nil; [self presentScreen:[self.screenProvider nextScreenType]]; } -- (void)skipAll { +- (void)skipAllScreens { [self finishPresentingScreens]; }
diff --git a/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_coordinator.mm b/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_coordinator.mm index ac3c905..1c82fdb 100644 --- a/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_coordinator.mm +++ b/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_coordinator.mm
@@ -171,7 +171,7 @@ // performed with irregular states. We expect sync to be disabled when the // FRE is displayed in a regular situation (i.e., first launch after // install). - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; return; } @@ -419,9 +419,9 @@ } if (skipRemainingScreens) { - [self.delegate skipAll]; + [self.delegate skipAllScreens]; } else { - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } }
diff --git a/ios/chrome/browser/ui/first_run/default_browser/default_browser_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/default_browser/default_browser_screen_coordinator.mm index 4ffe0a7..8bc21f5 100644 --- a/ios/chrome/browser/ui/first_run/default_browser/default_browser_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/default_browser/default_browser_screen_coordinator.mm
@@ -72,7 +72,7 @@ openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:{} completionHandler:nil]; - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } - (void)didTapSecondaryActionButton { @@ -80,7 +80,7 @@ "FirstRun.Stage", first_run::kDefaultBrowserScreenCompletionWithoutSettings); LogUserInteractionWithFirstRunPromo(NO); - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } @end
diff --git a/ios/chrome/browser/ui/first_run/first_run_coordinator.mm b/ios/chrome/browser/ui/first_run/first_run_coordinator.mm index 59d12d8..b885af9 100644 --- a/ios/chrome/browser/ui/first_run/first_run_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/first_run_coordinator.mm
@@ -90,7 +90,7 @@ #pragma mark - FirstRunScreenDelegate -- (void)willFinishPresenting { +- (void)screenWillFinishPresenting { [self.childCoordinator stop]; self.childCoordinator = nil; // Usually, finishing presenting the first FRE screen signifies that the user @@ -106,7 +106,7 @@ [self presentScreen:[self.screenProvider nextScreenType]]; } -- (void)skipAll { +- (void)skipAllScreens { [self.childCoordinator stop]; self.childCoordinator = nil; [self willFinishPresentingScreens];
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h b/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h index 1ff5525f..17a97f3 100644 --- a/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h +++ b/ios/chrome/browser/ui/first_run/first_run_screen_delegate.h
@@ -9,10 +9,10 @@ @protocol FirstRunScreenDelegate <NSObject> // Called when one screen finished presenting. -- (void)willFinishPresenting; +- (void)screenWillFinishPresenting; // Called when user want to skip all screens after. -- (void)skipAll; +- (void)skipAllScreens; @end
diff --git a/ios/chrome/browser/ui/first_run/legacy_signin/legacy_signin_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/legacy_signin/legacy_signin_screen_coordinator.mm index 7dc52f14..7d718ef 100644 --- a/ios/chrome/browser/ui/first_run/legacy_signin/legacy_signin_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/legacy_signin/legacy_signin_screen_coordinator.mm
@@ -119,7 +119,7 @@ // Don't show sign in screen if there is already an account signed in (for // example going through the FRE then killing the app and restarting the // FRE). Don't record any metric as the user didn't take any action. - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; return; } @@ -306,9 +306,9 @@ self.hadIdentitiesAtStartup); } if (skipRemainingScreens) { - [self.delegate skipAll]; + [self.delegate skipAllScreens]; } else { - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } }
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm index 22ff212..a5ac866 100644 --- a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
@@ -197,7 +197,7 @@ // Calls the mediator and the delegate when the coordinator is finished. - (void)finishPresentingWithSignIn:(BOOL)signIn { [self.mediator finishPresentingWithSignIn:signIn]; - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } // Shows the UMA dialog so the user can manage metric reporting.
diff --git a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm index 765fff5..5314103 100644 --- a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator.mm
@@ -106,7 +106,7 @@ if (!authenticationService->GetPrimaryIdentity( signin::ConsentLevel::kSignin)) { // Don't show sync screen if no logged-in user account. - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; return; } @@ -120,7 +120,7 @@ syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) || syncSetupService->IsFirstSetupComplete(); if (shouldSkipSyncScreen) { - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; return; } @@ -203,7 +203,7 @@ // user entered it before canceling the sync opt-in flow, and also to set // sync as requested. syncService->StopAndClear(); - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } - (void)didTapURLInDisclaimer:(NSURL*)URL { @@ -234,12 +234,12 @@ base::UmaHistogramEnumeration("FirstRun.Stage", first_run::kSyncScreenCompletionWithSync); } - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } } - (void)userRemoved { - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } #pragma mark - PolicyWatcherBrowserAgentObserving @@ -285,7 +285,7 @@ - (void)dismissSignedOutModalAndSkipScreens:(BOOL)skipScreens { [self.enterprisePromptCoordinator stop]; self.enterprisePromptCoordinator = nil; - [self.delegate skipAll]; + [self.delegate skipAllScreens]; } // Starts syncing or opens `advancedSettings`.
diff --git a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator_unittest.mm b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator_unittest.mm index 4ec318f..e79074cd 100644 --- a/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator_unittest.mm +++ b/ios/chrome/browser/ui/first_run/sync/sync_screen_coordinator_unittest.mm
@@ -116,7 +116,7 @@ // Tests that calling the delegate immidiately to stop the coordinator when // there's no user identity. TEST_F(SyncScreenCoordinatorTest, TestStartWithoutIdentity) { - OCMExpect([delegate_ willFinishPresenting]); + OCMExpect([delegate_ screenWillFinishPresenting]); [coordinator_ start]; EXPECT_OCMOCK_VERIFY(delegate_); @@ -134,7 +134,7 @@ auth_service_->SignIn(identity, nil); - OCMExpect([delegate_ willFinishPresenting]); + OCMExpect([delegate_ screenWillFinishPresenting]); [coordinator_ start]; EXPECT_OCMOCK_VERIFY(delegate_); @@ -153,7 +153,7 @@ auth_service_->SignIn(identity, nil); - OCMExpect([delegate_ willFinishPresenting]); + OCMExpect([delegate_ screenWillFinishPresenting]); [coordinator_ start]; EXPECT_OCMOCK_VERIFY(delegate_);
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm index 4a26de1..6988ae7 100644 --- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm +++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm
@@ -114,7 +114,7 @@ base::RecordAction(base::UserMetricsAction("MobileFreUMALinkTapped")); } - [self.delegate willFinishPresenting]; + [self.delegate screenWillFinishPresenting]; } #pragma mark - TOSCommands
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 87b7f16f..622adc0 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -338,10 +338,10 @@ if (is_win || use_vaapi) { sources += [ + "video_rate_control.cc", + "video_rate_control.h", "vp9_svc_layers.cc", "vp9_svc_layers.h", - "vpx_rate_control.cc", - "vpx_rate_control.h", ] configs += [ "//third_party/libvpx:libvpx_config" ] deps += [ "//third_party/libvpx:libvpxrc" ]
diff --git a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.h b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.h index a67bc6c..732eebe 100644 --- a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.h +++ b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.h
@@ -9,9 +9,9 @@ #include "media/base/video_bitrate_allocation.h" #include "media/gpu/vaapi/vaapi_video_encoder_delegate.h" +#include "media/gpu/video_rate_control.h" #include "media/gpu/vp8_picture.h" #include "media/gpu/vp8_reference_frame_vector.h" -#include "media/gpu/vpx_rate_control.h" #include "media/parsers/vp8_parser.h" namespace libvpx { @@ -95,9 +95,9 @@ Vp8ReferenceFrameVector reference_frames_; - using VP8RateControl = VPXRateControl<libvpx::VP8RateControlRtcConfig, - libvpx::VP8RateControlRTC, - libvpx::VP8FrameParamsQpRTC>; + using VP8RateControl = VideoRateControl<libvpx::VP8RateControlRtcConfig, + libvpx::VP8RateControlRTC, + libvpx::VP8FrameParamsQpRTC>; std::unique_ptr<VP8RateControl> rate_ctrl_; };
diff --git a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc index ad8fe8ae..a58e659 100644 --- a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc +++ b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc
@@ -17,8 +17,8 @@ #include "media/gpu/macros.h" #include "media/gpu/vaapi/vaapi_common.h" #include "media/gpu/vaapi/vaapi_wrapper.h" +#include "media/gpu/video_rate_control.h" #include "media/gpu/vp9_svc_layers.h" -#include "media/gpu/vpx_rate_control.h" #include "third_party/libvpx/source/libvpx/vp9/ratectrl_rtc.h" namespace media {
diff --git a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h index 3bdac33c..d135b0b 100644 --- a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h +++ b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h
@@ -12,9 +12,9 @@ #include "media/base/video_bitrate_allocation.h" #include "media/filters/vp9_parser.h" #include "media/gpu/vaapi/vaapi_video_encoder_delegate.h" +#include "media/gpu/video_rate_control.h" #include "media/gpu/vp9_picture.h" #include "media/gpu/vp9_reference_frame_vector.h" -#include "media/gpu/vpx_rate_control.h" namespace libvpx { struct VP9FrameParamsQpRTC; @@ -68,9 +68,9 @@ friend class VP9VaapiVideoEncoderDelegateTest; friend class VaapiVideoEncodeAcceleratorTest; - using VP9RateControl = VPXRateControl<libvpx::VP9RateControlRtcConfig, - libvpx::VP9RateControlRTC, - libvpx::VP9FrameParamsQpRTC>; + using VP9RateControl = VideoRateControl<libvpx::VP9RateControlRtcConfig, + libvpx::VP9RateControlRTC, + libvpx::VP9FrameParamsQpRTC>; void set_rate_ctrl_for_testing(std::unique_ptr<VP9RateControl> rate_ctrl); bool ApplyPendingUpdateRates();
diff --git a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate_unittest.cc b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate_unittest.cc index 423abd5b..d966194 100644 --- a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate_unittest.cc +++ b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate_unittest.cc
@@ -20,8 +20,8 @@ #include "media/gpu/gpu_video_encode_accelerator_helpers.h" #include "media/gpu/vaapi/vaapi_common.h" #include "media/gpu/vaapi/vaapi_wrapper.h" +#include "media/gpu/video_rate_control.h" #include "media/gpu/vp9_svc_layers.h" -#include "media/gpu/vpx_rate_control.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -240,9 +240,9 @@ }; class MockVP9RateControl - : public VPXRateControl<libvpx::VP9RateControlRtcConfig, - libvpx::VP9RateControlRTC, - libvpx::VP9FrameParamsQpRTC> { + : public VideoRateControl<libvpx::VP9RateControlRtcConfig, + libvpx::VP9RateControlRTC, + libvpx::VP9FrameParamsQpRTC> { public: MockVP9RateControl() = default; ~MockVP9RateControl() override = default;
diff --git a/media/gpu/vpx_rate_control.cc b/media/gpu/video_rate_control.cc similarity index 66% rename from media/gpu/vpx_rate_control.cc rename to media/gpu/video_rate_control.cc index 931eba3..90ce362 100644 --- a/media/gpu/vpx_rate_control.cc +++ b/media/gpu/video_rate_control.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/gpu/vpx_rate_control.h" +#include "media/gpu/video_rate_control.h" #include "third_party/libvpx/source/libvpx/vp9/ratectrl_rtc.h" @@ -11,10 +11,9 @@ // Template method specialization for VP9. // TODO(mcasas): Remove when VP8 also has a GetLoopfilterLevel() method. template <> -int VPXRateControl<libvpx::VP9RateControlRtcConfig, - libvpx::VP9RateControlRTC, - libvpx::VP9RateControlRtcConfig>::GetLoopfilterLevel() - const { +int VideoRateControl<libvpx::VP9RateControlRtcConfig, + libvpx::VP9RateControlRTC, + libvpx::VP9FrameParamsQpRTC>::GetLoopfilterLevel() const { return impl_->GetLoopfilterLevel(); }
diff --git a/media/gpu/vpx_rate_control.h b/media/gpu/video_rate_control.h similarity index 65% rename from media/gpu/vpx_rate_control.h rename to media/gpu/video_rate_control.h index 61831d9..c5666505 100644 --- a/media/gpu/vpx_rate_control.h +++ b/media/gpu/video_rate_control.h
@@ -2,35 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_GPU_VPX_RATE_CONTROL_H_ -#define MEDIA_GPU_VPX_RATE_CONTROL_H_ +#ifndef MEDIA_GPU_VIDEO_RATE_CONTROL_H_ +#define MEDIA_GPU_VIDEO_RATE_CONTROL_H_ #include <memory> #include "base/logging.h" namespace media { -// VPXRateControl is an interface to compute proper quantization +// VideoRateControl is an interface to compute proper quantization // parameter and loop filter level for vp8 and vp9. // T is a libvpx::VP(8|9)RateControlRtcConfig // S is a libvpx::VP(8|9)RateControlRTC -// U is a libvpx::VP(8|9)RateControlRtcConfig +// U is a libvpx::VP(8|9)FrameParamsQpRTC template <typename T, typename S, typename U> -class VPXRateControl { +class VideoRateControl { public: - // Creates VPXRateControl using libvpx implementation. - static std::unique_ptr<VPXRateControl> Create(const T& config) { + // Creates VideoRateControl using libvpx implementation. + static std::unique_ptr<VideoRateControl> Create(const T& config) { auto impl = S::Create(config); if (!impl) { - DLOG(ERROR) << "Failed creating libvpx's VPxRateControlRTC"; + DLOG(ERROR) << "Failed creating video RateControlRTC"; return nullptr; } - return std::make_unique<VPXRateControl>(std::move(impl)); + return std::make_unique<VideoRateControl>(std::move(impl)); } - VPXRateControl() = default; - explicit VPXRateControl(std::unique_ptr<S> impl) : impl_(std::move(impl)) {} - virtual ~VPXRateControl() = default; + VideoRateControl() = default; + explicit VideoRateControl(std::unique_ptr<S> impl) : impl_(std::move(impl)) {} + virtual ~VideoRateControl() = default; virtual void UpdateRateControl(const T& rate_control_config) { impl_->UpdateRateControl(rate_control_config); @@ -52,4 +52,4 @@ }; } // namespace media -#endif // MEDIA_GPU_VPX_RATE_CONTROL_H_ +#endif // MEDIA_GPU_VIDEO_RATE_CONTROL_H_
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc index a28e44cc..3b424904 100644 --- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc +++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -66,6 +66,8 @@ const size_t kMaxFrameRateDenominator = 1; const size_t kMaxResolutionWidth = 1920; const size_t kMaxResolutionHeight = 1088; +const size_t kMinResolutionWidth = 32; +const size_t kMinResolutionHeight = 32; const size_t kNumInputBuffers = 3; // Media Foundation uses 100 nanosecond units for time, see // https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx. @@ -388,6 +390,7 @@ profile.max_framerate_denominator = kMaxFrameRateDenominator; profile.rate_control_modes = kSupportedProfileModes; profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight); + profile.min_resolution = gfx::Size(kMinResolutionWidth, kMinResolutionHeight); if (svc_supported) { profile.scalability_modes.push_back(SVCScalabilityMode::kL1T2); profile.scalability_modes.push_back(SVCScalabilityMode::kL1T3);
diff --git a/media/video/openh264_video_encoder.cc b/media/video/openh264_video_encoder.cc index 86f37258..6f55b7c 100644 --- a/media/video/openh264_video_encoder.cc +++ b/media/video/openh264_video_encoder.cc
@@ -133,6 +133,12 @@ return; } + if (options.frame_size.height() < 16 || options.frame_size.width() < 16) { + std::move(done_cb).Run( + EncoderStatus(EncoderStatus::Codes::kEncoderInitializationError, + "Unsupported frame size which is less than 16")); + return; + } SetUpOpenH264Params(options, ¶ms); if (int err = codec->InitializeExt(¶ms)) {
diff --git a/mojo/public/cpp/bindings/array_data_view.h b/mojo/public/cpp/bindings/array_data_view.h index 0c5b79a..adfab0a 100644 --- a/mojo/public/cpp/bindings/array_data_view.h +++ b/mojo/public/cpp/bindings/array_data_view.h
@@ -7,7 +7,6 @@ #include <type_traits> -#include "base/memory/raw_ptr.h" #include "base/memory/raw_ptr_exclusion.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" @@ -40,10 +39,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -62,10 +61,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -94,10 +93,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -126,10 +125,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -153,10 +152,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -185,10 +184,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; template <typename T> @@ -214,10 +213,10 @@ protected: // `data_` is not a raw_ptr<...> for performance reasons (based on analysis of // sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Data_> data_; + RAW_PTR_EXCLUSION Data_* data_; // `message_` is not a raw_ptr<...> for performance reasons (based on analysis // of sampling profiler data). - RAW_PTR_EXCLUSION raw_ptr<Message> message_; + RAW_PTR_EXCLUSION Message* message_; }; } // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/send_message_helper.cc b/mojo/public/cpp/bindings/lib/send_message_helper.cc index 13e1be4..30f00026 100644 --- a/mojo/public/cpp/bindings/lib/send_message_helper.cc +++ b/mojo/public/cpp/bindings/lib/send_message_helper.cc
@@ -12,7 +12,7 @@ namespace mojo { namespace internal { -void SendMessage(MessageReceiver& receiver, Message& message) { +void SendMojoMessage(MessageReceiver& receiver, Message& message) { uint64_t flow_id = message.GetTraceId(); bool is_sync_non_response = message.has_flag(Message::kFlagIsSync) && !message.has_flag(Message::kFlagIsResponse); @@ -29,9 +29,9 @@ } } -void SendMessage(MessageReceiverWithResponder& receiver, - Message& message, - std::unique_ptr<MessageReceiver> responder) { +void SendMojoMessage(MessageReceiverWithResponder& receiver, + Message& message, + std::unique_ptr<MessageReceiver> responder) { uint64_t flow_id = message.GetTraceId(); bool is_sync_non_response = message.has_flag(Message::kFlagIsSync) && !message.has_flag(Message::kFlagIsResponse);
diff --git a/mojo/public/cpp/bindings/lib/send_message_helper.h b/mojo/public/cpp/bindings/lib/send_message_helper.h index 1a10da9..d92f083523 100644 --- a/mojo/public/cpp/bindings/lib/send_message_helper.h +++ b/mojo/public/cpp/bindings/lib/send_message_helper.h
@@ -18,12 +18,12 @@ // trace events can be performed without affecting the binary size of the // generated bindings. COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) -void SendMessage(MessageReceiverWithResponder& receiver, - Message& message, - std::unique_ptr<MessageReceiver> responder); +void SendMojoMessage(MessageReceiverWithResponder& receiver, + Message& message, + std::unique_ptr<MessageReceiver> responder); COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) -void SendMessage(MessageReceiver& receiver, Message& message); +void SendMojoMessage(MessageReceiver& receiver, Message& message); } // namespace internal } // namespace mojo
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index d538eaa..9edd2aa 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -235,7 +235,7 @@ {%- for param in method.response_parameters -%} , out_param_{{param.name}} {%- endfor %})); - ::mojo::internal::SendMessage(*receiver_, message, std::move(responder)); + ::mojo::internal::SendMojoMessage(*receiver_, message, std::move(responder)); #if BUILDFLAG(MOJO_TRACE_ENABLED) {{interface_macros.trace_event(prefix="out_param_", method_parameters=method.response_parameters, @@ -289,11 +289,11 @@ std::unique_ptr<mojo::MessageReceiver> responder( new {{class_name}}_{{method.name}}_ForwardToCallback( std::move(callback))); - ::mojo::internal::SendMessage(*receiver_, message, std::move(responder)); + ::mojo::internal::SendMojoMessage(*receiver_, message, std::move(responder)); {%- else %} // This return value may be ignored as false implies the Connector has // encountered an error, which will be visible through other means. - ::mojo::internal::SendMessage(*receiver_, message); + ::mojo::internal::SendMojoMessage(*receiver_, message); {%- endif %} } {%- endfor %} @@ -424,8 +424,8 @@ message.set_request_id(request_id_); message.set_trace_nonce(trace_nonce_); - ::mojo::internal::SendMessage(*responder_, message); - // SendMessage fails silently if the responder connection is closed, + ::mojo::internal::SendMojoMessage(*responder_, message); + // SendMojoMessage() fails silently if the responder connection is closed, // or if the message is malformed. // // TODO(darin): If Accept() returns false due to a malformed message, that
diff --git a/mojo/public/tools/mojom/mojom/generate/translate.py b/mojo/public/tools/mojom/mojom/generate/translate.py index 727a3508..0be7732 100644 --- a/mojo/public/tools/mojom/mojom/generate/translate.py +++ b/mojo/public/tools/mojom/mojom/generate/translate.py
@@ -576,7 +576,11 @@ return kind if spec.startswith('?'): - kind = _Kind(kinds, spec[1:], scope).MakeNullableKind() + kind = _Kind(kinds, spec[1:], scope) + if not mojom.IsReferenceKind(kind): + raise Exception('Unknown spec: %s. Check for missing imports.' % spec) + + kind = kind.MakeNullableKind() elif spec.startswith('a:'): kind = mojom.Array(_Kind(kinds, spec[2:], scope)) elif spec.startswith('asso:'):
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc index f554d0ac..f238eed 100644 --- a/net/cert/cert_verify_proc_builtin_unittest.cc +++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -223,12 +223,13 @@ // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials| // as revoked, and registers it to be served by the test server. // Returns the full URL to retrieve the CRL from the test server. - GURL CreateAndServeCrl(EmbeddedTestServer* test_server, - CertBuilder* crl_issuer, - const std::vector<uint64_t>& revoked_serials, - DigestAlgorithm digest = DigestAlgorithm::Sha256) { + GURL CreateAndServeCrl( + EmbeddedTestServer* test_server, + CertBuilder* crl_issuer, + const std::vector<uint64_t>& revoked_serials, + absl::optional<SignatureAlgorithm> signature_algorithm = absl::nullopt) { std::string crl = BuildCrl(crl_issuer->GetSubject(), crl_issuer->GetKey(), - revoked_serials, digest); + revoked_serials, signature_algorithm); std::string crl_path = MakeRandomPath(".crl"); test_server->RegisterRequestHandler( base::BindRepeating(&test_server::HandlePrefixedRequest, crl_path,
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc index b5c522cb..7f22a56 100644 --- a/net/cert/cert_verify_proc_unittest.cc +++ b/net/cert/cert_verify_proc_unittest.cc
@@ -629,11 +629,19 @@ ASSERT_EQ(3U, orig_certs.size()); for (bool trust_the_intermediate : {false, true}) { + SCOPED_TRACE(trust_the_intermediate); + // Need to build unique certs for each try otherwise caching can break // things. CertBuilder root(orig_certs[2]->cert_buffer(), nullptr); + root.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + root.GenerateECKey(); CertBuilder intermediate(orig_certs[1]->cert_buffer(), &root); + intermediate.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + intermediate.GenerateECKey(); CertBuilder leaf(orig_certs[0]->cert_buffer(), &intermediate); + leaf.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + leaf.GenerateECKey(); // The policy that "explicit-policy-chain.pem" target certificate asserts. static const char kEVTestCertPolicy[] = "1.2.3.4"; @@ -3119,11 +3127,12 @@ // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials| // as revoked, and registers it to be served by the test server. // Returns the full URL to retrieve the CRL from the test server. - GURL CreateAndServeCrl(CertBuilder* crl_issuer, - const std::vector<uint64_t>& revoked_serials, - DigestAlgorithm digest = DigestAlgorithm::Sha256) { + GURL CreateAndServeCrl( + CertBuilder* crl_issuer, + const std::vector<uint64_t>& revoked_serials, + absl::optional<SignatureAlgorithm> signature_algorithm = absl::nullopt) { std::string crl = BuildCrl(crl_issuer->GetSubject(), crl_issuer->GetKey(), - revoked_serials, digest); + revoked_serials, signature_algorithm); std::string crl_path = MakeRandomPath(".crl"); return RegisterSimpleTestServerHandler(crl_path, HTTP_OK, "application/pkix-crl", crl); @@ -3433,8 +3442,13 @@ // Build slightly modified variants of |orig_certs|. CertBuilder root(orig_certs[2]->cert_buffer(), nullptr); + root.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + root.GenerateECKey(); CertBuilder intermediate(orig_certs[1]->cert_buffer(), &root); + intermediate.GenerateECKey(); CertBuilder leaf(orig_certs[0]->cert_buffer(), &intermediate); + leaf.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + leaf.GenerateECKey(); // Make the leaf certificate have an AIA (CA Issuers) that points to the // embedded test server. This uses a random URL for predictable behavior in @@ -3448,11 +3462,11 @@ // that is SHA1 signed. Note that the subjectKeyIdentifier for `intermediate` // is intentionally not changed, so that path building will consider both // certificate paths. - intermediate.SetSignatureAlgorithmRsaPkca1(DigestAlgorithm::Sha256); + intermediate.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); intermediate.SetRandomSerialNumber(); auto intermediate_sha256 = intermediate.DupCertBuffer(); - intermediate.SetSignatureAlgorithmRsaPkca1(DigestAlgorithm::Sha1); + intermediate.SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha1); intermediate.SetRandomSerialNumber(); auto intermediate_sha1 = intermediate.DupCertBuffer(); @@ -4003,9 +4017,10 @@ intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); // Leaf is revoked by intermediate issued CRL which is signed with - // sha1WithRSAEncryption. - leaf->SetCrlDistributionPointUrl(CreateAndServeCrl( - intermediate.get(), {leaf->GetSerialNumber()}, DigestAlgorithm::Sha1)); + // ecdsaWithSha256. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()}, + SignatureAlgorithm::kEcdsaSha1)); // Trust the root and build a chain to verify that includes the intermediate. ScopedTestRoot scoped_root(root->GetX509Certificate().get()); @@ -4045,11 +4060,17 @@ // Root-issued CRL which does not revoke intermediate. intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + // This test wants to check handling of MD5 CRLs, but ecdsa-with-md5 + // signatureAlgorithm does not exist. Use an RSA private key for intermediate + // so that the CRL will be signed with the md5WithRSAEncryption algorithm. + intermediate->GenerateRSAKey(); + leaf->SetSignatureAlgorithm(SignatureAlgorithm::kRsaPkcs1Sha256); // Leaf is revoked by intermediate issued CRL which is signed with // md5WithRSAEncryption. - leaf->SetCrlDistributionPointUrl(CreateAndServeCrl( - intermediate.get(), {leaf->GetSerialNumber()}, DigestAlgorithm::Md5)); + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()}, + SignatureAlgorithm::kRsaPkcs1Md5)); // Trust the root and build a chain to verify that includes the intermediate. ScopedTestRoot scoped_root(root->GetX509Certificate().get());
diff --git a/net/cert/internal/revocation_checker_unittest.cc b/net/cert/internal/revocation_checker_unittest.cc index 50c1f2f..1ad96505 100644 --- a/net/cert/internal/revocation_checker_unittest.cc +++ b/net/cert/internal/revocation_checker_unittest.cc
@@ -120,7 +120,7 @@ std::string crl_data_as_string_for_some_reason = BuildCrl(root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{}, DigestAlgorithm::Sha256); + /*revoked_serials=*/{}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end()); @@ -193,9 +193,9 @@ policy.networking_allowed = true; policy.crl_allowed = true; - std::string crl_data_as_string_for_some_reason = BuildCrl( - root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{leaf->GetSerialNumber()}, DigestAlgorithm::Sha256); + std::string crl_data_as_string_for_some_reason = + BuildCrl(root->GetSubject(), root->GetKey(), + /*revoked_serials=*/{leaf->GetSerialNumber()}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end()); @@ -390,7 +390,7 @@ std::string crl_data_as_string_for_some_reason = BuildCrl(root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{}, DigestAlgorithm::Sha256); + /*revoked_serials=*/{}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end()); @@ -476,7 +476,7 @@ std::string crl_data_as_string_for_some_reason = BuildCrl(root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{}, DigestAlgorithm::Sha256); + /*revoked_serials=*/{}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end()); @@ -546,7 +546,7 @@ std::string crl_data_as_string_for_some_reason = BuildCrl(root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{}, DigestAlgorithm::Sha256); + /*revoked_serials=*/{}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end()); @@ -648,7 +648,7 @@ std::string crl_data_as_string_for_some_reason = BuildCrl(root->GetSubject(), root->GetKey(), - /*revoked_serials=*/{}, DigestAlgorithm::Sha256); + /*revoked_serials=*/{}); std::vector<uint8_t> crl_data(crl_data_as_string_for_some_reason.begin(), crl_data_as_string_for_some_reason.end());
diff --git a/net/dns/dns_config_service_linux.cc b/net/dns/dns_config_service_linux.cc index df795af..b9d65f6a 100644 --- a/net/dns/dns_config_service_linux.cc +++ b/net/dns/dns_config_service_linux.cc
@@ -20,11 +20,13 @@ #include "base/bind.h" #include "base/callback.h" #include "base/check.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" #include "base/files/file_path_watcher.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/sequence_checker.h" #include "base/threading/scoped_blocking_call.h" @@ -204,11 +206,12 @@ void RecordIncompatibleNsswitchReason( IncompatibleNsswitchReason reason, absl::optional<NsswitchReader::Service> service_token) { - UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsConfig.Nsswitch.IncompatibleReason", - reason); + base::UmaHistogramEnumeration("Net.DNS.DnsConfig.Nsswitch.IncompatibleReason", + reason); if (service_token) { - UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsConfig.Nsswitch.IncompatibleService", - service_token.value()); + base::UmaHistogramEnumeration( + "Net.DNS.DnsConfig.Nsswitch.IncompatibleService", + service_token.value()); } } @@ -371,12 +374,12 @@ private: void OnResolvFilePathWatcherChange(const base::FilePath& path, bool error) { - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Resolv.FileChange", true); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.FileChange", true); OnConfigChanged(!error); } void OnNsswitchFilePathWatcherChange(const base::FilePath& path, bool error) { - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Nsswitch.FileChange", true); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.FileChange", true); OnConfigChanged(!error); } @@ -453,14 +456,14 @@ } } - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Resolv.Read", - dns_config_.has_value()); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Read", + dns_config_.has_value()); if (!dns_config_.has_value()) return; - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Resolv.Valid", - dns_config_->IsValid()); - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Resolv.Compatible", - !dns_config_->unhandled_options); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Valid", + dns_config_->IsValid()); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Resolv.Compatible", + !dns_config_->unhandled_options); // Override `fallback_period` value to match default setting on // Windows. @@ -469,12 +472,16 @@ if (dns_config_ && !dns_config_->unhandled_options) { std::vector<NsswitchReader::ServiceSpecification> nsswitch_hosts = nsswitch_reader_->ReadAndParseHosts(); - UMA_HISTOGRAM_COUNTS_100("Net.DNS.DnsConfig.Nsswitch.NumServices", - nsswitch_hosts.size()); + base::UmaHistogramCounts100("Net.DNS.DnsConfig.Nsswitch.NumServices", + nsswitch_hosts.size()); dns_config_->unhandled_options = !IsNsswitchConfigCompatible(nsswitch_hosts); - UMA_HISTOGRAM_BOOLEAN("Net.DNS.DnsConfig.Nsswitch.Compatible", - !dns_config_->unhandled_options); + base::UmaHistogramBoolean("Net.DNS.DnsConfig.Nsswitch.Compatible", + !dns_config_->unhandled_options); + base::UmaHistogramBoolean( + "Net.DNS.DnsConfig.Nsswitch.NisServiceInHosts", + base::Contains(nsswitch_hosts, NsswitchReader::Service::kNis, + &NsswitchReader::ServiceSpecification::service)); } }
diff --git a/net/dns/dns_config_service_linux_unittest.cc b/net/dns/dns_config_service_linux_unittest.cc index 4324085..cd92a04 100644 --- a/net/dns/dns_config_service_linux_unittest.cc +++ b/net/dns/dns_config_service_linux_unittest.cc
@@ -23,6 +23,7 @@ #include "base/task/single_thread_task_runner.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "base/test/test_waitable_event.h" #include "base/threading/thread_task_runner_handle.h" @@ -41,6 +42,9 @@ namespace { +const char kNsswitchNisServiceInHostsHistogramName[] = + "Net.DNS.DnsConfig.Nsswitch.NisServiceInHosts"; + // MAXNS is normally 3, but let's test 4 if possible. const char* const kNameserversIPv4[] = { "8.8.8.8", @@ -879,6 +883,28 @@ EXPECT_TRUE(config->unhandled_options); } +TEST_F(DnsConfigServiceLinuxTest, HistogramNoNisServiceInHosts) { + auto res = std::make_unique<struct __res_state>(); + InitializeResState(res.get()); + resolv_reader_->set_value(std::move(res)); + + nsswitch_reader_->set_value( + {NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles), + NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)}); + + base::HistogramTester histogram_tester; + CallbackHelper callback_helper; + service_.ReadConfig(callback_helper.GetCallback()); + absl::optional<DnsConfig> config = callback_helper.WaitForResult(); + EXPECT_TRUE(resolv_reader_->closed()); + histogram_tester.ExpectBucketCount(kNsswitchNisServiceInHostsHistogramName, + false, 1); + + ASSERT_TRUE(config.has_value()); + EXPECT_TRUE(config->IsValid()); + EXPECT_FALSE(config->unhandled_options); +} + TEST_F(DnsConfigServiceLinuxTest, AcceptsNsswitchNis) { auto res = std::make_unique<struct __res_state>(); InitializeResState(res.get()); @@ -889,10 +915,13 @@ NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis), NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)}); + base::HistogramTester histogram_tester; CallbackHelper callback_helper; service_.ReadConfig(callback_helper.GetCallback()); absl::optional<DnsConfig> config = callback_helper.WaitForResult(); EXPECT_TRUE(resolv_reader_->closed()); + histogram_tester.ExpectBucketCount(kNsswitchNisServiceInHostsHistogramName, + true, 1); ASSERT_TRUE(config.has_value()); EXPECT_TRUE(config->IsValid());
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index c337be7..1ee1910 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc
@@ -147,11 +147,6 @@ return readers.count(transaction) > 0; } -base::SafeRef<HttpCache::ActiveEntry> HttpCache::ActiveEntry::GetSafeRef() - const { - return weak_factory_.GetSafeRef(); -} - //----------------------------------------------------------------------------- // This structure keeps track of work items that are attempting to create or
diff --git a/net/http/http_cache.h b/net/http/http_cache.h index fc358a2c..987e073e 100644 --- a/net/http/http_cache.h +++ b/net/http/http_cache.h
@@ -25,7 +25,6 @@ #include "base/files/file_path.h" #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" -#include "base/memory/safe_ref.h" #include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" #include "base/time/clock.h" @@ -363,8 +362,6 @@ disk_cache::Entry* GetEntry() { return disk_entry.get(); } - base::SafeRef<ActiveEntry> GetSafeRef() const; - disk_cache::ScopedEntryPtr disk_entry; // Indicates if the disk_entry was opened or not (i.e.: created). @@ -396,10 +393,6 @@ // True if entry is doomed. bool doomed = false; - - // TODO(ricea): Delete this when undoing the change to - // HttpCache::Transaction to use SafeRef. - base::WeakPtrFactory<ActiveEntry> weak_factory_{this}; }; using ActiveEntriesMap =
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 50c1414..405fa57 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc
@@ -198,7 +198,7 @@ callback_.Reset(); if (cache_) { - if (safe_entry_) { + if (entry_) { DoneWithEntry(false /* entry_is_complete */); } else if (cache_pending_) { cache_->RemovePendingTransaction(this); @@ -214,7 +214,7 @@ const HttpTransaction* transaction = network_transaction(); if (transaction) return transaction->GetLoadState(); - if (safe_entry_ || !request_) + if (entry_ || !request_) return LOAD_STATE_IDLE; return LOAD_STATE_WAITING_FOR_CACHE; } @@ -236,7 +236,7 @@ DCHECK(callback_.is_null()); DCHECK(!reading_); DCHECK(!network_trans_.get()); - DCHECK(!safe_entry_); + DCHECK(!entry_); DCHECK_EQ(next_state_, STATE_NONE); if (!cache_.get()) @@ -370,7 +370,7 @@ } int HttpCache::Transaction::TransitionToReadingState() { - if (!safe_entry_) { + if (!entry_) { if (network_trans_) { // This can happen when the request should be handled exclusively by // the network layer (skipping the cache entirely using @@ -399,7 +399,7 @@ if (!InWriters()) { // Since transaction is not a writer and we are in Read(), it must be a // reader. - DCHECK(safe_entry_.value()->TransactionInReaders(this)); + DCHECK(entry_->TransactionInReaders(this)); DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_)); next_state_ = STATE_CACHE_READ_DATA; return OK; @@ -410,7 +410,7 @@ // If it's a writer and it is partial then it may need to read from the cache // or from the network based on whether network transaction is present or not. if (partial_) { - if (safe_entry_.value()->writers->network_transaction()) + if (entry_->writers->network_transaction()) next_state_ = STATE_NETWORK_READ_CACHE_WRITE; else next_state_ = STATE_CACHE_READ_DATA; @@ -420,10 +420,8 @@ // Full request. // If it's a writer and a full request then it may read from the cache if its // offset is behind the current offset else from the network. - int disk_entry_size = - safe_entry_.value()->GetEntry()->GetDataSize(kResponseContentIndex); - if (read_offset_ == disk_entry_size || - safe_entry_.value()->writers->network_read_only()) { + int disk_entry_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex); + if (read_offset_ == disk_entry_size || entry_->writers->network_read_only()) { next_state_ = STATE_NETWORK_READ_CACHE_WRITE; } else { DCHECK_LT(read_offset_, disk_entry_size); @@ -464,7 +462,7 @@ } void HttpCache::Transaction::DoneReading() { - if (cache_.get() && safe_entry_) { + if (cache_.get() && entry_) { DCHECK_NE(mode_, UPDATE); DoneWithEntry(true); } @@ -554,7 +552,7 @@ if (InWriters()) { DCHECK(!network_trans_ || partial_); - safe_entry_.value()->writers->UpdatePriority(); + entry_->writers->UpdatePriority(); } } @@ -621,7 +619,7 @@ if (network_trans_) { network_trans_->CloseConnectionOnDestruction(); } else if (InWriters()) { - safe_entry_.value()->writers->CloseConnectionOnDestruction(); + entry_->writers->CloseConnectionOnDestruction(); } } @@ -631,7 +629,7 @@ DCHECK_NE(STATE_UNSET, next_state_); next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED; - safe_entry_ = absl::nullopt; + entry_ = nullptr; } void HttpCache::Transaction::WriterAboutToBeRemovedFromEntry(int result) { @@ -642,12 +640,11 @@ // Since the transaction can no longer access the network transaction, save // all network related info now. if (moved_network_transaction_to_writers_ && - safe_entry_.value()->writers->network_transaction()) { - SaveNetworkTransactionInfo( - *(safe_entry_.value()->writers->network_transaction())); + entry_->writers->network_transaction()) { + SaveNetworkTransactionInfo(*(entry_->writers->network_transaction())); } - safe_entry_ = absl::nullopt; + entry_ = nullptr; mode_ = NONE; // Transactions in the midst of a Read call through writers will get any error @@ -664,9 +661,8 @@ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); mode_ = READ; if (moved_network_transaction_to_writers_ && - safe_entry_.value()->writers->network_transaction()) { - SaveNetworkTransactionInfo( - *(safe_entry_.value()->writers->network_transaction())); + entry_->writers->network_transaction()) { + SaveNetworkTransactionInfo(*(entry_->writers->network_transaction())); } } @@ -1021,7 +1017,7 @@ // Assert Start() state machine's allowed last state in successful cases when // caching is happening. - DCHECK(reading_ || rv != OK || !safe_entry_ || + DCHECK(reading_ || rv != OK || !entry_ || state == STATE_FINISH_HEADERS_COMPLETE); if (rv != ERR_IO_PENDING && !callback_.is_null()) { @@ -1466,7 +1462,7 @@ cache_pending_ = false; if (result == OK) - safe_entry_ = new_entry_->GetSafeRef(); + entry_ = new_entry_; // If there is a failure, the cache should have taken care of new_entry_. new_entry_ = nullptr; @@ -1496,7 +1492,7 @@ // already written, to avoid data race since cache thread can also access // this. if (!cache_->IsWritingInProgress(entry())) - open_entry_last_used_ = safe_entry_.value()->GetEntry()->GetLastUsed(); + open_entry_last_used_ = entry_->GetEntry()->GetLastUsed(); // TODO(jkarlin): We should either handle the case or DCHECK. if (result != OK) { @@ -1541,11 +1537,10 @@ return OK; } - safe_entry_ = new_entry_->GetSafeRef(); - + entry_ = new_entry_; DCHECK_NE(response_.headers->response_code(), net::HTTP_NOT_MODIFIED); DCHECK(cache_->CanTransactionWriteResponseHeaders( - entry(), this, partial_ != nullptr, false)); + entry_, this, partial_ != nullptr, false)); TransitionToState(STATE_CACHE_WRITE_RESPONSE); return OK; } @@ -1554,16 +1549,15 @@ TRACE_EVENT_WITH_FLOW0("net", "HttpCacheTransaction::DoCacheReadResponse", TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); - DCHECK(safe_entry_); + DCHECK(entry_); TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE); - io_buf_len_ = - safe_entry_.value()->GetEntry()->GetDataSize(kResponseInfoIndex); + io_buf_len_ = entry_->GetEntry()->GetDataSize(kResponseInfoIndex); read_buf_ = base::MakeRefCounted<IOBuffer>(io_buf_len_); net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_INFO); - return safe_entry_.value()->GetEntry()->ReadData( - kResponseInfoIndex, 0, read_buf_.get(), io_buf_len_, io_callback_); + return entry_->GetEntry()->ReadData(kResponseInfoIndex, 0, read_buf_.get(), + io_buf_len_, io_callback_); } int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { @@ -1606,8 +1600,7 @@ // currently writing the response body due to the data race mentioned in the // associated bug. if (!cache_->IsWritingInProgress(entry())) { - int current_size = - safe_entry_.value()->GetEntry()->GetDataSize(kResponseContentIndex); + int current_size = entry_->GetEntry()->GetDataSize(kResponseContentIndex); int64_t full_response_length = response_.headers->GetContentLength(); // Some resources may have slipped in as truncated when they're not. @@ -1704,7 +1697,7 @@ "HttpCacheTransaction::DoCacheDispatchValidation", TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); - if (!safe_entry_) { + if (!entry_) { // Entry got destroyed when twiddling unused-since-prefetch bit. TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED); return OK; @@ -1744,7 +1737,7 @@ int HttpCache::Transaction::DoCacheQueryData() { TransitionToState(STATE_CACHE_QUERY_DATA_COMPLETE); - return safe_entry_.value()->GetEntry()->ReadyForSparseIO(io_callback_); + return entry_->GetEntry()->ReadyForSparseIO(io_callback_); } int HttpCache::Transaction::DoCacheQueryDataComplete(int result) { @@ -1765,8 +1758,7 @@ } TransitionToState(STATE_COMPLETE_PARTIAL_CACHE_VALIDATION); - return partial_->ShouldValidateCache(safe_entry_.value()->GetEntry(), - io_callback_); + return partial_->ShouldValidateCache(entry_->GetEntry(), io_callback_); } int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { @@ -1782,7 +1774,7 @@ return result; } - partial_->PrepareCacheValidation(safe_entry_.value()->GetEntry(), + partial_->PrepareCacheValidation(entry_->GetEntry(), &custom_request_->extra_headers); if (reading_ && partial_->IsCurrentRangeCached()) { @@ -1936,7 +1928,7 @@ // has been read and we have no way to gather credentials. We would // fail again, and potentially loop. This can happen if the credentials // expire while chrome is suspended. - if (safe_entry_) + if (entry_) DoomPartialEntry(false); mode_ = NONE; partial_.reset(); @@ -1987,7 +1979,7 @@ if (mode_ == WRITE && (method_ == "PUT" || method_ == "DELETE" || method_ == "PATCH")) { if (NonErrorResponse(new_response_->headers->response_code()) && - (safe_entry_ && !safe_entry_.value()->doomed)) { + (entry_ && !entry_->doomed)) { int ret = cache_->DoomEntry(cache_key_, nullptr); DCHECK_EQ(OK, ret); } @@ -2061,7 +2053,7 @@ response_.vary_data.Init(*request_, *response_.headers); if (ShouldDisableCaching(*response_.headers)) { - if (!safe_entry_.value()->doomed) { + if (!entry_->doomed) { int ret = cache_->DoomEntry(cache_key_, nullptr); DCHECK_EQ(OK, ret); } @@ -2117,16 +2109,16 @@ // than the cached 200 response, is what will be returned to the user. UpdateSecurityHeadersBeforeForwarding(); DoneWithEntry(true); - } else if (safe_entry_ && !handling_206_) { + } else if (entry_ && !handling_206_) { DCHECK_EQ(READ_WRITE, mode_); - if ((!partial_ && !cache_->IsWritingInProgress(entry())) || + if ((!partial_ && !cache_->IsWritingInProgress(entry_)) || (partial_ && partial_->IsLastRange())) { mode_ = READ; } // We no longer need the network transaction, so destroy it. if (network_trans_) ResetNetworkTransaction(); - } else if (safe_entry_ && handling_206_ && truncated_ && + } else if (entry_ && handling_206_ && truncated_ && partial_->initial_validation()) { // We just finished the validation of a truncated entry, and the server // is willing to resume the operation. Now we go back and start serving @@ -2194,8 +2186,8 @@ // cannot write to this entry. This transaction then continues to read from // the network without writing to the backend. bool is_match = response_.headers->response_code() == net::HTTP_NOT_MODIFIED; - if (safe_entry_ && !cache_->CanTransactionWriteResponseHeaders( - entry(), this, partial_ != nullptr, is_match)) { + if (entry_ && !cache_->CanTransactionWriteResponseHeaders( + entry_, this, partial_ != nullptr, is_match)) { done_headers_create_new_entry_ = true; // The transaction needs to overwrite this response. Doom the current entry, @@ -2205,8 +2197,8 @@ // so that this transaction can go straight to writing a response. mode_ = WRITE; TransitionToState(STATE_INIT_ENTRY); - cache_->DoomEntryValidationNoMatch(entry()); - safe_entry_ = absl::nullopt; + cache_->DoomEntryValidationNoMatch(entry_); + entry_ = nullptr; return OK; } @@ -2234,13 +2226,13 @@ TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_TRUNCATE_CACHED_DATA_COMPLETE); - if (!safe_entry_) + if (!entry_) return OK; net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_DATA); // Truncate the stream. - return safe_entry_.value()->GetEntry()->WriteData( - kResponseContentIndex, /*offset=*/0, - /*buf=*/nullptr, /*buf_len=*/0, io_callback_, /*truncate=*/true); + return entry_->GetEntry()->WriteData(kResponseContentIndex, /*offset=*/0, + /*buf=*/nullptr, /*buf_len=*/0, + io_callback_, /*truncate=*/true); } int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { @@ -2248,7 +2240,7 @@ "net", "HttpCacheTransaction::DoTruncateCachedDataComplete", TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result); - if (safe_entry_) { + if (entry_) { net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_DATA, result); } @@ -2283,7 +2275,7 @@ SetRequest(net_log_); - safe_entry_ = absl::nullopt; + entry_ = nullptr; new_entry_ = nullptr; // TODO(https://crbug.com/1219402): This should probably clear `response_`, @@ -2301,7 +2293,7 @@ TRACE_EVENT_WITH_FLOW1( "net", "HttpCacheTransaction::DoFinishHeaders", TRACE_ID_LOCAL(trace_id_), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "result", result); - if (!cache_.get() || !safe_entry_ || result != OK) { + if (!cache_.get() || !entry_ || result != OK) { TransitionToState(STATE_NONE); return result; } @@ -2319,13 +2311,13 @@ // If the transaction needs to wait because another transaction is still // writing the response body, it will return ERR_IO_PENDING now and the // io_callback_ will be invoked when the wait is done. - int rv = cache_->DoneWithResponseHeaders(entry(), this, partial_ != nullptr); + int rv = cache_->DoneWithResponseHeaders(entry_, this, partial_ != nullptr); DCHECK(!reading_ || rv == OK) << "Expected OK, but got " << rv; if (rv == ERR_IO_PENDING) { DCHECK(entry_lock_waiting_since_.is_null()); entry_lock_waiting_since_ = TimeTicks::Now(); - AddCacheLockTimeoutHandler(entry()); + AddCacheLockTimeoutHandler(entry_); } return rv; } @@ -2342,8 +2334,8 @@ } if (network_trans_ && InWriters()) { - safe_entry_.value()->writers->SetNetworkTransaction( - this, std::move(network_trans_), std::move(checksum_)); + entry_->writers->SetNetworkTransaction(this, std::move(network_trans_), + std::move(checksum_)); moved_network_transaction_to_writers_ = true; } @@ -2367,8 +2359,7 @@ read_buf_len_); DCHECK(InWriters()); TransitionToState(STATE_NETWORK_READ_CACHE_WRITE_COMPLETE); - return safe_entry_.value()->writers->Read(read_buf_, read_buf_len_, - io_callback_, this); + return entry_->writers->Read(read_buf_, read_buf_len_, io_callback_, this); } int HttpCache::Transaction::DoNetworkReadCacheWriteComplete(int result) { @@ -2388,7 +2379,7 @@ // We should have discovered this error in WriterAboutToBeRemovedFromEntry DCHECK_EQ(result, shared_writing_error_); DCHECK_EQ(NONE, mode_); - DCHECK(!safe_entry_); + DCHECK(!entry_); TransitionToState(STATE_NONE); return result; } @@ -2399,7 +2390,7 @@ if (result == 0) { DCHECK_EQ(NONE, mode_); - DCHECK(!safe_entry_); + DCHECK(!entry_); } else { read_offset_ += result; if (checksum_) @@ -2423,11 +2414,9 @@ // We need to move on to the next range. if (network_trans_) { ResetNetworkTransaction(); - } else if (InWriters() && - safe_entry_.value()->writers->network_transaction()) { - SaveNetworkTransactionInfo( - *(safe_entry_.value()->writers->network_transaction())); - safe_entry_.value()->writers->ResetNetworkTransaction(); + } else if (InWriters() && entry_->writers->network_transaction()) { + SaveNetworkTransactionInfo(*(entry_->writers->network_transaction())); + entry_->writers->ResetNetworkTransaction(); } TransitionToState(STATE_START_PARTIAL_CACHE_VALIDATION); } else { @@ -2483,18 +2472,18 @@ return 0; } - DCHECK(safe_entry_); + DCHECK(entry_); TransitionToState(STATE_CACHE_READ_DATA_COMPLETE); net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_DATA); if (partial_) { - return partial_->CacheRead(safe_entry_.value()->GetEntry(), read_buf_.get(), + return partial_->CacheRead(entry_->GetEntry(), read_buf_.get(), read_buf_len_, io_callback_); } - return safe_entry_.value()->GetEntry()->ReadData( - kResponseContentIndex, read_offset_, read_buf_.get(), read_buf_len_, - io_callback_); + return entry_->GetEntry()->ReadData(kResponseContentIndex, read_offset_, + read_buf_.get(), read_buf_len_, + io_callback_); } int HttpCache::Transaction::DoCacheReadDataComplete(int result) { @@ -2864,7 +2853,7 @@ DCHECK_EQ(mode_, READ_WRITE); if (!partial_->UpdateFromStoredHeaders( - response_.headers.get(), safe_entry_.value()->GetEntry(), truncated_, + response_.headers.get(), entry_->GetEntry(), truncated_, cache_->IsWritingInProgress(entry()))) { return DoRestartPartialRequest(); } @@ -3188,7 +3177,7 @@ bool partial_response = (response_code == net::HTTP_PARTIAL_CONTENT); handling_206_ = false; - if (!safe_entry_ || method_ != "GET") + if (!entry_ || method_ != "GET") return true; if (invalid_range_) { @@ -3367,7 +3356,7 @@ if (network_trans_) ResetNetworkTransaction(); - if (!safe_entry_) { + if (!entry_) { // Entry got destroyed when twiddling SWR bits. TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED); return OK; @@ -3389,7 +3378,7 @@ } } - if (!cache_->IsWritingInProgress(entry())) + if (!cache_->IsWritingInProgress(entry_)) mode_ = READ; if (method_ == "HEAD") @@ -3404,7 +3393,7 @@ bool truncated) { DCHECK(response.headers); - if (!safe_entry_) + if (!entry_) return OK; net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_WRITE_INFO); @@ -3442,19 +3431,19 @@ // Summarize some info on cacheability in memory. Don't do it if doomed // since then |entry_| isn't definitive for |cache_key_|. - if (!safe_entry_.value()->doomed) { + if (!entry_->doomed) { cache_->GetCurrentBackend()->SetEntryInMemoryData( cache_key_, ComputeUnusablePerCachingHeaders() ? HINT_UNUSABLE_PER_CACHING_HEADERS : 0); } - return safe_entry_.value()->GetEntry()->WriteData( - kResponseInfoIndex, 0, data.get(), io_buf_len_, io_callback_, true); + return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), + io_buf_len_, io_callback_, true); } int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { - if (!safe_entry_) + if (!entry_) return OK; net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_INFO, result); @@ -3470,11 +3459,10 @@ bool stopped = false; // Let writers know so that it doesn't attempt to write to the cache. if (InWriters()) { - stopped = - safe_entry_.value()->writers->StopCaching(success /* keep_entry */); + stopped = entry_->writers->StopCaching(success /* keep_entry */); if (stopped) mode_ = NONE; - } else if (safe_entry_) { + } else if (entry_) { stopped = true; DoneWithEntry(success /* entry_is_complete */); } @@ -3482,21 +3470,21 @@ } void HttpCache::Transaction::DoneWithEntry(bool entry_is_complete) { - if (!safe_entry_) + if (!entry_) return; - cache_->DoneWithEntry(entry(), this, entry_is_complete, partial_ != nullptr); - safe_entry_ = absl::nullopt; + cache_->DoneWithEntry(entry_, this, entry_is_complete, partial_ != nullptr); + entry_ = nullptr; mode_ = NONE; // switch to 'pass through' mode } void HttpCache::Transaction::DoneWithEntryForRestartWithCache() { - if (!safe_entry_) + if (!entry_) return; - cache_->DoneWithEntry(entry(), this, /*entry_is_complete=*/true, + cache_->DoneWithEntry(entry_, this, /*entry_is_complete=*/true, partial_ != nullptr); - safe_entry_ = absl::nullopt; + entry_ = nullptr; new_entry_ = nullptr; } @@ -3522,9 +3510,9 @@ // Since we are going to add this to a new entry, not recording histograms // or setting mode to NONE at this point by invoking the wrapper // DoneWithEntry. - cache_->DoneWithEntry(entry(), this, true /* entry_is_complete */, + cache_->DoneWithEntry(entry_, this, true /* entry_is_complete */, partial_ != nullptr); - safe_entry_ = absl::nullopt; + entry_ = nullptr; is_sparse_ = false; // It's OK to use PartialData::RestoreHeaders here as |restart| is only set // when the HttpResponseInfo couldn't even be read, at which point it's @@ -3559,14 +3547,14 @@ void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { DVLOG(2) << "DoomPartialEntry"; - if (entry() && !safe_entry_.value()->doomed) { + if (entry_ && !entry_->doomed) { int rv = cache_->DoomEntry(cache_key_, nullptr); DCHECK_EQ(OK, rv); } - cache_->DoneWithEntry(entry(), this, false /* entry_is_complete */, + cache_->DoneWithEntry(entry_, this, false /* entry_is_complete */, partial_ != nullptr); - safe_entry_ = absl::nullopt; + entry_ = nullptr; is_sparse_ = false; truncated_ = false; if (delete_object) @@ -3628,7 +3616,7 @@ if (network_trans_) return network_trans_.get(); if (InWriters()) - return safe_entry_.value()->writers->network_transaction(); + return entry_->writers->network_transaction(); return nullptr; } @@ -3637,7 +3625,7 @@ if (network_trans_) return network_trans_.get(); if (InWriters() && moved_network_transaction_to_writers_) - return safe_entry_.value()->writers->network_transaction(); + return entry_->writers->network_transaction(); return nullptr; } @@ -3661,8 +3649,7 @@ // bool HttpCache::Transaction::CanResume(bool has_data) { // Double check that there is something worth keeping. - if (has_data && - !safe_entry_.value()->GetEntry()->GetDataSize(kResponseContentIndex)) + if (has_data && !entry_->GetEntry()->GetDataSize(kResponseContentIndex)) return false; if (method_ != "GET") @@ -3871,8 +3858,7 @@ } bool HttpCache::Transaction::InWriters() const { - return safe_entry_ && safe_entry_.value()->writers && - safe_entry_.value()->writers->HasTransaction(this); + return entry_ && entry_->writers && entry_->writers->HasTransaction(this); } HttpCache::Transaction::NetworkTransactionInfo::NetworkTransactionInfo() =
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h index d17f282..0a937a3 100644 --- a/net/http/http_cache_transaction.h +++ b/net/http/http_cache_transaction.h
@@ -16,7 +16,6 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" -#include "base/memory/safe_ref.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "net/base/completion_once_callback.h" @@ -36,7 +35,6 @@ #include "net/log/net_log_with_source.h" #include "net/socket/connection_attempts.h" #include "net/websockets/websocket_handshake_stream_base.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace crypto { class SecureHash; @@ -107,6 +105,8 @@ const std::string& key() const { return cache_key_; } + HttpCache::ActiveEntry* entry() { return entry_; } + // Returns the LoadState of the writer transaction of a given ActiveEntry. In // other words, returns the LoadState of this transaction without asking the // http cache, because this transaction should be the one currently writing @@ -608,13 +608,6 @@ // forwarded might need to set these headers again to avoid being blocked. void UpdateSecurityHeadersBeforeForwarding(); - HttpCache::ActiveEntry* entry() { - // SafeRef lacks a method to extract the pointer so we have to convert it to - // a reference then back to a pointer. This will CHECK() if `safe_entry_` is - // not set. - return &*safe_entry_.value(); - } - State next_state_{STATE_NONE}; // Used for tracing. @@ -638,16 +631,7 @@ // |external_validation_| contains the value of those headers. ValidationHeaders external_validation_; base::WeakPtr<HttpCache> cache_; - // This member is temporarily an optional SafeRef. The name has been - // temporarily changed from `entry_` to `safe_entry_` to ensure that no - // references are missed. It is wrapped in an optional because SafeRef cannot - // be null. There are many unchecked calls to safe_entry_.value(). The safety - // of these rely on the fact that absl::optional's implementation of value() - // aborts when not set. - // TODO(ricea): Change this back to a raw_ptr<ActiveEntry, DangingUntriaged> - // or possibly a WeakPtr. - absl::optional<base::SafeRef<HttpCache::ActiveEntry>> safe_entry_; - + raw_ptr<HttpCache::ActiveEntry, DanglingUntriaged> entry_ = nullptr; HttpCache::ActiveEntry* new_entry_ = nullptr; std::unique_ptr<HttpTransaction> network_trans_; CompletionOnceCallback callback_; // Consumer's callback.
diff --git a/net/http/http_cache_writers.cc b/net/http/http_cache_writers.cc index b70dbec..fdb6fd4 100644 --- a/net/http/http_cache_writers.cc +++ b/net/http/http_cache_writers.cc
@@ -69,8 +69,9 @@ default; HttpCache::Writers::Writers(HttpCache* cache, HttpCache::ActiveEntry* entry) - : cache_(cache), entry_(entry->GetSafeRef()) { + : cache_(cache), entry_(entry) { DCHECK(cache_); + DCHECK(entry_); } HttpCache::Writers::~Writers() = default; @@ -122,7 +123,7 @@ network_read_only_ = true; if (!keep_entry) { should_keep_entry_ = false; - cache_->WritersDoomEntryRestartTransactions(entry()); + cache_->WritersDoomEntryRestartTransactions(entry_); } return true; @@ -197,7 +198,7 @@ if (!success && ShouldTruncate()) TruncateEntry(); - cache_->WritersDoneWritingToEntry(entry(), success, should_keep_entry_, + cache_->WritersDoneWritingToEntry(entry_, success, should_keep_entry_, TransactionSet()); } @@ -631,7 +632,7 @@ if (all_writers_.empty()) { SetCacheCallback(false, TransactionSet()); } else { - cache_->WritersDoomEntryRestartTransactions(entry()); + cache_->WritersDoomEntryRestartTransactions(entry_); } } @@ -680,7 +681,7 @@ const TransactionSet& make_readers) { DCHECK(!cache_callback_); cache_callback_ = base::BindOnce(&HttpCache::WritersDoneWritingToEntry, - cache_->GetWeakPtr(), entry(), success, + cache_->GetWeakPtr(), entry_, success, should_keep_entry_, make_readers); }
diff --git a/net/http/http_cache_writers.h b/net/http/http_cache_writers.h index b9374fd..7de8dde 100644 --- a/net/http/http_cache_writers.h +++ b/net/http/http_cache_writers.h
@@ -9,7 +9,6 @@ #include <memory> #include "base/memory/raw_ptr.h" -#include "base/memory/safe_ref.h" #include "base/memory/weak_ptr.h" #include "net/base/completion_once_callback.h" #include "net/http/http_cache.h" @@ -242,8 +241,6 @@ // IO Completion callback function. void OnIOComplete(int result); - ActiveEntry* entry() const { return &*entry_; } - State next_state_ = State::NONE; // True if only reading from network and not writing to cache. @@ -252,7 +249,7 @@ raw_ptr<HttpCache> const cache_ = nullptr; // Owner of |this|. - base::SafeRef<ActiveEntry> const entry_; + raw_ptr<ActiveEntry> const entry_ = nullptr; std::unique_ptr<HttpTransaction> network_transaction_;
diff --git a/net/test/cert_builder.cc b/net/test/cert_builder.cc index f564d81..60755e4a 100644 --- a/net/test/cert_builder.cc +++ b/net/test/cert_builder.cc
@@ -7,9 +7,11 @@ #include "base/files/file_path.h" #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" +#include "crypto/ec_private_key.h" #include "crypto/openssl_util.h" #include "crypto/rsa_private_key.h" #include "net/cert/asn1_util.h" +#include "net/cert/pki/verify_signed_data.h" #include "net/cert/x509_util.h" #include "net/der/encode_values.h" #include "net/der/input.h" @@ -51,6 +53,26 @@ std::end(kSha1WithRSAEncryption)); } +std::string Md5WithRSAEncryption() { + const uint8_t kMd5WithRSAEncryption[] = {0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x04, 0x05, 0x00}; + return std::string(std::begin(kMd5WithRSAEncryption), + std::end(kMd5WithRSAEncryption)); +} + +std::string EcdsaWithSha256() { + const uint8_t kDer[] = {0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02}; + return std::string(std::begin(kDer), std::end(kDer)); +} + +std::string EcdsaWithSha1() { + const uint8_t kDer[] = {0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x01}; + return std::string(std::begin(kDer), std::end(kDer)); +} + // Adds bytes (specified as a StringPiece) to the given CBB. // The argument ordering follows the boringssl CBB_* api style. bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) { @@ -171,17 +193,26 @@ // Build slightly modified variants of |orig_certs|. *out_root = std::make_unique<CertBuilder>(orig_certs[2]->cert_buffer(), nullptr); + (*out_root)->SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + (*out_root)->GenerateECKey(); + *out_intermediate = std::make_unique<CertBuilder>( orig_certs[1]->cert_buffer(), out_root->get()); (*out_intermediate)->EraseExtension(der::Input(kCrlDistributionPointsOid)); (*out_intermediate)->EraseExtension(der::Input(kAuthorityInfoAccessOid)); + (*out_intermediate)->SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + (*out_intermediate)->GenerateECKey(); + *out_leaf = std::make_unique<CertBuilder>(orig_certs[0]->cert_buffer(), out_intermediate->get()); (*out_leaf)->SetSubjectAltName(kHostname); (*out_leaf)->EraseExtension(der::Input(kCrlDistributionPointsOid)); (*out_leaf)->EraseExtension(der::Input(kAuthorityInfoAccessOid)); + (*out_leaf)->SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + (*out_leaf)->GenerateECKey(); } +// static void CertBuilder::CreateSimpleChain(std::unique_ptr<CertBuilder>* out_leaf, std::unique_ptr<CertBuilder>* out_root) { const char kHostname[] = "www.example.com"; @@ -194,10 +225,120 @@ // Build slightly modified variants of |orig_certs|. *out_root = std::make_unique<CertBuilder>(orig_root->cert_buffer(), nullptr); + (*out_root)->SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + (*out_root)->GenerateECKey(); *out_leaf = std::make_unique<CertBuilder>(orig_leaf->cert_buffer(), out_root->get()); (*out_leaf)->SetSubjectAltName(kHostname); + (*out_leaf)->SetSignatureAlgorithm(SignatureAlgorithm::kEcdsaSha256); + (*out_leaf)->GenerateECKey(); +} + +// static +absl::optional<SignatureAlgorithm> CertBuilder::DefaultSignatureAlgorithmForKey( + EVP_PKEY* key) { + if (EVP_PKEY_id(key) == EVP_PKEY_RSA) + return SignatureAlgorithm::kRsaPkcs1Sha256; + if (EVP_PKEY_id(key) == EVP_PKEY_EC) + return SignatureAlgorithm::kEcdsaSha256; + return absl::nullopt; +} + +// static +bool CertBuilder::SignData(SignatureAlgorithm signature_algorithm, + base::StringPiece tbs_data, + EVP_PKEY* key, + CBB* out_signature) { + if (!key) + return false; + + int expected_pkey_id = 1; + const EVP_MD* digest; + + switch (signature_algorithm) { + case SignatureAlgorithm::kRsaPkcs1Md5: + expected_pkey_id = EVP_PKEY_RSA; + digest = EVP_md5(); + break; + case SignatureAlgorithm::kRsaPkcs1Sha1: + expected_pkey_id = EVP_PKEY_RSA; + digest = EVP_sha1(); + break; + case SignatureAlgorithm::kRsaPkcs1Sha256: + expected_pkey_id = EVP_PKEY_RSA; + digest = EVP_sha256(); + break; + case SignatureAlgorithm::kRsaPkcs1Sha384: + expected_pkey_id = EVP_PKEY_RSA; + digest = EVP_sha384(); + break; + case SignatureAlgorithm::kRsaPkcs1Sha512: + expected_pkey_id = EVP_PKEY_RSA; + digest = EVP_sha512(); + break; + + case SignatureAlgorithm::kEcdsaSha1: + expected_pkey_id = EVP_PKEY_EC; + digest = EVP_sha1(); + break; + case SignatureAlgorithm::kEcdsaSha256: + expected_pkey_id = EVP_PKEY_EC; + digest = EVP_sha256(); + break; + case SignatureAlgorithm::kEcdsaSha384: + expected_pkey_id = EVP_PKEY_EC; + digest = EVP_sha384(); + break; + case SignatureAlgorithm::kEcdsaSha512: + expected_pkey_id = EVP_PKEY_EC; + digest = EVP_sha512(); + break; + + case SignatureAlgorithm::kRsaPssSha256: + case SignatureAlgorithm::kRsaPssSha384: + case SignatureAlgorithm::kRsaPssSha512: + case SignatureAlgorithm::kRsaPkcs1Md2: + case SignatureAlgorithm::kRsaPkcs1Md4: + case SignatureAlgorithm::kDsaSha1: + case SignatureAlgorithm::kDsaSha256: + // Unsupported algorithms. + return false; + } + + const uint8_t* tbs_bytes = reinterpret_cast<const uint8_t*>(tbs_data.data()); + bssl::ScopedEVP_MD_CTX ctx; + uint8_t* sig_out; + size_t sig_len; + + return expected_pkey_id == EVP_PKEY_id(key) && + EVP_DigestSignInit(ctx.get(), nullptr, digest, nullptr, key) && + EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_bytes, + tbs_data.size()) && + CBB_reserve(out_signature, &sig_out, sig_len) && + EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_bytes, + tbs_data.size()) && + CBB_did_write(out_signature, sig_len); +} + +// static +std::string CertBuilder::SignatureAlgorithmToDer( + SignatureAlgorithm signature_algorithm) { + switch (signature_algorithm) { + case SignatureAlgorithm::kRsaPkcs1Md5: + return Md5WithRSAEncryption(); + case SignatureAlgorithm::kRsaPkcs1Sha1: + return Sha1WithRSAEncryption(); + case SignatureAlgorithm::kRsaPkcs1Sha256: + return Sha256WithRSAEncryption(); + case SignatureAlgorithm::kEcdsaSha1: + return EcdsaWithSha1(); + case SignatureAlgorithm::kEcdsaSha256: + return EcdsaWithSha256(); + default: + ADD_FAILURE(); + return std::string(); + } } void CertBuilder::SetExtension(const der::Input& oid, @@ -544,25 +685,9 @@ SetExtension(der::Input(kAuthorityKeyIdentifierOid), FinishCBB(cbb.get())); } -void CertBuilder::SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) { - switch (digest) { - case DigestAlgorithm::Sha256: { - SetSignatureAlgorithm(Sha256WithRSAEncryption()); - break; - } - - case DigestAlgorithm::Sha1: { - SetSignatureAlgorithm(Sha1WithRSAEncryption()); - break; - } - - default: - ASSERT_TRUE(false); - } -} - -void CertBuilder::SetSignatureAlgorithm(std::string algorithm_tlv) { - signature_algorithm_tlv_ = std::move(algorithm_tlv); +void CertBuilder::SetSignatureAlgorithm( + SignatureAlgorithm signature_algorithm) { + signature_algorithm_ = signature_algorithm; Invalidate(); } @@ -625,8 +750,16 @@ } EVP_PKEY* CertBuilder::GetKey() { - if (!key_) - GenerateKey(); + if (!key_) { + switch (default_pkey_id_) { + case EVP_PKEY_RSA: + GenerateRSAKey(); + break; + case EVP_PKEY_EC: + GenerateECKey(); + break; + } + } return key_.get(); } @@ -670,11 +803,16 @@ cert_.reset(); } -void CertBuilder::GenerateKey() { - ASSERT_FALSE(key_); +void CertBuilder::GenerateECKey() { + auto private_key = crypto::ECPrivateKey::Create(); + key_ = bssl::UpRef(private_key->key()); + Invalidate(); +} +void CertBuilder::GenerateRSAKey() { auto private_key = crypto::RSAPrivateKey::Create(2048); key_ = bssl::UpRef(private_key->key()); + Invalidate(); } void CertBuilder::GenerateSubjectKeyIdentifier() { @@ -736,7 +874,10 @@ // signature der::Input signature_algorithm_tlv; ASSERT_TRUE(tbs_certificate.ReadRawTLV(&signature_algorithm_tlv)); - signature_algorithm_tlv_ = signature_algorithm_tlv.AsString(); + auto signature_algorithm = + ParseSignatureAlgorithm(signature_algorithm_tlv, nullptr); + ASSERT_TRUE(signature_algorithm); + signature_algorithm_ = *signature_algorithm; // issuer ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence)); @@ -748,8 +889,14 @@ // subject ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence)); + // subjectPublicKeyInfo - ASSERT_TRUE(tbs_certificate.SkipTag(der::kSequence)); + der::Input spki_tlv; + ASSERT_TRUE(tbs_certificate.ReadRawTLV(&spki_tlv)); + bssl::UniquePtr<EVP_PKEY> public_key; + ASSERT_TRUE(ParsePublicKey(spki_tlv, &public_key)); + default_pkey_id_ = EVP_PKEY_id(public_key.get()); + // issuerUniqueID ASSERT_TRUE(tbs_certificate.SkipOptionalTag(der::ContextSpecificPrimitive(1), &unused)); @@ -773,7 +920,8 @@ } } -void CertBuilder::BuildTBSCertificate(std::string* out) { +void CertBuilder::BuildTBSCertificate(base::StringPiece signature_algorithm_tlv, + std::string* out) { bssl::ScopedCBB cbb; CBB tbs_cert, version, extensions_context, extensions; @@ -785,10 +933,11 @@ // Always use v3 certificates. ASSERT_TRUE(CBB_add_asn1_uint64(&version, 2)); ASSERT_TRUE(CBB_add_asn1_uint64(&tbs_cert, GetSerialNumber())); - ASSERT_TRUE(AddSignatureAlgorithm(&tbs_cert)); + ASSERT_TRUE(CBBAddBytes(&tbs_cert, signature_algorithm_tlv)); ASSERT_TRUE(CBBAddBytes(&tbs_cert, issuer_->GetSubject())); ASSERT_TRUE(CBBAddBytes(&tbs_cert, validity_tlv_)); ASSERT_TRUE(CBBAddBytes(&tbs_cert, GetSubject())); + ASSERT_TRUE(GetKey()); ASSERT_TRUE(EVP_marshal_public_key(&tbs_cert, GetKey())); // Serialize all the extensions. @@ -826,60 +975,33 @@ *out = FinishCBB(cbb.get()); } -bool CertBuilder::AddSignatureAlgorithm(CBB* cbb) { - return CBBAddBytes(cbb, signature_algorithm_tlv_); -} - void CertBuilder::GenerateCertificate() { ASSERT_FALSE(cert_); + absl::optional<SignatureAlgorithm> signature_algorithm = signature_algorithm_; + if (!signature_algorithm) + signature_algorithm = DefaultSignatureAlgorithmForKey(issuer_->GetKey()); + ASSERT_TRUE(signature_algorithm.has_value()); + + std::string signature_algorithm_tlv = + SignatureAlgorithmToDer(*signature_algorithm); + ASSERT_FALSE(signature_algorithm_tlv.empty()); + std::string tbs_cert; - BuildTBSCertificate(&tbs_cert); - const uint8_t* tbs_cert_bytes = - reinterpret_cast<const uint8_t*>(tbs_cert.data()); - - // Determine the correct digest algorithm to use (assumes RSA PKCS#1 - // signatures). - auto signature_algorithm = - ParseSignatureAlgorithm(der::Input(&signature_algorithm_tlv_), nullptr); - ASSERT_TRUE(signature_algorithm); - const EVP_MD* md = nullptr; - switch (*signature_algorithm) { - case SignatureAlgorithm::kRsaPkcs1Sha256: - md = EVP_sha256(); - break; - - case SignatureAlgorithm::kRsaPkcs1Sha1: - md = EVP_sha1(); - break; - - default: - ASSERT_TRUE(false) << "Only rsaEncryptionWithSha256 or " - "rsaEncryptionWithSha1 are supported"; - break; - } + BuildTBSCertificate(signature_algorithm_tlv, &tbs_cert); // Sign the TBSCertificate and write the entire certificate. bssl::ScopedCBB cbb; CBB cert, signature; - bssl::ScopedEVP_MD_CTX ctx; - uint8_t* sig_out; - size_t sig_len; ASSERT_TRUE(CBB_init(cbb.get(), tbs_cert.size())); ASSERT_TRUE(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBBAddBytes(&cert, tbs_cert)); - ASSERT_TRUE(AddSignatureAlgorithm(&cert)); + ASSERT_TRUE(CBBAddBytes(&cert, signature_algorithm_tlv)); ASSERT_TRUE(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING)); ASSERT_TRUE(CBB_add_u8(&signature, 0 /* no unused bits */)); ASSERT_TRUE( - EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, issuer_->GetKey())); - ASSERT_TRUE(EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes, - tbs_cert.size())); - ASSERT_TRUE(CBB_reserve(&signature, &sig_out, sig_len)); - ASSERT_TRUE(EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes, - tbs_cert.size())); - ASSERT_TRUE(CBB_did_write(&signature, sig_len)); + SignData(*signature_algorithm, tbs_cert, issuer_->GetKey(), &signature)); auto cert_der = FinishCBB(cbb.get()); cert_ =
diff --git a/net/test/cert_builder.h b/net/test/cert_builder.h index d7cda44..d8e1bdc 100644 --- a/net/test/cert_builder.h +++ b/net/test/cert_builder.h
@@ -10,13 +10,14 @@ #include "base/memory/raw_ptr.h" #include "base/rand_util.h" -#include "base/strings/string_piece.h" +#include "base/strings/string_piece_forward.h" #include "net/base/ip_address.h" #include "net/cert/pki/parse_certificate.h" #include "net/cert/pki/signature_algorithm.h" #include "net/cert/x509_certificate.h" #include "third_party/boringssl/src/include/openssl/base.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/pool.h" class GURL; @@ -87,6 +88,22 @@ static void CreateSimpleChain(std::unique_ptr<CertBuilder>* out_leaf, std::unique_ptr<CertBuilder>* out_root); + // Returns a compatible signature algorithm for |key|. + static absl::optional<SignatureAlgorithm> DefaultSignatureAlgorithmForKey( + EVP_PKEY* key); + + // Signs |tbs_data| with |key| using |signature_algorithm| appending the + // signature onto |out_signature| and returns true if successful. + static bool SignData(SignatureAlgorithm signature_algorithm, + base::StringPiece tbs_data, + EVP_PKEY* key, + CBB* out_signature); + + // Returns a DER encoded AlgorithmIdentifier TLV for |signature_algorithm| + // empty string on error. + static std::string SignatureAlgorithmToDer( + SignatureAlgorithm signature_algorithm); + // Sets a value for the indicated X.509 (v3) extension. void SetExtension(const der::Input& oid, std::string value, @@ -163,14 +180,25 @@ // introducing AKI/SKI chain building issues. void SetAuthorityKeyIdentifier(const std::string& authority_key_identifier); - // Sets the signature algorithm for the certificate to either - // sha256WithRSAEncryption or sha1WithRSAEncryption. - void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest); - - void SetSignatureAlgorithm(std::string algorithm_tlv); + // Sets the signature algorithm to use in generating the certificate's + // signature. The signature algorithm should be compatible with + // the type of |issuer_->GetKey()|. If this method is not called, and the + // CertBuilder was initialized from a template cert, the signature algorithm + // of that cert will be used, or if there was no template cert, a default + // algorithm will be used base on the signing key type. + void SetSignatureAlgorithm(SignatureAlgorithm signature_algorithm); void SetRandomSerialNumber(); + // Sets the private key for the generated certificate to an EC key. If a key + // was already set, it will be replaced. + void GenerateECKey(); + + // Sets the private key for the generated certificate to a 2048-bit RSA key. + // RSA key generation is expensive, so this should not be used unless an RSA + // key is specifically needed. If a key was already set, it will be replaced. + void GenerateRSAKey(); + // Returns the CertBuilder that issues this certificate. (Will be |this| if // certificate is self-signed.) CertBuilder* issuer() { return issuer_; } @@ -197,7 +225,7 @@ // |not_before| and |not_after|, returning true on success. bool GetValidity(base::Time* not_before, base::Time* not_after) const; - // Returns the (RSA) key for the generated certificate. + // Returns the key for the generated certificate. EVP_PKEY* GetKey(); // Returns an X509Certificate for the generated certificate. @@ -230,9 +258,6 @@ // be re-generated next time the DER is accessed. void Invalidate(); - // Sets the |key_| to a 2048-bit RSA key. - void GenerateKey(); - // Generates a random Subject Key Identifier for the certificate. This is // necessary for Windows, which otherwises uses SKI/AKI matching for lookups // with greater precedence than subject/issuer name matching, and on newer @@ -252,9 +277,8 @@ void InitFromCert(const der::Input& cert); // Assembles the CertBuilder into a TBSCertificate. - void BuildTBSCertificate(std::string* out); - - bool AddSignatureAlgorithm(CBB* cbb); + void BuildTBSCertificate(base::StringPiece signature_algorithm_tlv, + std::string* out); void GenerateCertificate(); @@ -265,8 +289,9 @@ std::string validity_tlv_; std::string subject_tlv_; - std::string signature_algorithm_tlv_; + absl::optional<SignatureAlgorithm> signature_algorithm_; uint64_t serial_number_ = 0; + int default_pkey_id_ = EVP_PKEY_EC; std::map<std::string, ExtensionValue> extensions_;
diff --git a/net/test/revocation_builder.cc b/net/test/revocation_builder.cc index 090ea56..01f9d3ad 100644 --- a/net/test/revocation_builder.cc +++ b/net/test/revocation_builder.cc
@@ -11,6 +11,7 @@ #include "net/cert/x509_util.h" #include "net/der/encode_values.h" #include "net/der/input.h" +#include "net/test/cert_builder.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/mem.h" @@ -19,30 +20,6 @@ namespace { -std::string Sha256WithRSAEncryption() { - const uint8_t kSha256WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x0b, 0x05, 0x00}; - return std::string(std::begin(kSha256WithRSAEncryption), - std::end(kSha256WithRSAEncryption)); -} - -std::string Sha1WithRSAEncryption() { - const uint8_t kSha1WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00}; - return std::string(std::begin(kSha1WithRSAEncryption), - std::end(kSha1WithRSAEncryption)); -} - -std::string Md5WithRSAEncryption() { - const uint8_t kMd5WithRSAEncryption[] = {0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x04, 0x05, 0x00}; - return std::string(std::begin(kMd5WithRSAEncryption), - std::end(kMd5WithRSAEncryption)); -} - std::string Sha1() { // SEQUENCE { OBJECT_IDENTIFIER { 1.3.14.3.2.26 } } const uint8_t kSHA1[] = {0x30, 0x07, 0x06, 0x05, 0x2b, @@ -343,7 +320,8 @@ std::string BuildOCSPResponseWithResponseData( EVP_PKEY* responder_key, - const std::string& tbs_response_data) { + const std::string& tbs_response_data, + absl::optional<SignatureAlgorithm> signature_algorithm) { // For a basic OCSP responder, responseType will be id-pkix-ocsp-basic. // // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp } @@ -366,28 +344,29 @@ // bssl::ScopedCBB basic_ocsp_response_cbb; CBB basic_ocsp_response, signature; - bssl::ScopedEVP_MD_CTX ctx; - uint8_t* sig_out; - size_t sig_len; - if (!CBB_init(basic_ocsp_response_cbb.get(), 64 + tbs_response_data.size()) || + if (!responder_key) { + ADD_FAILURE(); + return std::string(); + } + if (!signature_algorithm) + signature_algorithm = + CertBuilder::DefaultSignatureAlgorithmForKey(responder_key); + if (!signature_algorithm) { + ADD_FAILURE(); + return std::string(); + } + std::string signature_algorithm_tlv = + CertBuilder::SignatureAlgorithmToDer(*signature_algorithm); + if (signature_algorithm_tlv.empty() || + !CBB_init(basic_ocsp_response_cbb.get(), 64 + tbs_response_data.size()) || !CBB_add_asn1(basic_ocsp_response_cbb.get(), &basic_ocsp_response, CBS_ASN1_SEQUENCE) || !CBBAddBytes(&basic_ocsp_response, tbs_response_data) || - !CBBAddBytes(&basic_ocsp_response, Sha256WithRSAEncryption()) || + !CBBAddBytes(&basic_ocsp_response, signature_algorithm_tlv) || !CBB_add_asn1(&basic_ocsp_response, &signature, CBS_ASN1_BITSTRING) || !CBB_add_u8(&signature, 0 /* no unused bits */) || - !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, - responder_key) || - !EVP_DigestSign( - ctx.get(), nullptr, &sig_len, - reinterpret_cast<const uint8_t*>(tbs_response_data.data()), - tbs_response_data.size()) || - !CBB_reserve(&signature, &sig_out, sig_len) || - !EVP_DigestSign( - ctx.get(), sig_out, &sig_len, - reinterpret_cast<const uint8_t*>(tbs_response_data.data()), - tbs_response_data.size()) || - !CBB_did_write(&signature, sig_len)) { + !CertBuilder::SignData(*signature_algorithm, tbs_response_data, + responder_key, &signature)) { ADD_FAILURE(); return std::string(); } @@ -402,31 +381,23 @@ std::string BuildCrl(const std::string& crl_issuer_subject, EVP_PKEY* crl_issuer_key, const std::vector<uint64_t>& revoked_serials, - DigestAlgorithm digest) { - std::string signature_algorithm; - const EVP_MD* md = nullptr; - switch (digest) { - case DigestAlgorithm::Sha256: { - signature_algorithm = Sha256WithRSAEncryption(); - md = EVP_sha256(); - break; - } - - case DigestAlgorithm::Sha1: { - signature_algorithm = Sha1WithRSAEncryption(); - md = EVP_sha1(); - break; - } - - case DigestAlgorithm::Md5: { - signature_algorithm = Md5WithRSAEncryption(); - md = EVP_md5(); - break; - } - - default: - ADD_FAILURE(); - return std::string(); + absl::optional<SignatureAlgorithm> signature_algorithm) { + if (!crl_issuer_key) { + ADD_FAILURE(); + return std::string(); + } + if (!signature_algorithm) + signature_algorithm = + CertBuilder::DefaultSignatureAlgorithmForKey(crl_issuer_key); + if (!signature_algorithm) { + ADD_FAILURE(); + return std::string(); + } + std::string signature_algorithm_tlv = + CertBuilder::SignatureAlgorithmToDer(*signature_algorithm); + if (signature_algorithm_tlv.empty()) { + ADD_FAILURE(); + return std::string(); } // TBSCertList ::= SEQUENCE { // version Version OPTIONAL, @@ -449,7 +420,7 @@ if (!CBB_init(tbs_cbb.get(), 10) || !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) || - !CBBAddBytes(&tbs_cert_list, signature_algorithm) || + !CBBAddBytes(&tbs_cert_list, signature_algorithm_tlv) || !CBBAddBytes(&tbs_cert_list, crl_issuer_subject) || !x509_util::CBBAddTime(&tbs_cert_list, base::Time::Now() - base::Days(1)) || @@ -486,24 +457,14 @@ // signatureValue BIT STRING } bssl::ScopedCBB crl_cbb; CBB cert_list, signature; - bssl::ScopedEVP_MD_CTX ctx; - uint8_t* sig_out; - size_t sig_len; if (!CBB_init(crl_cbb.get(), 10) || !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) || !CBBAddBytes(&cert_list, tbs_tlv) || - !CBBAddBytes(&cert_list, signature_algorithm) || + !CBBAddBytes(&cert_list, signature_algorithm_tlv) || !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) || !CBB_add_u8(&signature, 0 /* no unused bits */) || - !EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, crl_issuer_key) || - !EVP_DigestSign(ctx.get(), nullptr, &sig_len, - reinterpret_cast<const uint8_t*>(tbs_tlv.data()), - tbs_tlv.size()) || - !CBB_reserve(&signature, &sig_out, sig_len) || - !EVP_DigestSign(ctx.get(), sig_out, &sig_len, - reinterpret_cast<const uint8_t*>(tbs_tlv.data()), - tbs_tlv.size()) || - !CBB_did_write(&signature, sig_len)) { + !CertBuilder::SignData(*signature_algorithm, tbs_tlv, crl_issuer_key, + &signature)) { ADD_FAILURE(); return std::string(); }
diff --git a/net/test/revocation_builder.h b/net/test/revocation_builder.h index b011a35..e98d475 100644 --- a/net/test/revocation_builder.h +++ b/net/test/revocation_builder.h
@@ -11,6 +11,8 @@ #include "base/time/time.h" #include "net/cert/ocsp_revocation_status.h" #include "net/cert/pki/ocsp.h" +#include "net/cert/pki/signature_algorithm.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/boringssl/src/include/openssl/evp.h" namespace net { @@ -45,18 +47,23 @@ base::Time produced_at, const std::vector<OCSPBuilderSingleResponse>& responses); -// Creates an OCSPResponse signed by |responder_key| with |tbs_response_data| as -// the to-be-signed ResponseData. -std::string BuildOCSPResponseWithResponseData(EVP_PKEY* responder_key, - const std::string& response_data); +// Creates an OCSPResponse signed by |responder_key| with |tbs_response_data| +// as the to-be-signed ResponseData. If |signature_algorithm| is nullopt, a +// default algorithm will be chosen based on the key type. +std::string BuildOCSPResponseWithResponseData( + EVP_PKEY* responder_key, + const std::string& response_data, + absl::optional<SignatureAlgorithm> signature_algorithm = absl::nullopt); // Creates a CRL issued by |crl_issuer_subject| and signed by |crl_issuer_key|, -// marking |revoked_serials| as revoked. +// marking |revoked_serials| as revoked. If |signature_algorithm| is nullopt, a +// default algorithm will be chosen based on the key type. // Returns the DER-encoded CRL. -std::string BuildCrl(const std::string& crl_issuer_subject, - EVP_PKEY* crl_issuer_key, - const std::vector<uint64_t>& revoked_serials, - DigestAlgorithm digest); +std::string BuildCrl( + const std::string& crl_issuer_subject, + EVP_PKEY* crl_issuer_key, + const std::vector<uint64_t>& revoked_serials, + absl::optional<SignatureAlgorithm> signature_algorithm = absl::nullopt); } // namespace net
diff --git a/sandbox/policy/mac/common.sb b/sandbox/policy/mac/common.sb index 3cf7b61..9c6638f 100644 --- a/sandbox/policy/mac/common.sb +++ b/sandbox/policy/mac/common.sb
@@ -198,6 +198,16 @@ (path "/System/Library/CoreServices/checkfixlist") ) +; From /System/Library/Sandbox/Profiles/dyld-support.sb on macOS 13. +(if (>= os-version 1300) + (allow file-read* file-map-executable + (subpath "/System/Cryptexes/App") + (subpath "/System/Cryptexes/OS") + (subpath "/System/Volumes/Preboot/Cryptexes/App/System") + (subpath "/System/Volumes/Preboot/Cryptexes/OS") + ) +) + ; Reads from /usr. (allow file-read-data (subpath "/usr/share/icu")
diff --git a/storage/browser/file_system/file_system_operation_runner.h b/storage/browser/file_system/file_system_operation_runner.h index 8e9eaa5..f07f5ea 100644 --- a/storage/browser/file_system/file_system_operation_runner.h +++ b/storage/browser/file_system/file_system_operation_runner.h
@@ -304,7 +304,7 @@ void FinishOperation(OperationID id); // Not owned; whatever owns this has to make sure context outlives this. - raw_ptr<FileSystemContext, DanglingUntriaged> file_system_context_; + raw_ptr<FileSystemContext> file_system_context_; using Operations = std::map<OperationID, std::unique_ptr<FileSystemOperation>>;
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 84c22e00..4405a71 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -76622,7 +76622,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "absl_hardening_tests", @@ -76652,7 +76652,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76674,7 +76674,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "base_unittests", @@ -76704,7 +76704,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76726,7 +76726,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "base_unittests", @@ -76756,7 +76756,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76778,7 +76778,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "base_unittests", @@ -76808,7 +76808,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76830,7 +76830,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "boringssl_crypto_tests", @@ -76860,7 +76860,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76882,7 +76882,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "boringssl_ssl_tests", @@ -76912,7 +76912,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76934,7 +76934,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "components_unittests", @@ -76964,7 +76964,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -76986,7 +76986,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "components_unittests", @@ -77016,7 +77016,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77038,7 +77038,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "components_unittests", @@ -77068,7 +77068,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77090,7 +77090,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "crashpad_tests", @@ -77120,7 +77120,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77142,7 +77142,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "crypto_unittests", @@ -77172,7 +77172,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77194,7 +77194,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "gfx_unittests", @@ -77224,7 +77224,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77246,7 +77246,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "gfx_unittests", @@ -77276,7 +77276,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77298,7 +77298,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "gfx_unittests", @@ -77328,7 +77328,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77350,7 +77350,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "google_apis_unittests", @@ -77380,7 +77380,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77402,7 +77402,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77433,7 +77433,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77455,7 +77455,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77486,7 +77486,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77508,7 +77508,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77539,7 +77539,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77561,7 +77561,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77592,7 +77592,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77614,7 +77614,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77645,7 +77645,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77668,7 +77668,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77699,7 +77699,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77722,7 +77722,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77753,7 +77753,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77776,7 +77776,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77807,7 +77807,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77830,7 +77830,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77861,7 +77861,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77884,7 +77884,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77915,7 +77915,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77938,7 +77938,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -77969,7 +77969,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -77992,7 +77992,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78023,7 +78023,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78046,7 +78046,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78077,7 +78077,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78100,7 +78100,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78131,7 +78131,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78154,7 +78154,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78185,7 +78185,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78207,7 +78207,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78238,7 +78238,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78260,7 +78260,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78291,7 +78291,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78313,7 +78313,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78344,7 +78344,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78366,7 +78366,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78397,7 +78397,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78420,7 +78420,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78451,7 +78451,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78474,7 +78474,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78505,7 +78505,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78528,7 +78528,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_chrome_unittests", @@ -78558,7 +78558,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78580,7 +78580,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_chrome_unittests", @@ -78610,7 +78610,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78632,7 +78632,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_chrome_unittests", @@ -78662,7 +78662,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78684,7 +78684,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78715,7 +78715,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78737,7 +78737,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78768,7 +78768,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78790,7 +78790,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78821,7 +78821,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78843,7 +78843,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78874,7 +78874,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78896,7 +78896,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_components_unittests", @@ -78926,7 +78926,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -78948,7 +78948,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -78979,7 +78979,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79001,7 +79001,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_net_unittests", @@ -79031,7 +79031,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79054,7 +79054,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_remoting_unittests", @@ -79084,7 +79084,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79106,7 +79106,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79137,7 +79137,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79159,7 +79159,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79190,7 +79190,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79212,7 +79212,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79243,7 +79243,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79265,7 +79265,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79296,7 +79296,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79318,7 +79318,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_testing_unittests", @@ -79348,7 +79348,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79370,7 +79370,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_inttests", @@ -79400,7 +79400,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79422,7 +79422,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_inttests", @@ -79452,7 +79452,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79474,7 +79474,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_inttests", @@ -79504,7 +79504,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79526,7 +79526,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79557,7 +79557,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79579,7 +79579,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79610,7 +79610,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79632,7 +79632,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest", "--xcode-parallelization" ], @@ -79663,7 +79663,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79685,7 +79685,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_unittests", @@ -79715,7 +79715,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79737,7 +79737,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_unittests", @@ -79767,7 +79767,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79789,7 +79789,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_unittests", @@ -79819,7 +79819,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79841,7 +79841,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_inttests", @@ -79871,7 +79871,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79893,7 +79893,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_inttests", @@ -79923,7 +79923,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79945,7 +79945,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_inttests", @@ -79975,7 +79975,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -79997,7 +79997,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_unittests", @@ -80027,7 +80027,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80049,7 +80049,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_unittests", @@ -80079,7 +80079,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80101,7 +80101,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ios_web_view_unittests", @@ -80131,7 +80131,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80153,7 +80153,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "net_unittests", @@ -80183,7 +80183,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80205,7 +80205,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "services_unittests", @@ -80235,7 +80235,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80257,7 +80257,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "skia_unittests", @@ -80287,7 +80287,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80309,7 +80309,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "skia_unittests", @@ -80339,7 +80339,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80361,7 +80361,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "skia_unittests", @@ -80391,7 +80391,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80413,7 +80413,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "sql_unittests", @@ -80443,7 +80443,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80465,7 +80465,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ui_base_unittests", @@ -80495,7 +80495,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80517,7 +80517,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ui_base_unittests", @@ -80547,7 +80547,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80569,7 +80569,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "ui_base_unittests", @@ -80599,7 +80599,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, { @@ -80621,7 +80621,7 @@ "--out-dir", "${ISOLATED_OUTDIR}", "--xcode-build-version", - "14a5284g", + "14a5294e", "--xctest" ], "isolate_name": "url_unittests", @@ -80651,7 +80651,7 @@ ], "named_caches": [ { - "name": "xcode_ios_14a5284g", + "name": "xcode_ios_14a5294e", "path": "Xcode.app" }, {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 5d01837..8dac0d61 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -332,7 +332,7 @@ }, "cast_runner_pkg":{ "label": "//fuchsia_web/runners:cast_runner_pkg", - "type": "additonal_compile_target", + "type": "additional_compile_target", }, "cast_runner_unittests": { "label": "//fuchsia_web/runners:cast_runner_unittests", @@ -2003,7 +2003,7 @@ }, "web_runner_pkg": { "label": "//fuchsia_web/runners:web_runner_pkg", - "type": "additonal_compile_target", + "type": "additional_compile_target", }, "webapk_client_junit_tests": { "label": "//chrome/android/webapk/libs/client:webapk_client_junit_tests",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index cc04210..b83c659 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -1370,23 +1370,6 @@ '$mixin_append': { 'args': [ '--xcode-build-version', - '14a5284g' - ], - }, - 'swarming': { - 'named_caches': [ - { - 'name': 'xcode_ios_14a5284g', - 'path': 'Xcode.app', - }, - ], - }, - }, - # Xcode 14 beta 5. - 'xcode_14_beta_5': { - '$mixin_append': { - 'args': [ - '--xcode-build-version', '14a5294e' ], },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index d55ad97..cc58025 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -3454,7 +3454,7 @@ 'mac_beta_x64', 'mac_toolchain', 'out_dir_arg', - 'xcode_14_beta_5', + 'xcode_14_beta', # crbug/1343123: temporarily increasing readline timeout due to slow simulator start up time. 'xcode_14_readline_timeout', 'xctest',
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index 172cdd4..c199054 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -756,13 +756,6 @@ const base::Feature kOffsetParentNewSpecBehavior{ "OffsetParentNewSpecBehavior", base::FEATURE_DISABLED_BY_DEFAULT}; -// Makes form elements cancel previous form submissions made by the same form -// when the default event handler schedules a form submission. -// TODO(crbug.com/1234409): Remove this flag when this feature has been in -// stable for a release with no issues -const base::Feature kCancelFormSubmissionInDefaultHandler{ - "CancelFormSubmissionInDefaultHandler", base::FEATURE_ENABLED_BY_DEFAULT}; - // Enables the JPEG XL Image File Format (JXL). const base::Feature kJXL{"JXL", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index 210090334..8fb70b3 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -290,9 +290,6 @@ BLINK_COMMON_EXPORT extern const base::Feature kOffsetParentNewSpecBehavior; BLINK_COMMON_EXPORT extern const base::Feature - kCancelFormSubmissionInDefaultHandler; - -BLINK_COMMON_EXPORT extern const base::Feature kAlignFontDisplayAutoTimeoutWithLCPGoal; BLINK_COMMON_EXPORT extern const base::FeatureParam<int> kAlignFontDisplayAutoTimeoutWithLCPGoalTimeoutParam;
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.cc b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.cc index 26c444c..99ce343 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.cc
@@ -95,6 +95,8 @@ } } DCHECK_NE(leftmost_relation_, CSSSelector::kSubSelector); + DCHECK_LE(adjacent_distance_limit_, kInfiniteAdjacentDistance); + DCHECK_LE(depth_limit_, kInfiniteDepth); switch (leftmost_relation_) { case CSSSelector::kRelativeDescendant:
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.h b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.h index c64733c..0b4ac8f 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.h +++ b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context.h
@@ -58,8 +58,19 @@ // (.e.g. :has(~ .a > .b), :has(+ .a ~ .b > .c), // :has(~ .a > .b ~ .c), :has(+ .a ~ .b > .c ~ .d), kAllNextSiblingsFixedDepthDescendants, + + kTraversalScopeMax = kAllNextSiblingsFixedDepthDescendants, }; +// Unique value of each traversal type. The value can be used as a key of +// fast reject filter cache. +// +// These 3 values are stored by dividing the 4-byte field by: +// - depth limit : 0 ~ 13 (14bits) +// - adjacent distance limit : 14 ~ 27 (14 bits) +// - traversal scope : 28 ~ 31 (4 bits) +using CheckPseudoHasArgumentTraversalType = uint32_t; + class CORE_EXPORT CheckPseudoHasArgumentContext { STACK_ALLOCATED(); @@ -98,9 +109,24 @@ return pseudo_has_argument_hashes_; } + CheckPseudoHasArgumentTraversalType TraversalType() const { + return depth_limit_ | (adjacent_distance_limit_ << kDepthBits) | + (traversal_scope_ << (kDepthBits + kAdjacentBits)); + } + private: - const static int kInfiniteDepth = std::numeric_limits<int>::max(); - const static int kInfiniteAdjacentDistance = std::numeric_limits<int>::max(); + const static size_t kDepthBits = 14; + const static size_t kAdjacentBits = 14; + const static size_t kTraversalScopeBits = 4; + + const static int kInfiniteDepth = (1 << kDepthBits) - 1; + const static int kInfiniteAdjacentDistance = (1 << kAdjacentBits) - 1; + + static_assert(kTraversalScopeMax <= ((1 << kTraversalScopeBits) - 1), + "traversal scope size check"); + static_assert((kDepthBits + kAdjacentBits + kTraversalScopeBits) <= + sizeof(CheckPseudoHasArgumentTraversalType) * 8, + "traversal type size check"); inline const CSSSelector* GetCurrentRelationAndNextCompound( const CSSSelector* compound_selector, @@ -236,6 +262,8 @@ const CSSSelector* has_argument_; Vector<unsigned> pseudo_has_argument_hashes_; + + friend class CheckPseudoHasArgumentContextTest; }; // Subtree traversal iterator class for :has() argument checking. To solve the
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context_test.cc b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context_test.cc index 3201bc3b..86580441 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_argument_context_test.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_argument_context_test.cc
@@ -16,7 +16,9 @@ class CheckPseudoHasArgumentContextTest : public PageTestBase { protected: - const int kMax = std::numeric_limits<int>::max(); + const int kDepthMax = CheckPseudoHasArgumentContext::kInfiniteDepth; + const int kAdjacentMax = + CheckPseudoHasArgumentContext::kInfiniteAdjacentDistance; void TestArgumentContext( const String& selector_text, @@ -77,6 +79,18 @@ } } + CheckPseudoHasArgumentTraversalType GetTraversalType( + const char* selector_text) const { + CSSSelectorList selector_list = + css_test_helpers::ParseSelectorList(selector_text); + + EXPECT_EQ(selector_list.First()->GetPseudoType(), CSSSelector::kPseudoHas); + + CheckPseudoHasArgumentContext context( + selector_list.First()->SelectorList()->First()); + return context.TraversalType(); + } + template <unsigned length> void TestTraversalIteratorSteps( Document* document, @@ -142,28 +156,28 @@ TEST_F(CheckPseudoHasArgumentContextTest, TestArgumentMatchContext) { TestArgumentContext(":has(.a)", CSSSelector::kRelativeDescendant, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(.a ~ .b)", CSSSelector::kRelativeDescendant, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(.a ~ .b > .c)", CSSSelector::kRelativeDescendant, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(.a > .b)", CSSSelector::kRelativeDescendant, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(.a + .b)", CSSSelector::kRelativeDescendant, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(> .a .b)", CSSSelector::kRelativeChild, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(> .a ~ .b .c)", CSSSelector::kRelativeChild, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(> .a + .b .c)", CSSSelector::kRelativeChild, /* expected_adjacent_distance_limit */ 0, - /* expected_depth_limit */ kMax, kSubtree); + /* expected_depth_limit */ kDepthMax, kSubtree); TestArgumentContext(":has(> .a)", CSSSelector::kRelativeChild, /* expected_adjacent_distance_limit */ 0, /* expected_depth_limit */ 1, kFixedDepthDescendants); @@ -180,83 +194,85 @@ /* expected_adjacent_distance_limit */ 0, /* expected_depth_limit */ 2, kFixedDepthDescendants); TestArgumentContext(":has(~ .a .b)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, - /* expected_depth_limit */ kMax, kAllNextSiblingSubtrees); - TestArgumentContext(":has(~ .a + .b > .c ~ .d .e)", - CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, - /* expected_depth_limit */ kMax, kAllNextSiblingSubtrees); + /* expected_adjacent_distance_limit */ kAdjacentMax, + /* expected_depth_limit */ kDepthMax, + kAllNextSiblingSubtrees); + TestArgumentContext( + ":has(~ .a + .b > .c ~ .d .e)", CSSSelector::kRelativeIndirectAdjacent, + /* expected_adjacent_distance_limit */ kAdjacentMax, + /* expected_depth_limit */ kDepthMax, kAllNextSiblingSubtrees); TestArgumentContext(":has(~ .a)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext(":has(~ .a ~ .b)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext(":has(~ .a + .b)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext(":has(~ .a + .b ~ .c)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext(":has(~ .a > .b)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 1, kAllNextSiblingsFixedDepthDescendants); TestArgumentContext( ":has(~ .a + .b > .c ~ .d > .e)", CSSSelector::kRelativeIndirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 2, kAllNextSiblingsFixedDepthDescendants); - TestArgumentContext(":has(+ .a ~ .b .c)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, - /* expected_depth_limit */ kMax, kAllNextSiblingSubtrees); - TestArgumentContext(":has(+ .a ~ .b > .c + .d .e)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, - /* expected_depth_limit */ kMax, kAllNextSiblingSubtrees); + TestArgumentContext( + ":has(+ .a ~ .b .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kAdjacentMax, + /* expected_depth_limit */ kDepthMax, kAllNextSiblingSubtrees); + TestArgumentContext( + ":has(+ .a ~ .b > .c + .d .e)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ kAdjacentMax, + /* expected_depth_limit */ kDepthMax, kAllNextSiblingSubtrees); TestArgumentContext(":has(+ .a ~ .b)", CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext(":has(+ .a + .b ~ .c)", CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 0, kAllNextSiblings); TestArgumentContext( ":has(+ .a ~ .b > .c)", CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 1, kAllNextSiblingsFixedDepthDescendants); TestArgumentContext( ":has(+ .a ~ .b > .c + .d > .e)", CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ kMax, + /* expected_adjacent_distance_limit */ kAdjacentMax, /* expected_depth_limit */ 2, kAllNextSiblingsFixedDepthDescendants); TestArgumentContext(":has(+ .a .b)", CSSSelector::kRelativeDirectAdjacent, /* expected_adjacent_distance_limit */ 1, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a > .b .c)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 1, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a .b > .c)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 1, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a .b ~ .c)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 1, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a + .b .c)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 2, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a > .b + .c .d)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 1, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); - TestArgumentContext(":has(+ .a + .b > .c .d)", - CSSSelector::kRelativeDirectAdjacent, - /* expected_adjacent_distance_limit */ 2, - /* expected_depth_limit */ kMax, kOneNextSiblingSubtree); + /* expected_depth_limit */ kDepthMax, + kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a > .b .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a .b > .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a .b ~ .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a + .b .c)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 2, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a > .b + .c .d)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 1, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); + TestArgumentContext( + ":has(+ .a + .b > .c .d)", CSSSelector::kRelativeDirectAdjacent, + /* expected_adjacent_distance_limit */ 2, + /* expected_depth_limit */ kDepthMax, kOneNextSiblingSubtree); TestArgumentContext(":has(+ .a)", CSSSelector::kRelativeDirectAdjacent, /* expected_adjacent_distance_limit */ 1, /* expected_depth_limit */ 0, kOneNextSibling); @@ -281,6 +297,122 @@ /* expected_depth_limit */ 2, kOneNextSiblingFixedDepthDescendants); } +TEST_F(CheckPseudoHasArgumentContextTest, TestTraversalType) { + CheckPseudoHasArgumentTraversalType traversal_type; + + // traversal scope: kSubtree + // adjacent distance: 0 + // depth: Max + traversal_type = GetTraversalType(":has(.a)"); + EXPECT_EQ(traversal_type, 0x00003fffu); + EXPECT_EQ(GetTraversalType(":has(.a ~ .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(.a ~ .b > .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(.a > .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(.a + .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(> .a .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(> .a ~ .b .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(> .a + .b .c)"), traversal_type); + + // traversal scope: kAllNextSiblings + // adjacent distance: Max + // depth: 0 + traversal_type = GetTraversalType(":has(~ .a)"); + EXPECT_EQ(traversal_type, 0x1fffc000u); + EXPECT_EQ(GetTraversalType(":has(~ .a ~ .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(~ .a + .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(~ .a + .b ~ .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a ~ .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a + .b ~ .c)"), traversal_type); + + // traversal scope: kOneNextSiblingSubtree + // adjacent distance: 1 + // depth: Max + traversal_type = GetTraversalType(":has(+ .a .b)"); + EXPECT_EQ(traversal_type, 0x20007fffu); + EXPECT_EQ(GetTraversalType(":has(+ .a > .b .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a .b > .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a .b ~ .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a > .b + .c .d)"), traversal_type); + + // traversal scope: kOneNextSiblingSubtree + // adjacent distance: 2 + // depth: Max + traversal_type = GetTraversalType(":has(+ .a + .b .c)"); + EXPECT_EQ(traversal_type, 0x2000bfffu); + EXPECT_EQ(GetTraversalType(":has(+ .a + .b > .c .d)"), traversal_type); + + // traversal scope: kAllNextSiblingSubtrees + // adjacent distance: Max + // depth: Max + traversal_type = GetTraversalType(":has(~ .a .b)"); + EXPECT_EQ(traversal_type, 0x3fffffffu); + EXPECT_EQ(GetTraversalType(":has(~ .a + .b > .c ~ .d .e)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a ~ .b .c)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(+ .a ~ .b > .c + .d .e)"), traversal_type); + + // traversal scope: kOneNextSibling + // adjacent distance: 1 + // depth: 0 + traversal_type = GetTraversalType(":has(+ .a)"); + EXPECT_EQ(traversal_type, 0x40004000u); + + // traversal scope: kOneNextSibling + // adjacent distance: 2 + // depth: 0 + traversal_type = GetTraversalType(":has(+ .a + .b)"); + EXPECT_EQ(traversal_type, 0x40008000u); + + // traversal scope: kOneNextSibling + // adjacent distance: 3 + // depth: 0 + traversal_type = GetTraversalType(":has(+ .a + .b + .c)"); + EXPECT_EQ(traversal_type, 0x4000c000u); + + // traversal scope: kFixedDepthDescendants + // adjacent distance: 0 + // depth: 1 + traversal_type = GetTraversalType(":has(> .a)"); + EXPECT_EQ(traversal_type, 0x50000001u); + EXPECT_EQ(GetTraversalType(":has(> .a + .b)"), traversal_type); + EXPECT_EQ(GetTraversalType(":has(> .a ~ .b)"), traversal_type); + + // traversal scope: kFixedDepthDescendants + // adjacent distance: 0 + // depth: 2 + traversal_type = GetTraversalType(":has(> .a > .b)"); + EXPECT_EQ(traversal_type, 0x50000002u); + EXPECT_EQ(GetTraversalType(":has(> .a ~ .b > .c)"), traversal_type); + + // traversal scope: kOneNextSiblingFixedDepthDescendants + // adjacent distance: 1 + // depth: 1 + traversal_type = GetTraversalType(":has(+ .a > .b)"); + EXPECT_EQ(traversal_type, 0x60004001u); + EXPECT_EQ(GetTraversalType(":has(+ .a > .b ~ .c)"), traversal_type); + + // traversal scope: kOneNextSiblingFixedDepthDescendants + // adjacent distance: 2 + // depth: 2 + traversal_type = GetTraversalType(":has(+ .a + .b > .c ~ .d > .e)"); + EXPECT_EQ(traversal_type, 0x60008002u); + EXPECT_EQ(GetTraversalType(":has(+ .a + .b > .c ~ .d > .e ~ .f)"), + traversal_type); + + // traversal scope: kAllNextSiblingsFixedDepthDescendants + // adjacent distance: Max + // depth: 1 + traversal_type = GetTraversalType(":has(~ .a > .b)"); + EXPECT_EQ(traversal_type, 0x7fffc001u); + EXPECT_EQ(GetTraversalType(":has(+ .a ~ .b > .c)"), traversal_type); + + // traversal scope: kAllNextSiblingsFixedDepthDescendants + // adjacent distance: Max + // depth: 2 + traversal_type = GetTraversalType(":has(~ .a > .b > .c)"); + EXPECT_EQ(traversal_type, 0x7fffc002u); + EXPECT_EQ(GetTraversalType(":has(+ .a ~ .b > .c + .d > .e)"), traversal_type); +} + TEST_F(CheckPseudoHasArgumentContextTest, TestTraversalIteratorCase1) { // CheckPseudoHasArgumentTraversalScope::kSubtree
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.cc b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.cc index d28d77ad..b4df6a13 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.cc
@@ -52,6 +52,25 @@ return *entry.stored_value->value; } +// static +ElementCheckPseudoHasFastRejectFilterMap& +CheckPseudoHasCacheScope::GetFastRejectFilterMap( + const Document* document, + CheckPseudoHasArgumentTraversalType traversal_type) { + DCHECK(document); + DCHECK(document->GetCheckPseudoHasCacheScope()); + + auto entry = document->GetCheckPseudoHasCacheScope() + ->GetFastRejectFilterCache() + .insert(traversal_type, nullptr); + if (entry.is_new_entry) { + entry.stored_value->value = + MakeGarbageCollected<ElementCheckPseudoHasFastRejectFilterMap>(); + } + DCHECK(entry.stored_value->value); + return *entry.stored_value->value; +} + CheckPseudoHasCacheScope::Context::Context( const Document* document, const CheckPseudoHasArgumentContext& argument_context) @@ -64,6 +83,9 @@ cache_allowed_ = true; result_map_ = &CheckPseudoHasCacheScope::GetResultMap( document, argument_context.HasArgument()); + fast_reject_filter_map_ = + &CheckPseudoHasCacheScope::GetFastRejectFilterMap( + document, argument_context.TraversalType()); break; default: cache_allowed_ = false; @@ -225,4 +247,21 @@ return false; } +CheckPseudoHasFastRejectFilter& +CheckPseudoHasCacheScope::Context::EnsureFastRejectFilter(Element* element, + bool& is_new_entry) { + DCHECK(element); + DCHECK(cache_allowed_); + DCHECK(fast_reject_filter_map_); + + auto entry = fast_reject_filter_map_->insert(element, nullptr); + is_new_entry = entry.is_new_entry; + if (entry.is_new_entry) { + entry.stored_value->value = + std::make_unique<CheckPseudoHasFastRejectFilter>(); + } + DCHECK(entry.stored_value->value); + return *entry.stored_value->value.get(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h index fdb32852..7a0e72b63 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h +++ b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h
@@ -6,6 +6,8 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CHECK_PSEUDO_HAS_CACHE_SCOPE_H_ #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/css/check_pseudo_has_argument_context.h" +#include "third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" @@ -141,7 +143,7 @@ // because we can get the result of ':has(.a)' from the cache with the cache // key '.a'. // -// The :has() checking result cache uses 2 dimensional hash map to store the +// The :has() checking result cache uses a 2 dimensional hash map to store the // result. // - hashmap[<argument-selector>][<element>] = <result> // @@ -153,12 +155,39 @@ using CheckPseudoHasResultCache = HeapHashMap<String, Member<ElementCheckPseudoHasResultMap>>; -// CheckPseudoHasCacheScope is the stack-allocated scoping class for :has() -// pseudo class checking result cache. +// The :has() result cache keeps a bloom filter for rejecting :has() argument +// selector checking. // -// This class has hashmap to hold the checking result, so the lifecycle of the -// cache follows the lifecycle of the CheckPseudoHasCacheScope instance. -// (The hashmap for caching will be created at the construction of a +// The element identifier hashes in the bloom filter depend on the relationship +// between the :has() anchor element and the :has() argument subject element. +// The relationship can be categorized by this information in +// CheckPseudoHasArgumentContext. +// - traversal scope +// - adjacent limit +// - depth limit +// (Please refer the comment of CheckPseudoHasArgumentTraversalType) +// +// The CheckPseudoHasFastRejectFilterCache uses a 2 dimensional hash map to +// store the filter. +// - hashmap[<traversal type>][<element>] = <filter> +// +// ElementCheckPseudoHasFastRejectFilterMap is a hash map that stores the +// filter for each element. +// - hashmap[<element>] = <filter> +using ElementCheckPseudoHasFastRejectFilterMap = + HeapHashMap<Member<const Element>, + std::unique_ptr<CheckPseudoHasFastRejectFilter>>; +using CheckPseudoHasFastRejectFilterCache = + HeapHashMap<CheckPseudoHasArgumentTraversalType, + Member<ElementCheckPseudoHasFastRejectFilterMap>>; + +// CheckPseudoHasCacheScope is the stack-allocated scoping class for :has() +// pseudo class checking result cache and :has() pseudo class checking fast +// reject filter cache. +// +// This class has hashmap to hold the checking result and filter, so the +// lifecycle of the caches follow the lifecycle of the CheckPseudoHasCacheScope +// instance. (The hashmap for caching will be created at the construction of a // CheckPseudoHasCacheScope instance, and removed at the destruction of the // instance) // @@ -198,8 +227,10 @@ explicit CheckPseudoHasCacheScope(Document*); ~CheckPseudoHasCacheScope(); - // Context provides getter and setter of the cached :has() - // pseudo class checking result in ElementCheckPseudoHasResultMap. + // Context provides getter and setter of the following cache items. + // - :has() pseudo class checking result in ElementCheckPseudoHasResultMap + // - :has() pseudo class checking fast reject filter in + // ElementCheckPseudoHasFastRejectFilterMap. class CORE_EXPORT Context { STACK_ALLOCATED(); @@ -218,6 +249,9 @@ bool AlreadyChecked(Element*) const; + CheckPseudoHasFastRejectFilter& EnsureFastRejectFilter(Element*, + bool& is_new_entry); + inline bool CacheAllowed() const { return cache_allowed_; } private: @@ -231,20 +265,35 @@ bool HasSiblingsWithAllDescendantsOrNextSiblingsChecked(Element*) const; bool HasAncestorsWithAllDescendantsOrNextSiblingsChecked(Element*) const; - size_t GetCacheCount() { return cache_allowed_ ? result_map_->size() : 0; } + size_t GetResultCacheCountForTesting() const { + return cache_allowed_ ? result_map_->size() : 0; + } + + size_t GetFastRejectFilterCacheCountForTesting() const { + return cache_allowed_ ? fast_reject_filter_map_->size() : 0; + } bool cache_allowed_; ElementCheckPseudoHasResultMap* result_map_; + ElementCheckPseudoHasFastRejectFilterMap* fast_reject_filter_map_; const CheckPseudoHasArgumentContext& argument_context_; }; private: static ElementCheckPseudoHasResultMap& GetResultMap(const Document*, const CSSSelector*); + static ElementCheckPseudoHasFastRejectFilterMap& GetFastRejectFilterMap( + const Document*, + CheckPseudoHasArgumentTraversalType); CheckPseudoHasResultCache& GetResultCache() { return result_cache_; } + CheckPseudoHasFastRejectFilterCache& GetFastRejectFilterCache() { + return fast_reject_filter_cache_; + } + CheckPseudoHasResultCache result_cache_; + CheckPseudoHasFastRejectFilterCache fast_reject_filter_cache_; Document* document_; };
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc index 74581b4..0d1a543 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_cache_scope_context_test.cc
@@ -95,12 +95,13 @@ } template <unsigned length> - void CheckCacheResults(Document* document, - String query_name, - const char* selector_text, - unsigned expected_result_cache_count, - const ExpectedResultCacheEntry ( - &expected_result_cache_entries)[length]) const { + void CheckCacheResults( + Document* document, + String query_name, + const char* selector_text, + unsigned expected_result_cache_count, + const ExpectedResultCacheEntry (&expected_result_cache_entries)[length], + unsigned expected_fast_reject_filter_cache_count) const { CSSSelectorVector selector_vector = CSSParser::ParseSelector( MakeGarbageCollected<CSSParserContext>( *document, NullURL(), true /* origin_clean */, Referrer(), @@ -124,7 +125,8 @@ CheckPseudoHasCacheScope::Context cache_scope_context(document, argument_context); - EXPECT_EQ(expected_result_cache_count, cache_scope_context.GetCacheCount()) + EXPECT_EQ(expected_result_cache_count, + cache_scope_context.GetResultCacheCountForTesting()) << "Failed : " << query_name; for (ExpectedResultCacheEntry expected_result_cache_entry : @@ -161,6 +163,10 @@ break; } } + + EXPECT_EQ(expected_fast_reject_filter_cache_count, + cache_scope_context.GetFastRejectFilterCacheCountForTesting()) + << "Failed : " << query_name; } template <unsigned cache_size> @@ -170,7 +176,8 @@ bool expected_match_result, unsigned expected_result_cache_count, const ExpectedResultCacheEntry ( - &expected_result_cache_entries)[cache_size]) const { + &expected_result_cache_entries)[cache_size], + unsigned expected_fast_reject_filter_cache_count) const { Element* query_scope_element = document->getElementById(query_scope_element_id); ASSERT_TRUE(query_scope_element); @@ -184,9 +191,9 @@ query_scope_element->matches(selector_text)) << "Failed : " << query_name; - CheckCacheResults(document, query_name, selector_text, - expected_result_cache_count, - expected_result_cache_entries); + CheckCacheResults( + document, query_name, selector_text, expected_result_cache_count, + expected_result_cache_entries, expected_fast_reject_filter_cache_count); } template <unsigned query_result_size, unsigned cache_size> @@ -197,7 +204,8 @@ const String (&expected_results)[query_result_size], unsigned expected_result_cache_count, const ExpectedResultCacheEntry ( - &expected_result_cache_entries)[cache_size]) const { + &expected_result_cache_entries)[cache_size], + unsigned expected_fast_reject_filter_cache_count) const { Element* query_scope_element = document->getElementById(query_scope_element_id); ASSERT_TRUE(query_scope_element); @@ -220,9 +228,9 @@ << "Failed :" << query_name << " result at index " << i; } - CheckCacheResults(document, query_name, selector_text, - expected_result_cache_count, - expected_result_cache_entries); + CheckCacheResults( + document, query_name, selector_text, expected_result_cache_count, + expected_result_cache_entries, expected_fast_reject_filter_cache_count); } }; @@ -302,7 +310,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div2", ":has(.b)", /* expected_match_result */ true, @@ -333,7 +342,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div2", ":has(.c)", /* expected_match_result */ false, @@ -363,7 +373,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, Case1StartsWithChildCombinator) { @@ -477,7 +488,8 @@ {"#div231", kNotCached, kNotYetChecked}, {"#div24", kNotCached, kNotYetChecked}, {"#div3", kNotCached, kNotYetChecked}, - {"#div31", kNotCached, kNotYetChecked}}); + {"#div31", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div2", ":has(> .a .b)", /* expected_match_result */ false, @@ -522,7 +534,8 @@ {"#div231", kNotCached, kAlreadyNotMatched}, {"#div24", kNotCached, kAlreadyNotMatched}, {"#div3", kNotCached, kNotYetChecked}, - {"#div31", kNotCached, kNotYetChecked}}); + {"#div31", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div2", ":has(> .a .c)", /* expected_match_result */ false, @@ -567,7 +580,8 @@ {"#div231", kNotCached, kAlreadyNotMatched}, {"#div24", kNotCached, kAlreadyNotMatched}, {"#div3", kNotCached, kNotYetChecked}, - {"#div31", kNotCached, kNotYetChecked}}); + {"#div31", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, Case2StartsWithIndirectAdjacent) { @@ -632,7 +646,8 @@ {"#div251", kNotCached, kNotYetChecked}, {"#div252", kNotCached, kNotYetChecked}, {"#div3", kNotCached, kNotYetChecked}, - {"#div31", kNotCached, kNotYetChecked}}); + {"#div31", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div22", ":has(~ .b)", /* expected_match_result */ false, @@ -658,7 +673,8 @@ {"#div251", kNotCached, kNotYetChecked}, {"#div252", kNotCached, kNotYetChecked}, {"#div3", kNotCached, kNotYetChecked}, - {"#div31", kNotCached, kNotYetChecked}}); + {"#div31", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, Case2StartsWithDirectAdjacent) { @@ -756,7 +772,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div22", ":has(+ .a ~ .b)", /* expected_match_result */ false, @@ -797,7 +814,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div22", ":has(+ .a ~ .c)", /* expected_match_result */ false, @@ -838,7 +856,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, Case3) { @@ -945,7 +964,8 @@ {"#div241", kNotCached, kNotYetChecked}, {"#div25", kNotCached, kNotYetChecked}, {"#div3", kNotCached, kNotYetChecked}, - {"#div4", kNotCached, kNotYetChecked}}); + {"#div4", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div1", ":has(+ .a .b)", /* expected_match_result */ false, @@ -988,7 +1008,8 @@ {"#div241", kNotCached, kAlreadyNotMatched}, {"#div25", kNotCached, kAlreadyNotMatched}, {"#div3", kNotCached, kNotYetChecked}, - {"#div4", kNotCached, kNotYetChecked}}); + {"#div4", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div22", ":has(+ .a .c)", /* expected_match_result */ false, @@ -1031,7 +1052,8 @@ {"#div241", kNotCached, kNotYetChecked}, {"#div25", kNotCached, kNotYetChecked}, {"#div3", kNotCached, kNotYetChecked}, - {"#div4", kNotCached, kNotYetChecked}}); + {"#div4", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, Case4) { @@ -1151,7 +1173,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div21", ":has(~ .a .b)", /* expected_match_result */ true, @@ -1201,7 +1224,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div1", ":has(~ .a .b)", /* expected_match_result */ false, @@ -1249,7 +1273,8 @@ {"#div3", kNotCached, kAlreadyNotMatched}, {"#div31", kNotCached, kAlreadyNotMatched}, {"#div4", kNotCached, kAlreadyNotMatched}, - {"#div41", kNotCached, kAlreadyNotMatched}}); + {"#div41", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div22", ":has(~ .a .c)", /* expected_match_result */ false, @@ -1297,7 +1322,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div4", kNotCached, kNotYetChecked}, - {"#div41", kNotCached, kNotYetChecked}}); + {"#div41", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); } TEST_F(CheckPseudoHasCacheScopeContextTest, @@ -1341,12 +1367,14 @@ {"#div131", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, {"#div14", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div11", ":has(.a .b)", /* expected_match_result */ false, /* expected_result_cache_count */ 1, - {{"#div11", kNotMatched, kSameAsCached}}); + {{"#div11", kNotMatched, kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div12", ":has(.a .b)", /* expected_match_result */ true, @@ -1363,7 +1391,8 @@ kSameAsCached}, {"#div13", kNotCached, kNotYetChecked}, {"#div131", kNotCached, kNotYetChecked}, - {"#div14", kNotCached, kNotYetChecked}}); + {"#div14", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); // ':has(.a .b)' does not match #div1211 but this caches possibly matched // elements because argument selector checking can cross over the :has() @@ -1383,7 +1412,8 @@ kSameAsCached}, {"#div13", kNotCached, kNotYetChecked}, {"#div131", kNotCached, kNotYetChecked}, - {"#div14", kNotCached, kNotYetChecked}}); + {"#div14", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); // ':has(.a .b)' does not match #div13 but this caches possibly matched // elements because argument selector checking can cross over the :has() @@ -1403,7 +1433,8 @@ {"#div13", kNotMatchedAndSomeChildrenChecked, kSameAsCached}, {"#div131", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, - {"#div14", kNotCached, kNotYetChecked}}); + {"#div14", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); TestQuerySelectorAll( document, "main", ":has(.a .b)", {"div1", "div12", "div121"}, @@ -1422,7 +1453,8 @@ {"#div131", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, {"#div14", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 5); } TEST_F(CheckPseudoHasCacheScopeContextTest, @@ -1453,7 +1485,8 @@ {"#div111", kMatched, kSameAsCached}, {"#div1111", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, {"#div11111", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div11", ":has(> .a .b)", /* expected_match_result */ false, @@ -1463,7 +1496,8 @@ {"#div111", kMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, {"#div1111", kNotCached, kAlreadyNotMatched}, - {"#div11111", kNotCached, kAlreadyNotMatched}}); + {"#div11111", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestQuerySelectorAll( document, "main", ":has(> .a .b)", {"div1", "div111"}, @@ -1474,7 +1508,8 @@ kSameAsCached}, {"#div1111", kNotMatchedAndSomeChildrenChecked, kSameAsCached}, {"#div11111", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 2); } TEST_F(CheckPseudoHasCacheScopeContextTest, @@ -1515,7 +1550,8 @@ /* expected_result_cache_count */ 2, {{"#div112", kNotMatchedAndSomeChildrenChecked, kSameAsCached}, {"#div1121", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div111", ":has(> .a .b)", /* expected_match_result */ true, @@ -1525,7 +1561,8 @@ {"#div11111", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, {"#div1112", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, - kSameAsCached}}); + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div11", ":has(> .a .b)", /* expected_match_result */ true, @@ -1541,7 +1578,8 @@ kSameAsCached}, {"#div1121", kNotCached, kAlreadyNotMatched}, {"#div113", kNotCached, kAlreadyNotMatched}, - {"#div1131", kNotCached, kAlreadyNotMatched}}); + {"#div1131", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div1", ":has(> .a .b)", /* expected_match_result */ false, @@ -1558,7 +1596,8 @@ {"#div113", kNotCached, kAlreadyNotMatched}, {"#div1131", kNotCached, kAlreadyNotMatched}, {"#div12", kNotCached, kAlreadyNotMatched}, - {"#div121", kNotCached, kAlreadyNotMatched}}); + {"#div121", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestQuerySelectorAll( document, "main", ":has(> .a .b) ~ .c .d", {"div1131", "div121"}, @@ -1583,7 +1622,250 @@ {"#div12", kNotCached, kAlreadyNotMatched}, {"#div121", kNotCached, kAlreadyNotMatched}, {"#div2", kNotCached, kNotYetChecked}, - {"#div21", kNotCached, kNotYetChecked}}); + {"#div21", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 4); +} + +TEST_F(CheckPseudoHasCacheScopeContextTest, + QuerySelectorAllCase2NonSubjectHas) { + // CheckPseudoHasArgumentTraversalScope::kAllNextSiblings + + auto* document = HTMLDocument::CreateForTest(); + document->write(R"HTML( + <!DOCTYPE html> + <main id=main> + <div id=div1> + <div id=div11 class=a> + <div id=div111> + <div id=div1111 class=b></div> + </div> + <div id=div112 class=a></div> + </div> + <div id=div12> + <div id=div121> + <div id=div1211 class=b></div> + </div> + <div id=div122></div> + </div> + <div id=div13></div> + </div> + <div id=div2 class=a></div> + </main> + )HTML"); + + TestMatches(document, "div1111", ":has(~ .a) .b", + /* expected_match_result */ true, + /* expected_result_cache_count */ 3, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kNotCached, kNotYetChecked}, + {"#div11", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div111", kMatched, kSameAsCached}, + {"#div1111", kNotCached, kNotYetChecked}, + {"#div112", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div12", kNotCached, kNotYetChecked}, + {"#div121", kNotCached, kNotYetChecked}, + {"#div1211", kNotCached, kNotYetChecked}, + {"#div122", kNotCached, kNotYetChecked}, + {"#div13", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); + + TestMatches(document, "div1211", ":has(~ .a) .b", + /* expected_match_result */ true, + /* expected_result_cache_count */ 7, + {{"main", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div1", kMatchedAndSomeChildrenChecked, kSameAsCached}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div111", kNotCached, kNotYetChecked}, + {"#div1111", kNotCached, kNotYetChecked}, + {"#div112", kNotCached, kNotYetChecked}, + {"#div12", kNotMatchedAndSomeChildrenChecked, kSameAsCached}, + {"#div121", kNotMatched, kSameAsCached}, + {"#div1211", kNotCached, kNotYetChecked}, + {"#div122", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div13", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div2", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 3); + + TestQuerySelectorAll( + document, "main", ":has(~ .a) .b", {"div1111", "div1211"}, + /* expected_result_cache_count */ 10, + {{"main", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div1", kMatchedAndSomeChildrenChecked, kSameAsCached}, + {"#div11", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div111", kMatched, kSameAsCached}, + {"#div1111", kNotCached, kNotYetChecked}, + {"#div112", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div12", kNotMatchedAndSomeChildrenChecked, kSameAsCached}, + {"#div121", kNotMatched, kSameAsCached}, + {"#div1211", kNotCached, kNotYetChecked}, + {"#div122", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div13", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div2", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 4); +} + +TEST_F(CheckPseudoHasCacheScopeContextTest, + QuerySelectorAllCase3NonSubjectHas) { + // CheckPseudoHasArgumentTraversalScope::kOneNextSiblingSubtree + + auto* document = HTMLDocument::CreateForTest(); + document->write(R"HTML( + <!DOCTYPE html> + <main id=main> + <div id=div1> + <div id=div11 class=c></div> + </div> + <div id=div2 class=a> + <div id=div21> + <div id=div211 class=c></div> + </div> + <div id=div22 class=a> + <div id=div221 class=b></div> + </div> + <div id=div23> + <div id=div231 class=b></div> + </div> + </div> + </main> + )HTML"); + + TestMatches(document, "div11", ":has(+ .a .b) .c", + /* expected_match_result */ true, + /* expected_result_cache_count */ 3, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kMatched, kSameAsCached}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kNotCached, kNotYetChecked}, + {"#div211", kNotCached, kNotYetChecked}, + {"#div22", kNotCached, kNotYetChecked}, + {"#div221", kNotCached, kNotYetChecked}, + {"#div23", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div231", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); + + TestMatches(document, "div211", ":has(+ .a .b) .c", + /* expected_match_result */ true, + /* expected_result_cache_count */ 3, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kNotCached, kNotYetChecked}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kMatched, kSameAsCached}, + {"#div211", kNotCached, kNotYetChecked}, + {"#div22", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div221", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div23", kNotCached, kNotYetChecked}, + {"#div231", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); + + TestQuerySelectorAll( + document, "main", ":has(+ .a .b) .c", {"div11", "div211"}, + /* expected_result_cache_count */ 6, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kMatched, kSameAsCached}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kMatched, kSameAsCached}, + {"#div211", kNotCached, kNotYetChecked}, + {"#div22", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div221", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div23", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div231", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 2); +} + +TEST_F(CheckPseudoHasCacheScopeContextTest, + QuerySelectorAllCase4NonSubjectHas) { + // CheckPseudoHasArgumentTraversalScope::kAllNextSiblingSubtrees + + auto* document = HTMLDocument::CreateForTest(); + document->write(R"HTML( + <!DOCTYPE html> + <main id=main> + <div id=div1> + <div id=div11 class=c></div> + </div> + <div id=div2 class=a> + <div id=div21> + <div id=div211> + <div id=div2111 class=c></div> + </div> + <div id=div212 class=a> + <div id=div2121 class=b></div> + </div> + </div> + <div id=div22> + <div id=div221 class=b></div> + </div> + </div> + </main> + )HTML"); + + TestMatches(document, "div11", ":has(~ .a .b) .c", + /* expected_match_result */ true, + /* expected_result_cache_count */ 3, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kMatched, kSameAsCached}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kNotCached, kNotYetChecked}, + {"#div211", kNotCached, kNotYetChecked}, + {"#div2111", kNotCached, kNotYetChecked}, + {"#div212", kNotCached, kNotYetChecked}, + {"#div2121", kNotCached, kNotYetChecked}, + {"#div22", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div221", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 1); + + TestMatches(document, "div2111", ":has(~ .a .b) .c", + /* expected_match_result */ true, + /* expected_result_cache_count */ 3, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kNotCached, kNotYetChecked}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div211", kMatched, kSameAsCached}, + {"#div2111", kNotCached, kNotYetChecked}, + {"#div212", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div2121", kNotCached, kAlreadyNotMatched}, + {"#div22", kNotCached, kNotYetChecked}, + {"#div221", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 1); + + TestQuerySelectorAll( + document, "main", ":has(~ .a .b) .c", {"div11", "div2111"}, + /* expected_result_cache_count */ 6, + {{"main", kNotCached, kNotYetChecked}, + {"#div1", kMatched, kSameAsCached}, + {"#div11", kNotCached, kNotYetChecked}, + {"#div2", kNotCached, kNotYetChecked}, + {"#div21", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div211", kMatched, kSameAsCached}, + {"#div2111", kNotCached, kNotYetChecked}, + {"#div212", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}, + {"#div2121", kNotCached, kAlreadyNotMatched}, + {"#div22", kNotCheckedAndSomeChildrenChecked, kNotYetChecked}, + {"#div221", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, + kSameAsCached}}, + /* expected_fast_reject_filter_cache_count */ 2); } TEST_F(CheckPseudoHasCacheScopeContextTest, @@ -1647,7 +1929,8 @@ {"#div5", kNotCached, kAlreadyNotMatched}, {"#div51", kNotCached, kAlreadyNotMatched}, {"#div6", kNotCached, kAlreadyNotMatched}, - {"#div61", kNotCached, kAlreadyNotMatched}}); + {"#div61", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div11", ":has(+ .a ~ .b .c)", /* expected_match_result */ true, @@ -1662,7 +1945,8 @@ {"#div14", kNotMatchedAndAllDescendantsOrNextSiblingsChecked, kSameAsCached}, {"#div141", kNotCached, kAlreadyNotMatched}, - {"#div15", kNotCached, kAlreadyNotMatched}}); + {"#div15", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestMatches(document, "div12", ":has(+ .a ~ .b .c)", /* expected_match_result */ false, @@ -1676,7 +1960,8 @@ {"#div132", kNotCached, kAlreadyNotMatched}, {"#div14", kNotCached, kAlreadyNotMatched}, {"#div141", kNotCached, kAlreadyNotMatched}, - {"#div15", kNotCached, kAlreadyNotMatched}}); + {"#div15", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 1); TestQuerySelectorAll( document, "main", ":has(+ .a ~ .b .c)", {"div11", "div4"}, @@ -1705,7 +1990,8 @@ {"#div5", kNotCached, kAlreadyNotMatched}, {"#div51", kNotCached, kAlreadyNotMatched}, {"#div6", kNotCached, kAlreadyNotMatched}, - {"#div61", kNotCached, kAlreadyNotMatched}}); + {"#div61", kNotCached, kAlreadyNotMatched}}, + /* expected_fast_reject_filter_cache_count */ 3); } TEST_F(CheckPseudoHasCacheScopeContextTest, QuerySelectorAllCase5) { @@ -1748,7 +2034,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div21", ":has(+ .a)", /* expected_match_result */ true, @@ -1765,7 +2052,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestQuerySelectorAll(document, "main", ":has(+ .a)", {"div2", "div21"}, /* expected_result_cache_count */ 0, @@ -1781,7 +2069,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); } TEST_F(CheckPseudoHasCacheScopeContextTest, QuerySelectorAllCase6) { @@ -1825,7 +2114,8 @@ {"#div12", kNotCached, kNotYetChecked}, {"#div121", kNotCached, kNotYetChecked}, {"#div122", kNotCached, kNotYetChecked}, - {"#div123", kNotCached, kNotYetChecked}}); + {"#div123", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div112", ":has(> .a)", /* expected_match_result */ true, @@ -1842,7 +2132,8 @@ {"#div12", kNotCached, kNotYetChecked}, {"#div121", kNotCached, kNotYetChecked}, {"#div122", kNotCached, kNotYetChecked}, - {"#div123", kNotCached, kNotYetChecked}}); + {"#div123", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div12", ":has(> .a)", /* expected_match_result */ true, @@ -1859,7 +2150,8 @@ {"#div12", kNotCached, kNotYetChecked}, {"#div121", kNotCached, kNotYetChecked}, {"#div122", kNotCached, kNotYetChecked}, - {"#div123", kNotCached, kNotYetChecked}}); + {"#div123", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestQuerySelectorAll(document, "main", ":has(> .a)", {"div1", "div112", "div12"}, @@ -1876,7 +2168,8 @@ {"#div12", kNotCached, kNotYetChecked}, {"#div121", kNotCached, kNotYetChecked}, {"#div122", kNotCached, kNotYetChecked}, - {"#div123", kNotCached, kNotYetChecked}}); + {"#div123", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); } TEST_F(CheckPseudoHasCacheScopeContextTest, QuerySelectorAllCase7) { @@ -1917,7 +2210,8 @@ {"#div23", kNotCached, kNotYetChecked}, {"#div231", kNotCached, kNotYetChecked}, {"#div232", kNotCached, kNotYetChecked}, - {"#div233", kNotCached, kNotYetChecked}}); + {"#div233", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div22", ":has(+ .a > .b)", /* expected_match_result */ true, @@ -1933,7 +2227,8 @@ {"#div23", kNotCached, kNotYetChecked}, {"#div231", kNotCached, kNotYetChecked}, {"#div232", kNotCached, kNotYetChecked}, - {"#div233", kNotCached, kNotYetChecked}}); + {"#div233", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestQuerySelectorAll(document, "main", ":has(+ .a > .b)", {"div1", "div22"}, /* expected_result_cache_count */ 0, @@ -1948,7 +2243,8 @@ {"#div23", kNotCached, kNotYetChecked}, {"#div231", kNotCached, kNotYetChecked}, {"#div232", kNotCached, kNotYetChecked}, - {"#div233", kNotCached, kNotYetChecked}}); + {"#div233", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); } TEST_F(CheckPseudoHasCacheScopeContextTest, QuerySelectorAllCase8) { @@ -1998,7 +2294,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div2", ":has(~ .a > .b)", /* expected_match_result */ true, @@ -2018,7 +2315,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestMatches(document, "div21", ":has(~ .a > .b)", /* expected_match_result */ true, @@ -2038,7 +2336,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); TestQuerySelectorAll(document, "main", ":has(~ .a > .b)", {"div1", "div2", "div21"}, @@ -2058,7 +2357,8 @@ {"#div3", kNotCached, kNotYetChecked}, {"#div31", kNotCached, kNotYetChecked}, {"#div32", kNotCached, kNotYetChecked}, - {"#div33", kNotCached, kNotYetChecked}}); + {"#div33", kNotCached, kNotYetChecked}}, + /* expected_fast_reject_filter_cache_count */ 0); } } // namespace blink
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.cc b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.cc index ccd7d981..1a0b46f1 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.cc
@@ -40,14 +40,15 @@ void CheckPseudoHasFastRejectFilter::AddElementIdentifierHashes( const Element& element) { - filter_.Add(GetTagHash(element.LocalNameForSelectorMatching())); + DCHECK(filter_.get()); + filter_->Add(GetTagHash(element.LocalNameForSelectorMatching())); if (element.HasID()) - filter_.Add(GetIdHash(element.IdForStyleResolution())); + filter_->Add(GetIdHash(element.IdForStyleResolution())); if (element.HasClass()) { const SpaceSplitString& class_names = element.ClassNames(); wtf_size_t count = class_names.size(); for (wtf_size_t i = 0; i < count; ++i) - filter_.Add(GetClassHash(class_names[i])); + filter_->Add(GetClassHash(class_names[i])); } AttributeCollection attributes = element.AttributesWithoutUpdate(); for (const auto& attribute_item : attributes) { @@ -56,16 +57,17 @@ continue; auto lower = attribute_name.IsLowerASCII() ? attribute_name : attribute_name.LowerASCII(); - filter_.Add(GetAttributeHash(lower)); + filter_->Add(GetAttributeHash(lower)); } } bool CheckPseudoHasFastRejectFilter::FastReject( const Vector<unsigned>& pseudo_has_argument_hashes) const { + DCHECK(filter_.get()); if (pseudo_has_argument_hashes.IsEmpty()) return false; for (unsigned hash : pseudo_has_argument_hashes) { - if (!filter_.MayContain(hash)) + if (!filter_->MayContain(hash)) return true; } return false; @@ -115,4 +117,10 @@ } } +void CheckPseudoHasFastRejectFilter::AllocateBloomFilter() { + if (filter_) + return; + filter_ = std::make_unique<FastRejectFilter>(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h index aefe9ac0..3a7776a 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h +++ b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h
@@ -50,8 +50,12 @@ bool FastReject(const Vector<unsigned>& pseudo_has_argument_hashes) const; + void AllocateBloomFilter(); + bool BloomFilterAllocated() const { return filter_.get(); } + private: - WTF::BloomFilter<12> filter_; + using FastRejectFilter = WTF::BloomFilter<12>; + std::unique_ptr<FastRejectFilter> filter_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter_test.cc b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter_test.cc index 4d56f8f..7cf82b87 100644 --- a/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter_test.cc +++ b/third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter_test.cc
@@ -58,6 +58,10 @@ TEST_F(CheckPseudoHasFastRejectFilterTest, CheckFastReject) { CheckPseudoHasFastRejectFilter filter; + EXPECT_FALSE(filter.BloomFilterAllocated()); + filter.AllocateBloomFilter(); + EXPECT_TRUE(filter.BloomFilterAllocated()); + AddElementIdentifierHashes( filter, {{/* tag_name */ "div", /* id */ "d1", /* class_names */ "a", /* attribute_name */ "attr1", /* attribute_value */ "val1"},
diff --git a/third_party/blink/renderer/core/css/container_query.cc b/third_party/blink/renderer/core/css/container_query.cc index 7abef359..ca19253 100644 --- a/third_party/blink/renderer/core/css/container_query.cc +++ b/third_party/blink/renderer/core/css/container_query.cc
@@ -20,6 +20,8 @@ physical_axes_ |= kPhysicalAxisHorizontal; if (feature_flags & MediaQueryExpNode::kFeatureHeight) physical_axes_ |= kPhysicalAxisVertical; + if (feature_flags & MediaQueryExpNode::kFeatureStyle) + has_style_query_ = true; } unsigned ContainerSelector::Type(WritingMode writing_mode) const {
diff --git a/third_party/blink/renderer/core/css/container_query.h b/third_party/blink/renderer/core/css/container_query.h index 7c548c6..fea83e6 100644 --- a/third_party/blink/renderer/core/css/container_query.h +++ b/third_party/blink/renderer/core/css/container_query.h
@@ -34,10 +34,18 @@ // for this selector to match. unsigned Type(WritingMode) const; + bool SelectsSizeContainers() const { + return physical_axes_ != kPhysicalAxisNone || + logical_axes_ != kLogicalAxisNone; + } + + bool SelectsStyleContainers() const { return has_style_query_; } + private: AtomicString name_; PhysicalAxes physical_axes_{kPhysicalAxisNone}; LogicalAxes logical_axes_{kLogicalAxisNone}; + bool has_style_query_{false}; }; class CORE_EXPORT ContainerQuery final
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.cc b/third_party/blink/renderer/core/css/container_query_evaluator.cc index a18728d..d4ef72f 100644 --- a/third_party/blink/renderer/core/css/container_query_evaluator.cc +++ b/third_party/blink/renderer/core/css/container_query_evaluator.cc
@@ -52,13 +52,13 @@ // static Element* ContainerQueryEvaluator::FindContainer( - Element* context_element, + Element* starting_element, const ContainerSelector& container_selector) { // TODO(crbug.com/1213888): Cache results. - for (Element* element = context_element; element; + for (Element* element = starting_element; element; element = element->ParentOrShadowHostElement()) { if (const ComputedStyle* style = element->GetComputedStyle()) { - if (style->IsContainerForSizeContainerQueries() && + if (style->StyleType() == kPseudoIdNone && Matches(*style, container_selector)) { return element; } @@ -68,15 +68,30 @@ return nullptr; } -bool ContainerQueryEvaluator::EvalAndAdd(const StyleRecalcContext& context, +bool ContainerQueryEvaluator::EvalAndAdd(const Element& matching_element, + const StyleRecalcContext& context, const ContainerQuery& query, MatchResult& match_result) { - Element* container = FindContainer(context.container, query.Selector()); + const ContainerSelector& selector = query.Selector(); + Element* starting_element = + selector.SelectsSizeContainers() + ? context.container + : matching_element.ParentOrShadowHostElement(); + Element* container = FindContainer(starting_element, selector); if (!container) return false; ContainerQueryEvaluator* evaluator = container->GetContainerQueryEvaluator(); - if (!evaluator) - return false; + if (!evaluator) { + if (selector.SelectsSizeContainers() || + !selector.SelectsStyleContainers()) { + return false; + } + evaluator = &container->EnsureContainerQueryEvaluator(); + evaluator->SetData(container->GetDocument(), *container, PhysicalSize(), + kPhysicalAxisNone); + } + // TODO(crbug.com/1302630): style() queries should not compare with + // context.container. Change change = (context.container == container) ? Change::kNearestContainer : Change::kDescendantContainers;
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.h b/third_party/blink/renderer/core/css/container_query_evaluator.h index 7356ac0..8905b08 100644 --- a/third_party/blink/renderer/core/css/container_query_evaluator.h +++ b/third_party/blink/renderer/core/css/container_query_evaluator.h
@@ -27,11 +27,12 @@ class CORE_EXPORT ContainerQueryEvaluator final : public GarbageCollected<ContainerQueryEvaluator> { public: - // Look for a container query container in the inclusive ancestor - // chain of `context_element`. - static Element* FindContainer(Element* context_element, + // Look for a container query container in the shadow-including inclusive + // ancestor chain of 'starting_element'. + static Element* FindContainer(Element* starting_element, const ContainerSelector&); - static bool EvalAndAdd(const StyleRecalcContext&, + static bool EvalAndAdd(const Element& matching_element, + const StyleRecalcContext&, const ContainerQuery&, MatchResult&);
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc index 434e3ee1..c4031871 100644 --- a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc +++ b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
@@ -20,6 +20,7 @@ #include "third_party/blink/renderer/core/dom/dom_token_list.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" +#include "third_party/blink/renderer/core/dom/parent_node.h" #include "third_party/blink/renderer/core/execution_context/security_context.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/html/html_div_element.h" @@ -626,4 +627,42 @@ EXPECT_TRUE(Eval("style(--my-prop: 10px )", "--my-prop", "10px ")); } +TEST_F(ContainerQueryEvaluatorTest, FindContainer) { + SetBodyInnerHTML(R"HTML( + <div style="container-name:outer;container-type:size"> + <div style="container-name:outer"> + <div style="container-type: size"> + <div> + <div></div> + </div> + </div> + </div> + </div> + )HTML"); + + UpdateAllLifecyclePhasesForTest(); + + Element* outer_size = ParentNode::firstElementChild(*GetDocument().body()); + Element* outer = ParentNode::firstElementChild(*outer_size); + Element* inner_size = ParentNode::firstElementChild(*outer); + Element* inner = ParentNode::firstElementChild(*inner_size); + + EXPECT_EQ(ContainerQueryEvaluator::FindContainer( + inner, ParseContainer("style(--foo: bar)")->Selector()), + inner); + EXPECT_EQ( + ContainerQueryEvaluator::FindContainer( + inner, + ParseContainer("(width > 100px) and style(--foo: bar)")->Selector()), + inner_size); + EXPECT_EQ(ContainerQueryEvaluator::FindContainer( + inner, ParseContainer("outer style(--foo: bar)")->Selector()), + outer); + EXPECT_EQ( + ContainerQueryEvaluator::FindContainer( + inner, ParseContainer("outer (width > 100px) and style(--foo: bar)") + ->Selector()), + outer_size); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_gradient_value.cc b/third_party/blink/renderer/core/css/css_gradient_value.cc index 23c2d843..72fbe5e 100644 --- a/third_party/blink/renderer/core/css/css_gradient_value.cc +++ b/third_party/blink/renderer/core/css/css_gradient_value.cc
@@ -265,19 +265,30 @@ } GradientStop new_stops[9]; - // Position the new color stops. + // Position the new color stops. These must be in the range + // [offset_left, offset_right], and in non-decreasing order, even in the + // face of floating-point rounding. if (left_dist > right_dist) { for (size_t y = 0; y < 7; ++y) - new_stops[y].offset = offset_left + left_dist * (7 + y) / 13; - new_stops[7].offset = offset + right_dist / 3; - new_stops[8].offset = offset + right_dist * 2 / 3; + new_stops[y].offset = offset_left + left_dist * ((7.0f + y) / 13.0f); + new_stops[7].offset = offset + right_dist * (1.0f / 3.0f); + new_stops[8].offset = offset + right_dist * (2.0f / 3.0f); } else { - new_stops[0].offset = offset_left + left_dist / 3; - new_stops[1].offset = offset_left + left_dist * 2 / 3; + new_stops[0].offset = offset_left + left_dist * (1.0f / 3.0f); + new_stops[1].offset = offset_left + left_dist * (2.0f / 3.0f); for (size_t y = 0; y < 7; ++y) - new_stops[y + 2].offset = offset + right_dist * y / 13; + new_stops[y + 2].offset = offset + right_dist * (y / 13.0f); } +#if DCHECK_IS_ON() + // Verify that offset_left <= x_0 <= x_1 <= ... <= x_8 <= offset_right. + DCHECK_GE(new_stops[0].offset, offset_left); + for (int i = 1; i < 8; ++i) { + DCHECK_GE(new_stops[i].offset, new_stops[i - 1].offset); + } + DCHECK_GE(offset_right, new_stops[8].offset); +#endif // DCHECK_IS_ON() + // calculate colors for the new color hints. // The color weighting for the new color stops will be // pointRelativeOffset^(ln(0.5)/ln(hintRelativeOffset)).
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc index 3fcc632b..76c93902d 100644 --- a/third_party/blink/renderer/core/css/element_rule_collector.cc +++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -81,13 +81,14 @@ } bool EvaluateAndAddContainerQueries( + const Element& matching_element, const ContainerQuery& container_query, const StyleRecalcContext& style_recalc_context, MatchResult& result) { for (const ContainerQuery* current = &container_query; current; current = current->Parent()) { - if (!ContainerQueryEvaluator::EvalAndAdd(style_recalc_context, *current, - result)) { + if (!ContainerQueryEvaluator::EvalAndAdd( + matching_element, style_recalc_context, *current, result)) { return false; } } @@ -415,7 +416,8 @@ // elements when they depend on the originating element. if (pseudo_style_request_.pseudo_id != kPseudoIdNone || result.dynamic_pseudo == kPseudoIdNone) { - if (!EvaluateAndAddContainerQueries(*container_query, + if (!EvaluateAndAddContainerQueries(context_.GetElement(), + *container_query, style_recalc_context_, result_)) { if (AffectsAnimations(rule_data)) result_.SetConditionallyAffectsAnimations();
diff --git a/third_party/blink/renderer/core/css/layout_upgrade.cc b/third_party/blink/renderer/core/css/layout_upgrade.cc index 0b635a6..217c50a6 100644 --- a/third_party/blink/renderer/core/css/layout_upgrade.cc +++ b/third_party/blink/renderer/core/css/layout_upgrade.cc
@@ -20,7 +20,9 @@ } bool ParentLayoutUpgrade::ShouldUpgrade() { - return document_.GetStyleEngine().HasViewportDependentMediaQueries() || + StyleEngine& style_engine = document_.GetStyleEngine(); + return style_engine.HasViewportDependentMediaQueries() || + style_engine.HasViewportDependentPropertyRegistrations() || NodeLayoutUpgrade(owner_).ShouldUpgrade(); }
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc index 87a5081..f5c33a5 100644 --- a/third_party/blink/renderer/core/css/media_query_exp.cc +++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -694,6 +694,14 @@ builder.Append(")"); } +MediaQueryExpNode::FeatureFlags MediaQueryFunctionExpNode::CollectFeatureFlags() + const { + FeatureFlags flags = MediaQueryUnaryExpNode::CollectFeatureFlags(); + if (name_ == AtomicString("style")) + flags |= kFeatureStyle; + return flags; +} + void MediaQueryNotExpNode::SerializeTo(StringBuilder& builder) const { builder.Append("not "); Operand().SerializeTo(builder);
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h index cb6cb3bb..0f6b131 100644 --- a/third_party/blink/renderer/core/css/media_query_exp.h +++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -316,6 +316,7 @@ kFeatureHeight = 1 << 3, kFeatureInlineSize = 1 << 4, kFeatureBlockSize = 1 << 5, + kFeatureStyle = 1 << 6, }; using FeatureFlags = unsigned; @@ -398,6 +399,7 @@ Type GetType() const override { return Type::kFunction; } void SerializeTo(StringBuilder&) const override; + FeatureFlags CollectFeatureFlags() const override; private: AtomicString name_;
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc index 4132424..662abec3 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -2123,9 +2123,18 @@ const CSSValue& StyleBuilderConverter::ConvertRegisteredPropertyInitialValue( const Document& document, const CSSValue& value) { + CSSToLengthConversionData::FontSizes font_sizes; + CSSToLengthConversionData::ViewportSize viewport_size( + document.GetLayoutView()); + CSSToLengthConversionData::ContainerSizes container_sizes; + CSSToLengthConversionData conversion_data( + /* style */ nullptr, WritingMode::kHorizontalTb, font_sizes, + viewport_size, container_sizes, + /* zoom */ 1.0f); + return ComputeRegisteredPropertyValue( - document, nullptr /* state */, CSSToLengthConversionData(), value, - document.BaseURL(), document.Encoding()); + document, nullptr /* state */, conversion_data, value, document.BaseURL(), + document.Encoding()); } const CSSValue& StyleBuilderConverter::ConvertRegisteredPropertyValue(
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc index e11dacb..61412fa 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1583,9 +1583,9 @@ Element* StyleResolver::FindContainerForElement( Element* element, const ContainerSelector& container_selector) { - auto context = StyleRecalcContext::FromAncestors(*element); - return ContainerQueryEvaluator::FindContainer(context.container, - container_selector); + DCHECK(element); + return ContainerQueryEvaluator::FindContainer( + element->ParentOrShadowHostElement(), container_selector); } RuleIndexList* StyleResolver::PseudoCSSRulesForElement(
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc index de18ddd..be8e581 100644 --- a/third_party/blink/renderer/core/css/selector_checker.cc +++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -947,6 +947,43 @@ return false; } +void AddElementIdentifierHashesInTraversalScopeAndSetAffectedByHasFlags( + CheckPseudoHasFastRejectFilter& fast_reject_filter, + Element& has_anchor_element, + CheckPseudoHasArgumentContext& argument_context, + bool update_affected_by_has_flags) { + for (CheckPseudoHasArgumentTraversalIterator iterator(has_anchor_element, + argument_context); + !iterator.AtEnd(); ++iterator) { + fast_reject_filter.AddElementIdentifierHashes(*iterator.CurrentElement()); + if (update_affected_by_has_flags) { + SetAffectedByHasFlagsForElementAtDepth( + argument_context, iterator.CurrentElement(), iterator.CurrentDepth()); + } + } +} + +void SetAllElementsInTraversalScopeAsChecked( + Element* has_anchor_element, + CheckPseudoHasArgumentContext& argument_context, + CheckPseudoHasCacheScope::Context& cache_scope_context) { + // Find last element and last depth of the argument traversal iterator. + Element* last_element = has_anchor_element; + int last_depth = 0; + if (argument_context.AdjacentDistanceLimit() > 0) + last_element = ElementTraversal::NextSibling(*last_element); + if (last_element) { + if (argument_context.DepthLimit() > 0) { + last_element = ElementTraversal::FirstChild(*last_element); + last_depth = 1; + } + } + if (!last_element) + return; + cache_scope_context.SetAllTraversedElementsAsChecked(last_element, + last_depth); +} + enum EarlyBreakOnHasArgumentChecking { kBreakEarlyAndReturnAsMatched, kBreakEarlyAndMoveToNextArgument, @@ -958,7 +995,7 @@ Element* has_anchor_element, CheckPseudoHasArgumentContext& argument_context, CheckPseudoHasCacheScope::Context& cache_scope_context, - bool update_affected_by_has_flags) { + bool& update_affected_by_has_flags) { if (!cache_scope_context.CacheAllowed()) return kNoEarlyBreak; @@ -980,6 +1017,48 @@ : kBreakEarlyAndMoveToNextArgument; } + // Check fast reject filter to reject :has() argument checking early. + + bool is_new_entry; + CheckPseudoHasFastRejectFilter& fast_reject_filter = + cache_scope_context.EnsureFastRejectFilter(has_anchor_element, + is_new_entry); + + // Filter is not actually created on the first check to avoid unnecessary + // filter creation overhead. If the :has() anchor element has the + // AffectedByMultipleHas flag set, use fast reject filter even if on the first + // check since there can be more checks on the anchor element. + if (is_new_entry && !has_anchor_element->AffectedByMultipleHas()) + return kNoEarlyBreak; + + // The bloom filter in the fast reject filter is allocated and initialized on + // the second check. We can check fast rejection with the filter after the + // allocation and initialization. + if (!fast_reject_filter.BloomFilterAllocated()) { + if (update_affected_by_has_flags) { + // Mark the :has() anchor element as affected by multiple :has() pseudo + // classes so that we can always use fast reject filter for the anchor + // element. + has_anchor_element->SetAffectedByMultipleHas(); + } + + fast_reject_filter.AllocateBloomFilter(); + AddElementIdentifierHashesInTraversalScopeAndSetAffectedByHasFlags( + fast_reject_filter, *has_anchor_element, argument_context, + update_affected_by_has_flags); + } + + // affected-by-has flags were already set while adding element identifier + // hashes (AddElementIdentifierHashesInTraversalScopeAndSetAffectedByHasFlags) + update_affected_by_has_flags = false; + + if (fast_reject_filter.FastReject( + argument_context.GetPseudoHasArgumentHashes())) { + SetAllElementsInTraversalScopeAsChecked( + has_anchor_element, argument_context, cache_scope_context); + return kBreakEarlyAndMoveToNextArgument; + } + return kNoEarlyBreak; }
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index 46ff36eb..c339b99 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -882,9 +882,19 @@ void StyleEngine::InvalidateViewportUnitStylesIfNeeded() { if (!viewport_unit_dirty_flags_) return; - SetNeedsStyleRecalcForViewportUnits(GetDocument(), - viewport_unit_dirty_flags_); - viewport_unit_dirty_flags_ = 0; + unsigned dirty_flags = 0; + std::swap(viewport_unit_dirty_flags_, dirty_flags); + + // If there are registered custom properties which depend on the invalidated + // viewport units, it can potentially affect every element. + if (initial_data_ && (initial_data_->GetViewportUnitFlags() & dirty_flags)) { + InvalidateInitialData(); + MarkAllElementsForStyleRecalc(StyleChangeReasonForTracing::Create( + style_change_reason::kViewportUnits)); + return; + } + + SetNeedsStyleRecalcForViewportUnits(GetDocument(), dirty_flags); } void StyleEngine::InvalidateStyleAndLayoutForFontUpdates() { @@ -1704,6 +1714,12 @@ } } +bool StyleEngine::HasViewportDependentPropertyRegistrations() { + UpdateActiveStyle(); + const PropertyRegistry* registry = GetDocument().GetPropertyRegistry(); + return registry && registry->GetViewportUnitFlags(); +} + void StyleEngine::ScheduleInvalidationsForRuleSets( TreeScope& tree_scope, const HeapHashSet<Member<RuleSet>>& rule_sets,
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h index aa871c3..d69d367f 100644 --- a/third_party/blink/renderer/core/css/style_engine.h +++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -307,6 +307,7 @@ return global_rule_set_->GetRuleFeatureSet() .HasViewportDependentMediaQueries(); } + bool HasViewportDependentPropertyRegistrations(); class InApplyAnimationUpdateScope { STACK_ALLOCATED();
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index f15da2c..b3621a2a 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -6143,6 +6143,14 @@ EnsureElementRareData().SetAffectedByLogicalCombinationsInHas(); } +bool Element::AffectedByMultipleHas() const { + return HasRareData() ? GetElementRareData()->AffectedByMultipleHas() : false; +} + +void Element::SetAffectedByMultipleHas() { + EnsureElementRareData().SetAffectedByMultipleHas(); +} + bool Element::UpdateForceLegacyLayout(const ComputedStyle& new_style, const ComputedStyle* old_style) { // ::first-letter may cause structure discrepancies between DOM and layout @@ -6561,6 +6569,16 @@ return nullptr; } +ContainerQueryEvaluator& Element::EnsureContainerQueryEvaluator() { + ContainerQueryData& data = EnsureElementRareData().EnsureContainerQueryData(); + ContainerQueryEvaluator* evaluator = data.GetContainerQueryEvaluator(); + if (!evaluator) { + evaluator = MakeGarbageCollected<ContainerQueryEvaluator>(); + data.SetContainerQueryEvaluator(evaluator); + } + return *evaluator; +} + bool Element::SkippedContainerStyleRecalc() const { if (!RuntimeEnabledFeatures::CSSContainerSkipStyleRecalcEnabled()) return false;
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 1be5ed6..76c77410 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -1138,6 +1138,7 @@ ContainerQueryData* GetContainerQueryData() const; ContainerQueryEvaluator* GetContainerQueryEvaluator() const; + ContainerQueryEvaluator& EnsureContainerQueryEvaluator(); bool SkippedContainerStyleRecalc() const; virtual void SetActive(bool active); @@ -1177,6 +1178,8 @@ void SetAncestorsOrSiblingsAffectedByFocusVisibleInHas(); bool AffectedByLogicalCombinationsInHas() const; void SetAffectedByLogicalCombinationsInHas(); + bool AffectedByMultipleHas() const; + void SetAffectedByMultipleHas(); void SaveIntrinsicSize(ResizeObserverSize* size); const ResizeObserverSize* LastIntrinsicSize() const;
diff --git a/third_party/blink/renderer/core/dom/element_rare_data.h b/third_party/blink/renderer/core/dom/element_rare_data.h index a00cf935..612ce90 100644 --- a/third_party/blink/renderer/core/dom/element_rare_data.h +++ b/third_party/blink/renderer/core/dom/element_rare_data.h
@@ -244,6 +244,12 @@ void SetAffectedByLogicalCombinationsInHas() { has_invalidation_flags_.affected_by_logical_combinations_in_has = true; } + bool AffectedByMultipleHas() const { + return has_invalidation_flags_.affected_by_multiple_has; + } + void SetAffectedByMultipleHas() { + has_invalidation_flags_.affected_by_multiple_has = true; + } void Trace(blink::Visitor*) const; @@ -514,6 +520,12 @@ void SetAffectedByLogicalCombinationsInHas() { EnsureSuperRareData().SetAffectedByLogicalCombinationsInHas(); } + bool AffectedByMultipleHas() const { + return super_rare_data_ ? super_rare_data_->AffectedByMultipleHas() : false; + } + void SetAffectedByMultipleHas() { + EnsureSuperRareData().SetAffectedByMultipleHas(); + } AccessibleNode* GetAccessibleNode() const { if (super_rare_data_)
diff --git a/third_party/blink/renderer/core/dom/has_invalidation_flags.h b/third_party/blink/renderer/core/dom/has_invalidation_flags.h index b29e4ce..26c29d6 100644 --- a/third_party/blink/renderer/core/dom/has_invalidation_flags.h +++ b/third_party/blink/renderer/core/dom/has_invalidation_flags.h
@@ -25,6 +25,21 @@ // change mutation, if an element doesn't have the flag set, the element // will not be invalidated or scheduled on even if the element has the // AffectedBySubjectHas or AffectedByNonSubjectHas flag set. +// - AffectedByMultipleHas +// Indicate that this element can be affected by multiple :has() pseudo +// classes. +// SelectorChecker uses CheckPseudoHasFastRejectFilter to preemtively +// skip non-matching :has() pseudo class checks only if there are +// multiple :has() to check on the same anchor element. SelectorChecker +// would not use the reject filter for a single :has() because it would +// have worse performance caused by the bloom filter memory allocation +// and the tree traversal for collecting element identifier hashes. +// To avoid the unnecessary overhead, bloom filter creation and element +// identifier hash collection are performed on the second check, and at +// this time AffectedByMultipleHas flag is set. +// This flag is used to determine whether SelectorChecker can use the +// reject filter even if on the first check since the flag indicates that +// there can be additional checks on the same anchor element. // // SelectorChecker::CheckPseudoClass() set the flags on an element when it // checks a :has() pseudo class on the element. @@ -189,6 +204,8 @@ unsigned ancestors_or_siblings_affected_by_focus_visible_in_has : 1; unsigned affected_by_logical_combinations_in_has : 1; + unsigned affected_by_multiple_has : 1; + HasInvalidationFlags() : affected_by_subject_has(false), affected_by_non_subject_has(false), @@ -198,7 +215,8 @@ ancestors_or_siblings_affected_by_hover_in_has(false), ancestors_or_siblings_affected_by_active_in_has(false), ancestors_or_siblings_affected_by_focus_in_has(false), - ancestors_or_siblings_affected_by_focus_visible_in_has(false) {} + ancestors_or_siblings_affected_by_focus_visible_in_has(false), + affected_by_multiple_has(false) {} }; } // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc index 66709078..a8756801 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -360,10 +360,8 @@ if (should_submit) { // If this form already made a request to navigate another frame which is // still pending, then we should cancel that one. - if (cancel_last_submission_ && - RuntimeEnabledFeatures::CancelFormSubmissionInDefaultHandlerEnabled()) { + if (cancel_last_submission_) std::move(cancel_last_submission_).Run(); - } ScheduleFormSubmission(event, submit_button); } }
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc index 31254cc0..3889d24 100644 --- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc +++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -53,6 +53,7 @@ #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/loader/frame_load_request.h" #include "third_party/blink/renderer/core/loader/frame_loader.h" +#include "third_party/blink/renderer/core/loader/url_matcher.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" @@ -231,43 +232,6 @@ return false; } -using AllowedListForLazyLoading = - WTF::Vector<std::pair<scoped_refptr<const SecurityOrigin>, WTF::String>>; - -// The format of params from Finch experiment is like below: -// "https://www.exampole.com(:443)|/embed,https://www.exampole.com|embed=true" -// Expecting the param is a comma separated string, and each string is -// separated by the vertical bar. The left side of the vertical bar is a -// host name, and the right side is a part of path or search params. -// Both are the indicators of html embeds. -AllowedListForLazyLoading -ParseFieldParamForAutomaticLazyFrameLoadingToEmbeds() { - AllowedListForLazyLoading allowed_list; - WTF::Vector<WTF::String> parsed_strings; - WTF::String( - base::GetFieldTrialParamValueByFeature( - features::kAutomaticLazyFrameLoadingToEmbedUrls, "allowed_websites") - .c_str()) - .Split(",", /*allow_empty_entries=*/false, parsed_strings); - WTF::Vector<WTF::String> site_info; - for (const auto& it : parsed_strings) { - it.Split("|", /*allow_empty_entries=*/false, site_info); - DCHECK_EQ(site_info.size(), 2u) - << "Got unexpected AutomaticLazyFrameLoadingToEmbeds entry: " << it; - - allowed_list.push_back(std::make_pair( - SecurityOrigin::CreateFromString(site_info[0]), site_info[1])); - } - - return allowed_list; -} - -const AllowedListForLazyLoading& AllowedWebsitesForLazyLoading() { - DEFINE_STATIC_LOCAL(AllowedListForLazyLoading, allowed_websites, - (ParseFieldParamForAutomaticLazyFrameLoadingToEmbeds())); - return allowed_websites; -} - // Checks if the passed url is the same origin with the document. // This is called in order to limit LazyEmbeds/Ads to apply only cross-origin // frames. @@ -296,24 +260,12 @@ return false; } - scoped_refptr<const SecurityOrigin> origin = SecurityOrigin::Create(url); - for (const auto& it : AllowedWebsitesForLazyLoading()) { - // TODO(sisidovski): IsSameOriginWith is more strict but we skip the port - // number check in order to avoid hardcoding port numbers to corresponding - // WPT test suites. To check port numbers, we need to set them to the - // allowlist which is passed by Chrome launch flag or Finch params. But, - // WPT server could have multiple ports, and it's difficult to expect which - // ports are available and set to the feature params before starting the - // test. That will affect the test reliability. - if ((origin.get()->Protocol() == it.first->Protocol() && - origin.get()->Host() == it.first->Host()) && - (url.GetPath().Contains(it.second) || - url.Query().Contains(it.second))) { - return true; - } - } + DEFINE_STATIC_LOCAL(UrlMatcher, url_matcher, + (UrlMatcher(base::GetFieldTrialParamValueByFeature( + features::kAutomaticLazyFrameLoadingToEmbedUrls, + "allowed_websites")))); - return false; + return url_matcher.Match(url); } const base::TimeDelta GetLazyEmbedsTimeoutMs() {
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni index 5d4205d..5956d7925 100644 --- a/third_party/blink/renderer/core/layout/build.gni +++ b/third_party/blink/renderer/core/layout/build.gni
@@ -573,6 +573,7 @@ "ng/ng_length_utils.cc", "ng/ng_length_utils.h", "ng/ng_link.h", + "ng/ng_logical_link.h", "ng/ng_out_of_flow_layout_part.cc", "ng/ng_out_of_flow_layout_part.h", "ng/ng_out_of_flow_positioned_node.cc",
diff --git a/third_party/blink/renderer/core/layout/geometry/logical_rect.h b/third_party/blink/renderer/core/layout/geometry/logical_rect.h index f6c95d07..e7b10ad 100644 --- a/third_party/blink/renderer/core/layout/geometry/logical_rect.h +++ b/third_party/blink/renderer/core/layout/geometry/logical_rect.h
@@ -66,7 +66,7 @@ return other.offset == offset && other.size == size; } - LogicalRect operator+(const LogicalOffset&) const { + LogicalRect operator+(const LogicalOffset& offset) const { return {this->offset + offset, size}; }
diff --git a/third_party/blink/renderer/core/layout/geometry/logical_rect_test.cc b/third_party/blink/renderer/core/layout/geometry/logical_rect_test.cc index e82fdd6..646b56e 100644 --- a/third_party/blink/renderer/core/layout/geometry/logical_rect_test.cc +++ b/third_party/blink/renderer/core/layout/geometry/logical_rect_test.cc
@@ -11,6 +11,11 @@ namespace { +TEST(LogicalRectTest, AddOffset) { + EXPECT_EQ(LogicalRect(1, 2, 3, 4) + LogicalOffset(5, 6), + LogicalRect(6, 8, 3, 4)); +} + struct LogicalRectUniteTestData { const char* test_case; LogicalRect a;
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc index fe9c4658..c684ea10 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -349,6 +349,19 @@ DoesItemStretch(child); } +NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicInlineSize( + const NGBlockNode& child) const { + NGMinMaxConstraintSpaceBuilder builder(ConstraintSpace(), Style(), child, + /* is_new_fc */ true); + builder.SetAvailableBlockSize(ChildAvailableSize().block_size); + builder.SetPercentageResolutionBlockSize(child_percentage_size_.block_size); + builder.SetReplacedPercentageResolutionBlockSize( + child_percentage_size_.block_size); + if (!is_column_ && WillChildCrossSizeBeContainerCrossSize(child)) + builder.SetBlockAutoBehavior(NGAutoBehavior::kStretchExplicit); + return builder.ToConstraintSpace(); +} + NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicBlockSize( const NGBlockNode& flex_item) const { const ComputedStyle& child_style = flex_item.Style(); @@ -1863,7 +1876,7 @@ return; } DCHECK_EQ(children.size(), 1u); - const NGContainerFragmentBuilder::ChildWithOffset& child = children[0]; + const NGLogicalLink& child = children[0]; DCHECK(!child.fragment->IsLineBox()); const NGConstraintSpace& space = ConstraintSpace(); NGBoxFragment fragment(space.GetWritingDirection(), @@ -2129,16 +2142,8 @@ FlexFractionParts max_content_largest_fraction(Style()); for (const FlexItem& item : algorithm_.all_items_) { const NGBlockNode& child = item.ng_input_node_; - NGMinMaxConstraintSpaceBuilder builder(ConstraintSpace(), Style(), child, - /* is_new_fc */ true); - builder.SetAvailableBlockSize(ChildAvailableSize().block_size); - builder.SetPercentageResolutionBlockSize(child_percentage_size_.block_size); - builder.SetReplacedPercentageResolutionBlockSize( - child_percentage_size_.block_size); - if (!is_column_ && WillChildCrossSizeBeContainerCrossSize(child)) - builder.SetBlockAutoBehavior(NGAutoBehavior::kStretchExplicit); - const NGConstraintSpace space = builder.ToConstraintSpace(); + const NGConstraintSpace space = BuildSpaceForIntrinsicInlineSize(child); const MinMaxSizesResult min_max_content_contributions = ComputeItemContributions(space, item); depends_on_block_constraints |= @@ -2157,6 +2162,54 @@ } MinMaxSizesResult +NGFlexLayoutAlgorithm::ComputeMinMaxSizeOfMultilineColumnContainer() { + NGFlexChildIterator iterator(Node()); + MinMaxSizes largest_inline_size_contributions; + for (NGBlockNode child = iterator.NextChild(); child; + child = iterator.NextChild()) { + if (child.IsOutOfFlowPositioned()) + continue; + + auto space = BuildSpaceForIntrinsicInlineSize(child); + MinMaxSizesResult child_contributions = + ComputeMinAndMaxContentContribution(Style(), child, space); + NGBoxStrut child_margins = + ComputeMarginsFor(space, child.Style(), ConstraintSpace()); + child_contributions.sizes += child_margins.InlineSum(); + + largest_inline_size_contributions.Encompass(child_contributions.sizes); + } + + // The algorithm for determining the max-content width of a column-wrap + // container is simply: Run layout on the container but give the items an + // overridden available size, equal to the largest max-content width of any + // item, when they are laid out. The container's max-content width is then + // the farthest outer inline-end point of all the items. + item_inline_available_size_override_ = + largest_inline_size_contributions.max_size; + HeapVector<NGFlexLine> flex_line_outputs; + HeapVector<Member<LayoutBox>> dummy_oof_children; + PlaceFlexItems(&flex_line_outputs, &dummy_oof_children); + if (!flex_line_outputs.IsEmpty()) { + largest_inline_size_contributions.max_size = + flex_line_outputs.back().line_cross_size + + flex_line_outputs.back().cross_axis_offset - + flex_line_outputs.front().cross_axis_offset; + } + + DCHECK_GE(largest_inline_size_contributions.min_size, 0); + DCHECK_LE(largest_inline_size_contributions.min_size, + largest_inline_size_contributions.max_size); + + largest_inline_size_contributions += BorderScrollbarPadding().InlineSum(); + + // This always depends on block constraints because if block constraints + // change, this flexbox could get a different number of columns. + return {largest_inline_size_contributions, + /* depends_on_block_constraints */ true}; +} + +MinMaxSizesResult NGFlexLayoutAlgorithm::ComputeMinMaxSizeOfSingleLineRowContainer() { ConstructAndAppendFlexItems(/*is_computing_intrinsic_size*/ true); MinMaxSizes container_sizes; @@ -2220,7 +2273,7 @@ // TODO(crbug.com/240765): Implement all the cases here. if (is_column_) { if (algorithm_.IsMultiline()) { - // multiline column flexbox + return ComputeMinMaxSizeOfMultilineColumnContainer(); } else { // singleline column flexbox } @@ -2242,16 +2295,7 @@ continue; number_of_items++; - NGMinMaxConstraintSpaceBuilder builder(ConstraintSpace(), Style(), child, - /* is_new_fc */ true); - builder.SetAvailableBlockSize(ChildAvailableSize().block_size); - builder.SetPercentageResolutionBlockSize(child_percentage_size_.block_size); - builder.SetReplacedPercentageResolutionBlockSize( - child_percentage_size_.block_size); - if (!is_column_ && WillChildCrossSizeBeContainerCrossSize(child)) - builder.SetBlockAutoBehavior(NGAutoBehavior::kStretchExplicit); - const auto space = builder.ToConstraintSpace(); - + const NGConstraintSpace space = BuildSpaceForIntrinsicInlineSize(child); MinMaxSizesResult child_result = ComputeMinAndMaxContentContribution(Style(), child, space); NGBoxStrut child_margins = @@ -2550,4 +2594,13 @@ } #endif +LogicalSize NGFlexLayoutAlgorithm::ChildAvailableSize() const { + LogicalSize available_size = NGLayoutAlgorithm::ChildAvailableSize(); + if (UNLIKELY(item_inline_available_size_override_.has_value())) { + DCHECK_EQ(container_builder_.InlineSize(), kIndefiniteSize); + available_size.inline_size = *item_inline_available_size_override_; + } + return available_size; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h index abc4629..7bd621f 100644 --- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -63,6 +63,8 @@ bool IsColumnContainerMainSizeDefinite() const; bool IsContainerCrossSizeDefinite() const; + NGConstraintSpace BuildSpaceForIntrinsicInlineSize( + const NGBlockNode& flex_item) const; NGConstraintSpace BuildSpaceForFlexBasis(const NGBlockNode& flex_item) const; NGConstraintSpace BuildSpaceForIntrinsicBlockSize( const NGBlockNode& flex_item) const; @@ -116,7 +118,9 @@ NGFlexLayoutAlgorithm::FlexFractionParts, bool> FindLargestFractions() const; + MinMaxSizesResult ComputeMinMaxSizeOfSingleLineRowContainer(); + MinMaxSizesResult ComputeMinMaxSizeOfMultilineColumnContainer(); // This implements 9.9.3. Flex Item Intrinsic Size Contributions, from // https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions. MinMaxSizesResult ComputeItemContributions(const NGConstraintSpace& space, @@ -183,6 +187,13 @@ void CheckFlexLines(HeapVector<NGFlexLine>& flex_line_outputs) const; #endif + // These are used when determining the max-content width of a column-wrap flex + // container. Note: |item_inline_available_size_override_| has to be + // initialized before is_cross_size_definite_, and probably before some other + // data members too. + LogicalSize ChildAvailableSize() const; + absl::optional<LayoutUnit> item_inline_available_size_override_; + const bool is_column_; const bool is_horizontal_flow_; const bool is_cross_size_definite_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc index 1279ab3..266ed62 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -302,7 +302,7 @@ DCHECK(object.Parent()->IsFlexibleBox()); DCHECK(object.Parent()->IsOutOfFlowPositioned()); for (wtf_size_t idx = 0; idx < children_.size(); idx++) { - const ChildWithOffset& child = children_[idx]; + const NGLogicalLink& child = children_[idx]; if (child.fragment->GetLayoutObject() == &object) { children_.EraseAt(idx); return; @@ -502,7 +502,7 @@ WritingMode block_or_line_writing_mode) { #if DCHECK_IS_ON() if (ItemsBuilder()) { - for (const ChildWithOffset& child : Children()) { + for (const NGLogicalLink& child : Children()) { DCHECK(child.fragment); const NGPhysicalFragment& fragment = *child.fragment; DCHECK(fragment.IsLineBox() ||
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc index c65cbb6..905aa2d 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -845,8 +845,8 @@ NGOutOfFlowLayoutPart::ColumnBalancingInfo column_balancing_info; for (wtf_size_t i = 0; i < new_columns.size(); i++) { auto& new_column = new_columns[i]; - column_balancing_info.columns.emplace_back(new_column.offset, - &new_column.Fragment()); + column_balancing_info.columns.push_back( + NGLogicalLink{&new_column.Fragment(), new_column.offset}); // Because the current set of columns haven't been added to the builder // yet, any OOF descendants won't have been propagated up yet. Instead,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc index 2a2df24..3378991 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -26,17 +26,12 @@ } // namespace -void NGContainerFragmentBuilder::ChildWithOffset::Trace( - Visitor* visitor) const { - visitor->Trace(fragment); -} - void NGContainerFragmentBuilder::ReplaceChild( wtf_size_t index, const NGPhysicalFragment& new_child, const LogicalOffset offset) { DCHECK_LT(index, children_.size()); - children_[index] = ChildWithOffset(offset, std::move(&new_child)); + children_[index] = NGLogicalLink{std::move(&new_child), offset}; } // Propagate data in |child| to this fragment. The |child| will then be added as @@ -178,7 +173,7 @@ // In order to know where list-markers are within the children list (for the // |NGSimplifiedLayoutAlgorithm|) we always place them as the first child. if (child->IsListMarker()) { - children_.push_front(ChildWithOffset(child_offset, std::move(child))); + children_.push_front(NGLogicalLink{std::move(child), child_offset}); return; } @@ -187,13 +182,12 @@ // ::placeholder earlier. const wtf_size_t size = children_.size(); if (size > 0) { - children_.insert(size - 1, - ChildWithOffset(child_offset, std::move(child))); + children_.insert(size - 1, NGLogicalLink{std::move(child), child_offset}); return; } } - children_.emplace_back(child_offset, std::move(child)); + children_.push_back(NGLogicalLink{std::move(child), child_offset}); } void NGContainerFragmentBuilder::AddOutOfFlowChildCandidate(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h index 9cd0334..b5acd33 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -21,6 +21,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_link.h" +#include "third_party/blink/renderer/core/layout/ng/ng_logical_link.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" @@ -48,20 +49,7 @@ child_break_tokens_.clear(); } - struct ChildWithOffset { - DISALLOW_NEW(); - ChildWithOffset(LogicalOffset offset, const NGPhysicalFragment* fragment) - : offset(offset), fragment(std::move(fragment)) {} - - void Trace(Visitor*) const; - - // We store logical offsets (instead of the final physical), as we can't - // convert into the physical coordinate space until we know our final size. - LogicalOffset offset; - Member<const NGPhysicalFragment> fragment; - }; - - using ChildrenVector = HeapVector<ChildWithOffset, 4>; + using ChildrenVector = HeapVector<NGLogicalLink, 4>; using MulticolCollection = HeapHashMap<Member<LayoutBox>, Member<NGMulticolWithPendingOOFs<LogicalOffset>>>; @@ -476,7 +464,4 @@ } // namespace blink -WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( - blink::NGContainerFragmentBuilder::ChildWithOffset) - #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_CONTAINER_FRAGMENT_BUILDER_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc index 0487048..edd83e1 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -1471,7 +1471,9 @@ LayoutUnit default_block_size = CalculateDefaultBlockSize( constraint_space, node, break_token, border_scrollbar_padding); absl::optional<LayoutUnit> inline_size; - if (!is_intrinsic) { + if (!is_intrinsic && + (!InlineLengthUnresolvable(constraint_space, style.LogicalWidth()) || + constraint_space.IsFixedInlineSize())) { inline_size = ComputeInlineSizeForFragment(constraint_space, node, border_padding);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_link.h b/third_party/blink/renderer/core/layout/ng/ng_link.h index ac108e0d..1062345e 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_link.h +++ b/third_party/blink/renderer/core/layout/ng/ng_link.h
@@ -26,7 +26,7 @@ PhysicalOffset Offset() const { return offset; } const NGPhysicalFragment* get() const { return fragment; } - operator bool() const { return fragment; } + explicit operator bool() const { return fragment; } const NGPhysicalFragment& operator*() const { return *fragment; } const NGPhysicalFragment* operator->() const { return fragment; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_logical_link.h b/third_party/blink/renderer/core/layout/ng/ng_logical_link.h new file mode 100644 index 0000000..3f0812c --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/ng_logical_link.h
@@ -0,0 +1,37 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LOGICAL_LINK_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LOGICAL_LINK_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h" + +namespace blink { + +class NGPhysicalFragment; + +// Similar to |NGLink| but with |LogicalOffset| instead of |PhysicalOffset|. +struct CORE_EXPORT NGLogicalLink { + DISALLOW_NEW(); + + public: + const LogicalOffset& Offset() const { return offset; } + const NGPhysicalFragment* get() const { return fragment; } + + explicit operator bool() const { return fragment; } + const NGPhysicalFragment& operator*() const { return *fragment; } + const NGPhysicalFragment* operator->() const { return fragment; } + + void Trace(Visitor* visitor) const { visitor->Trace(fragment); } + + Member<const NGPhysicalFragment> fragment; + LogicalOffset offset; +}; + +} // namespace blink + +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGLogicalLink) + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LOGICAL_LINK_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc index b00e30c..1fc26ec0 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -1042,7 +1042,7 @@ LayoutUnit additional_column_block_size; // Then append the new fragmentainers. for (wtf_size_t i = old_fragment_count; i < new_fragment_count; i++) { - NGContainerFragmentBuilder::ChildWithOffset child = + const NGLogicalLink& child = limited_multicol_container_builder.Children()[i]; algorithm.AppendNewChildFragment(*child.fragment, child.offset); additional_column_block_size += @@ -2160,7 +2160,7 @@ // We're currently laying out |containing_block|, and it's a multicol // container. Search inside fragmentainer children in the builder. auto& children = FragmentationContextChildren(); - for (const NGContainerFragmentBuilder::ChildWithOffset& child : children) { + for (const NGLogicalLink& child : children) { if (ReplaceFragmentainerChild(*child.fragment)) return; }
diff --git a/third_party/blink/renderer/core/loader/build.gni b/third_party/blink/renderer/core/loader/build.gni index 1d056ee5..208e3c0 100644 --- a/third_party/blink/renderer/core/loader/build.gni +++ b/third_party/blink/renderer/core/loader/build.gni
@@ -140,6 +140,8 @@ "threadable_loader_client.h", "threaded_icon_loader.cc", "threaded_icon_loader.h", + "url_matcher.cc", + "url_matcher.h", "web_associated_url_loader_impl.cc", "web_associated_url_loader_impl.h", "web_bundle/script_web_bundle.cc", @@ -196,6 +198,7 @@ "resource_load_observer_for_frame_test.cc", "threadable_loader_test.cc", "threaded_icon_loader_test.cc", + "url_matcher_test.cc", "web_associated_url_loader_impl_test.cc", "web_bundle/script_web_bundle_rule_test.cc", ]
diff --git a/third_party/blink/renderer/core/loader/url_matcher.cc b/third_party/blink/renderer/core/loader/url_matcher.cc new file mode 100644 index 0000000..131de95 --- /dev/null +++ b/third_party/blink/renderer/core/loader/url_matcher.cc
@@ -0,0 +1,60 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/loader/url_matcher.h" + +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" + +namespace blink { + +UrlMatcher::UrlMatcher(const base::StringPiece& encoded_url_list_string) { + ParseFieldTrialParam(encoded_url_list_string); +} + +UrlMatcher::~UrlMatcher() = default; + +bool UrlMatcher::Match(const KURL& url) const { + scoped_refptr<const SecurityOrigin> origin = SecurityOrigin::Create(url); + for (const auto& it : url_list_) { + // TODO(sisidovski): IsSameOriginWith is more strict but we skip the port + // number check in order to avoid hardcoding port numbers to corresponding + // WPT test suites. To check port numbers, we need to set them to the + // allowlist which is passed by Chrome launch flag or Finch params. But, + // WPT server could have multiple ports, and it's difficult to expect which + // ports are available and set to the feature params before starting the + // test. That will affect the test reliability. + if ((origin.get()->Protocol() == it.first->Protocol() && + origin.get()->Host() == it.first->Host())) { + // AllowList could only have domain info. In that case the matcher neither + // cares path nor query strings. + if (!it.second.has_value()) + return true; + // Otherwise check if the path or query contains the string. + if (url.GetPath().Contains(it.second.value()) || + url.Query().Contains(it.second.value())) + return true; + } + } + + return false; +} + +void UrlMatcher::ParseFieldTrialParam( + const base::StringPiece& encoded_url_list_string) { + Vector<String> parsed_strings; + String::FromUTF8(encoded_url_list_string) + .Split(",", /*allow_empty_entries=*/false, parsed_strings); + Vector<String> site_info; + for (const auto& it : parsed_strings) { + it.Split("|", /*allow_empty_entries=*/false, site_info); + DCHECK_LE(site_info.size(), 2u) + << "Got unexpected format that UrlMatcher cannot handle: " << it; + absl::optional<String> match_string; + if (site_info.size() == 2u) + match_string = site_info[1]; + url_list_.push_back(std::make_pair( + SecurityOrigin::CreateFromString(site_info[0]), match_string)); + } +} +} // namespace blink
diff --git a/third_party/blink/renderer/core/loader/url_matcher.h b/third_party/blink/renderer/core/loader/url_matcher.h new file mode 100644 index 0000000..4a1d3026 --- /dev/null +++ b/third_party/blink/renderer/core/loader/url_matcher.h
@@ -0,0 +1,53 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_URL_MATCHER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_URL_MATCHER_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// UrlMatcher is a class to manage the list of URLs stored in the field trial +// param. As the original data from the field trial params is delivered as a +// special format string, this class parses and formats it, and stores the list. +// +// The expected param format is a comma separated string, and each string is +// separated by the vertical bar. The left side of the vertical bar is a +// host name, and the right side is a part of path or search params. +// +// The string is something like +// "https://test.exmaple|/foo,http://another.test.example|?foo=bar,https:://yet.another.test.example" +// Then the UrlMatcher will parse it to the formatted list like +// [ +// ["https://test.example", "/foo"], +// ["http://another.test.example", "foo=bar"], +// ["https:://yet.another.test.example", ""] +// ] +// Based on the above list, UrlMatcher::Match() checks 1) if the given url is a +// same origin or not, 2) if it's a same origin, check the second value in the +// list item. If it's an empty string, that means origin-level url matching. If +// it has a string, check the path string and query string in the given url +// contain it or not. +class CORE_EXPORT UrlMatcher final { + public: + explicit UrlMatcher(const base::StringPiece& encoded_url_list_string); + ~UrlMatcher(); + + bool Match(const KURL& url) const; + + private: + using UrlList = Vector< + std::pair<scoped_refptr<const SecurityOrigin>, absl::optional<String>>>; + UrlList url_list_; + + void ParseFieldTrialParam(const base::StringPiece& encoded_url_list_string); +}; +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_URL_MATCHER_H_
diff --git a/third_party/blink/renderer/core/loader/url_matcher_test.cc b/third_party/blink/renderer/core/loader/url_matcher_test.cc new file mode 100644 index 0000000..bb0fed8 --- /dev/null +++ b/third_party/blink/renderer/core/loader/url_matcher_test.cc
@@ -0,0 +1,38 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/loader/url_matcher.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +TEST(UrlMatcherTest, SingleDomain) { + UrlMatcher matcher("https://test.com"); + EXPECT_TRUE(matcher.Match(KURL("https://test.com/script.js"))); + EXPECT_FALSE(matcher.Match(KURL("http://test.com/script.js"))); + EXPECT_FALSE(matcher.Match(KURL("http://another.test.com/script.js"))); +} + +TEST(UrlMatcherTest, MultipleDomains) { + UrlMatcher matcher("https://test.com,https://another.test.com"); + KURL url = KURL("https://test.com/script.js"); + EXPECT_TRUE(matcher.Match(url)); +} + +TEST(UrlMatcherTest, WithSeparatorForPathStrings) { + UrlMatcher matcher("https://test.com|/foo"); + EXPECT_TRUE(matcher.Match(KURL("https://test.com/foo"))); + EXPECT_FALSE(matcher.Match(KURL("https://test.com/bar"))); + EXPECT_FALSE(matcher.Match(KURL("https://test.com?foo"))); +} + +TEST(UrlMatcherTest, WithSeparatorForQueryParams) { + UrlMatcher matcher("https://test.com|foo=bar"); + EXPECT_FALSE(matcher.Match(KURL("https://test.com/foo"))); + EXPECT_FALSE(matcher.Match(KURL("https://test.com/foo/bar"))); + EXPECT_TRUE(matcher.Match(KURL("https://test.com?foo=bar"))); + EXPECT_TRUE(matcher.Match(KURL("https://test.com?a=b&foo=bar"))); +} +} // namespace blink
diff --git a/third_party/blink/renderer/core/streams/readable_stream.cc b/third_party/blink/renderer/core/streams/readable_stream.cc index 091f561..91f1b5eb 100644 --- a/third_party/blink/renderer/core/streams/readable_stream.cc +++ b/third_party/blink/renderer/core/streams/readable_stream.cc
@@ -1324,17 +1324,20 @@ // static ReadableStream* ReadableStream::CreateByteStream( ScriptState* script_state, - UnderlyingByteSourceBase* underlying_byte_source, - ExceptionState& exception_state) { + UnderlyingByteSourceBase* underlying_byte_source) { auto* pull_algorithm = MakeGarbageCollected<PullAlgorithm>(underlying_byte_source); auto* cancel_algorithm = MakeGarbageCollected<CancelAlgorithm>(underlying_byte_source); + + // Construction of the byte stream cannot fail because the trivial start + // algorithm will not throw. + NonThrowableExceptionState exception_state; auto* stream = CreateByteStream(script_state, CreateTrivialStartAlgorithm(), pull_algorithm, cancel_algorithm, exception_state); DCHECK(stream); - DCHECK(!exception_state.HadException()); + ReadableStreamController* controller = stream->readable_stream_controller_; pull_algorithm->SetController(To<ReadableByteStreamController>(controller)); return stream;
diff --git a/third_party/blink/renderer/core/streams/readable_stream.h b/third_party/blink/renderer/core/streams/readable_stream.h index e70125e..a3fb5b5 100644 --- a/third_party/blink/renderer/core/streams/readable_stream.h +++ b/third_party/blink/renderer/core/streams/readable_stream.h
@@ -116,8 +116,7 @@ // Entry point to create a ReadableByteStream from other C++ APIs. static ReadableStream* CreateByteStream( ScriptState*, - UnderlyingByteSourceBase* underlying_byte_source, - ExceptionState&); + UnderlyingByteSourceBase* underlying_byte_source); // CreateReadableByteStream(): // https://streams.spec.whatwg.org/#abstract-opdef-createreadablebytestream
diff --git a/third_party/blink/renderer/core/streams/readable_stream_test.cc b/third_party/blink/renderer/core/streams/readable_stream_test.cc index 9744aef2..427ecce 100644 --- a/third_party/blink/renderer/core/streams/readable_stream_test.cc +++ b/third_party/blink/renderer/core/streams/readable_stream_test.cc
@@ -595,10 +595,9 @@ ReadableStream* Stream() const { return stream_; } void Init(ScriptState* script_state, - UnderlyingByteSourceBase* underlying_byte_source, - ExceptionState& exception_state) { - stream_ = ReadableStream::CreateByteStream( - script_state, underlying_byte_source, exception_state); + UnderlyingByteSourceBase* underlying_byte_source) { + stream_ = + ReadableStream::CreateByteStream(script_state, underlying_byte_source); } // This takes the |stream| property of ReadableStream and copies it onto the @@ -684,8 +683,7 @@ TEST_F(ReadableByteStreamTest, Construct) { V8TestingScope scope; Init(scope.GetScriptState(), - MakeGarbageCollected<TestUnderlyingByteSource>(scope.GetScriptState()), - ASSERT_NO_EXCEPTION); + MakeGarbageCollected<TestUnderlyingByteSource>(scope.GetScriptState())); EXPECT_TRUE(Stream()); } @@ -693,7 +691,7 @@ V8TestingScope scope; auto* mock = MakeGarbageCollected<MockUnderlyingByteSource>(scope.GetScriptState()); - Init(scope.GetScriptState(), mock, ASSERT_NO_EXCEPTION); + Init(scope.GetScriptState(), mock); // Need to run microtasks so the startAlgorithm promise resolves. v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate()); CopyStreamToGlobal(scope); @@ -713,7 +711,7 @@ V8TestingScope scope; auto* mock = MakeGarbageCollected<MockUnderlyingByteSource>(scope.GetScriptState()); - Init(scope.GetScriptState(), mock, ASSERT_NO_EXCEPTION); + Init(scope.GetScriptState(), mock); // Need to run microtasks so the startAlgorithm promise resolves. v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate()); CopyStreamToGlobal(scope); @@ -770,8 +768,7 @@ V8TestingScope scope; auto* script_state = scope.GetScriptState(); Init(script_state, - MakeGarbageCollected<ThrowFromPullUnderlyingByteSource>(script_state), - ASSERT_NO_EXCEPTION); + MakeGarbageCollected<ThrowFromPullUnderlyingByteSource>(script_state)); auto* reader = Stream()->GetBYOBReaderForTesting(script_state, ASSERT_NO_EXCEPTION); @@ -801,8 +798,7 @@ V8TestingScope scope; auto* script_state = scope.GetScriptState(); Init(script_state, - MakeGarbageCollected<ThrowFromCancelUnderlyingByteSource>(script_state), - ASSERT_NO_EXCEPTION); + MakeGarbageCollected<ThrowFromCancelUnderlyingByteSource>(script_state)); auto* reader = Stream()->GetBYOBReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
diff --git a/third_party/blink/renderer/core/style/style_initial_data.cc b/third_party/blink/renderer/core/style/style_initial_data.cc index 96abc76..077b3d4 100644 --- a/third_party/blink/renderer/core/style/style_initial_data.cc +++ b/third_party/blink/renderer/core/style/style_initial_data.cc
@@ -26,6 +26,9 @@ variables_.SetData(entry.key, std::move(computed_initial_data)); variables_.SetValue(entry.key, computed_initial_value); } + + viewport_unit_flags_ = registry.GetViewportUnitFlags(); + document.AddViewportUnitFlags(viewport_unit_flags_); } bool StyleInitialData::operator==(const StyleInitialData& other) const {
diff --git a/third_party/blink/renderer/core/style/style_initial_data.h b/third_party/blink/renderer/core/style/style_initial_data.h index fcbd9c37..664d3134 100644 --- a/third_party/blink/renderer/core/style/style_initial_data.h +++ b/third_party/blink/renderer/core/style/style_initial_data.h
@@ -49,6 +49,8 @@ return variables_.GetValue(name).value_or(nullptr); } + unsigned GetViewportUnitFlags() const { return viewport_unit_flags_; } + private: StyleInitialData(Document&, const PropertyRegistry&); @@ -56,6 +58,19 @@ // the initial style, and then shared with all other styles that directly or // indirectly inherit from that. StyleVariables variables_; + // This is equal to `PropertyRegistry::GetViewportUnitFlags()` at the time the + // `StyleInitialData` was created. + // + // Since StyleInitialData is only (re)created during style resolution, this + // tells us whether ComputedStyles from that process depend on viewport units + // or not, which in turns tells us if we need to recalculate any styles when + // we resize. + // + // PropertyRegistry::GetViewportUnitFlags on the other hand, can change + // immediately via JavaScript, and is also affected by active style updates. + // Hence this is not useful for understanding whether or not any current + // ComputedStyles need to be invalidated by a resize. + unsigned viewport_unit_flags_ = 0; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 61ea6e5..d57636d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -4587,8 +4587,10 @@ // objects may have been deferred by display-locking. Document* document = GetDocument(); Node* node = GetNode(); - if (document && node) - document->UpdateStyleAndLayoutTreeForNode(node); + if (!document || !node) + return false; + + document->UpdateStyleAndLayoutTreeForNode(node); if (!CanSetFocusAttribute()) return false; @@ -5378,7 +5380,8 @@ const AXObject* datetime_ancestor = DatetimeAncestor(); ax::mojom::blink::NameFrom datetime_ancestor_name_from; datetime_ancestor->GetName(datetime_ancestor_name_from, nullptr); - description_objects->clear(); + if (description_objects) + description_objects->clear(); String ancestor_description = DatetimeAncestor()->Description( datetime_ancestor_name_from, description_from, description_objects); if (!result.IsEmpty() && !ancestor_description.IsEmpty())
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index e2ca1e9..b48987fe 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -509,7 +509,7 @@ uint32_t max_len = kMaxStringAttributeLength) { if (value.IsEmpty()) return; - std::string value_utf8 = value.Utf8(); + std::string value_utf8 = value.Utf8(kStrictUTF8Conversion); if (value_utf8.size() > max_len) { std::string truncated; base::TruncateUTF8ToByteSize(value_utf8, max_len, &truncated); @@ -2960,6 +2960,9 @@ while (object && !object->IsAXNodeObject()) object = object->ParentObject(); + + DCHECK(object); + Node* node = object->GetNode(); auto* element = DynamicTo<Element>(node); if (!element)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index b664207..c764bc70 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -931,7 +931,7 @@ // Layout object is irrelevant, but node object can still be relevant. if (!node_id) { DCHECK(layout_id); // One of of node_id, layout_id is non-zero. - Invalidate(layout_object->GetDocument(), layout_id); + Invalidate(node->GetDocument(), layout_id); } else { layout_object = nullptr; layout_id = 0; @@ -943,7 +943,7 @@ // Change from AXLayoutObject -> AXNodeObject. // The node is in a display locked subtree, but we've previously put it in // the cache with its layout object. - Invalidate(layout_object->GetDocument(), layout_id); + Invalidate(node->GetDocument(), layout_id); } else if (layout_object && node_id && !layout_id && !IsDisplayLocked(node)) { // Change from AXNodeObject -> AXLayoutObject. // Has a layout object but no layout_id, meaning that when the AXObject was
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc index 42fb8106..d1d098a8 100644 --- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc +++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
@@ -954,7 +954,7 @@ message.Utf8().c_str(), kind().Utf8().c_str(), id().Utf8().c_str(), label().Utf8().c_str(), enabled() ? "true" : "false", muted() ? "true" : "false", readyState().Utf8().c_str(), - component_->Source()->Remote() ? "true" : "false") + component_->Remote() ? "true" : "false") .Utf8()); }
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc index efe69f0..146613e 100644 --- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc +++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms_compositor.cc
@@ -214,7 +214,7 @@ video_components = media_stream_descriptor->VideoComponents(); const bool remote_video = - video_components.size() && video_components[0]->Source()->Remote(); + video_components.size() && video_components[0]->Remote(); if (remote_video && Platform::Current()->RTCSmoothnessAlgorithmEnabled()) { base::AutoLock auto_lock(current_frame_lock_);
diff --git a/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc index f39f776f..82db5bf7 100644 --- a/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc +++ b/third_party/blink/renderer/platform/heap/test/incremental_marking_test.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/test/scoped_feature_list.h" +#include "build/buildflag.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" @@ -1338,7 +1339,14 @@ size_t DestructedAndTraced::n_destructed = 0; size_t DestructedAndTraced::n_traced = 0; -TEST_F(IncrementalMarkingTest, ConservativeGCOfWeakContainer) { +// Flaky <https://crbug.com/1351511>. +#if BUILDFLAG(IS_LINUX) +#define MAYBE_ConservativeGCOfWeakContainer \ + DISABLED_ConservativeGCOfWeakContainer +#else +#define MAYBE_ConservativeGCOfWeakContainer ConservativeGCOfWeakContainer +#endif +TEST_F(IncrementalMarkingTest, MAYBE_ConservativeGCOfWeakContainer) { // Regression test: https://crbug.com/1108676 // // Test ensures that on-stack references to weak containers (e.g. iterators)
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_component.h b/third_party/blink/renderer/platform/mediastream/media_stream_component.h index 87b05e6..2bbb7af 100644 --- a/third_party/blink/renderer/platform/mediastream/media_stream_component.h +++ b/third_party/blink/renderer/platform/mediastream/media_stream_component.h
@@ -64,6 +64,7 @@ virtual String Id() const = 0; // Uniquely identifies this component. virtual int UniqueId() const = 0; + virtual bool Remote() const = 0; virtual bool Enabled() const = 0; virtual void SetEnabled(bool enabled) = 0; virtual WebMediaStreamTrack::ContentHintType ContentHint() = 0;
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_component_impl.h b/third_party/blink/renderer/platform/mediastream/media_stream_component_impl.h index 1c141dd9..1e3d99a 100644 --- a/third_party/blink/renderer/platform/mediastream/media_stream_component_impl.h +++ b/third_party/blink/renderer/platform/mediastream/media_stream_component_impl.h
@@ -42,6 +42,7 @@ #include "third_party/blink/renderer/platform/heap/prefinalizer.h" #include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_track_platform.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -49,7 +50,6 @@ namespace blink { -class MediaStreamSource; class WebLocalFrame; class PLATFORM_EXPORT MediaStreamComponentImpl final @@ -82,6 +82,7 @@ String Id() const override { return id_; } int UniqueId() const override { return unique_id_; } + bool Remote() const override { return Source()->Remote(); } bool Enabled() const override { return enabled_; } void SetEnabled(bool enabled) override { enabled_ = enabled; } WebMediaStreamTrack::ContentHintType ContentHint() override {
diff --git a/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.cc b/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.cc index 623b779e9..0adc6d5c 100644 --- a/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.cc +++ b/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.cc
@@ -45,6 +45,14 @@ return 0; } +bool TransferredMediaStreamComponent::Remote() const { + if (component_) { + return component_->Remote(); + } + // TODO(crbug.com/1288839): Return the transferred value + return false; +} + bool TransferredMediaStreamComponent::Enabled() const { if (component_) { return component_->Enabled();
diff --git a/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.h b/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.h index b09dda6..370ed50 100644 --- a/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.h +++ b/third_party/blink/renderer/platform/mediastream/transferred_media_stream_component.h
@@ -39,6 +39,7 @@ String Id() const override; int UniqueId() const override; + bool Remote() const override; bool Enabled() const override; void SetEnabled(bool enabled) override; WebMediaStreamTrack::ContentHintType ContentHint() override;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 54908d9..ebd68bd 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -308,10 +308,6 @@ status: "experimental", }, { - name: "CancelFormSubmissionInDefaultHandler", - status: "stable", - }, - { name: "Canvas2dImageChromium", }, { @@ -584,7 +580,7 @@ }, { name: "CSSIcUnit", - status: "experimental", + status: "stable", }, { name: "CSSIndependentTransformProperties",
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials index 891eb45..2d039a5 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials +++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -49,6 +49,7 @@ crbug.com/1209223 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ] # ====== New tests from wpt-importer added here ====== +crbug.com/626703 external/wpt/custom-elements/form-associated/ElementInternals-target-element-is-held-strongly.html [ Timeout ] crbug.com/626703 external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-construct-xml-parser.xhtml [ Crash ] crbug.com/626703 external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-reactions-xml-parser.xhtml [ Crash ] crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/grid-aspect-ratio-040.html [ Crash ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index bac317db..48b566f 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1478,6 +1478,14 @@ crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/row-005.html [ Failure ] crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/row-007.html [ Failure ] crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/row-008.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-001.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-002.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-003.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-005.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-006.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-007.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-008.html [ Failure ] +crbug.com/240765 external/wpt/css/css-flexbox/intrinsic-size/col-wrap-009.html [ Failure ] # These require css-align-3 positional keywords that Blink doesn't yet support for flexbox. crbug.com/1011718 external/wpt/css/css-flexbox/flexbox-safe-overflow-position-001.html [ Failure ] @@ -2395,7 +2403,7 @@ crbug.com/922618 external/wpt/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html [ Timeout ] # crbug.com/1095379: These are flaky-slow, but are already present in SlowTests -crbug.com/73609 http/tests/media/video-play-stall.html [ Pass Timeout Failure ] +crbug.com/73609 http/tests/media/video-play-stall.html [ Failure Pass Timeout ] crbug.com/518987 http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ] crbug.com/538717 [ Win ] http/tests/permissions/chromium/test-request-worker.html [ Pass Timeout ] crbug.com/829740 fast/history/history-back-twice-with-subframes-assert.html [ Pass Timeout ] @@ -3365,8 +3373,8 @@ crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ] # ====== New tests from wpt-importer added here ====== -crbug.com/626703 [ Win10.20h2 ] external/wpt/custom-elements/form-associated/ElementInternals-target-element-is-held-strongly.html [ Timeout ] -crbug.com/626703 [ Mac11-arm64 ] external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.html [ Timeout Failure ] +crbug.com/626703 external/wpt/custom-elements/form-associated/ElementInternals-target-element-is-held-strongly.html [ Timeout ] +crbug.com/626703 [ Mac11-arm64 ] external/wpt/html/browsers/browsing-the-web/remote-context-helper-tests/navigation-same-document.window.html [ Failure Timeout ] crbug.com/626703 external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-construct-xml-parser.xhtml [ Crash ] crbug.com/626703 [ Linux ] external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-reactions-xml-parser.xhtml [ Crash ] crbug.com/626703 [ Mac10.15 ] external/wpt/custom-elements/throw-on-dynamic-markup-insertion-counter-reactions-xml-parser.xhtml [ Crash ]
diff --git a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations index f0c44ac9..c8fd002c 100644 --- a/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations +++ b/third_party/blink/web_tests/android/WebLayerWPTOverrideExpectations
@@ -1261,7 +1261,6 @@ crbug.com/1050754 external/wpt/paint-timing/with-first-paint/first-contentful-paint.html [ Pass ] crbug.com/1050754 external/wpt/paint-timing/with-first-paint/mask-image.html [ Pass ] crbug.com/1050754 external/wpt/paint-timing/with-first-paint/sibling-painting-first-image.html [ Failure ] -crbug.com/1050754 external/wpt/payment-handler/can-make-payment-event.https.html [ Failure Timeout ] crbug.com/1050754 external/wpt/payment-handler/respond-with-minimal-ui.https.html [ Failure Timeout ] crbug.com/1050754 external/wpt/payment-request/constructor_convert_method_data.https.html [ Pass Timeout ] crbug.com/1050754 external/wpt/payment-request/payment-request-hasenrolledinstrument-method-protection.tentative.https.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations index cca22cf..3d6f265 100644 --- a/third_party/blink/web_tests/android/WebviewWPTExpectations +++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -3694,7 +3694,6 @@ crbug.com/1050754 external/wpt/paint-timing/fcp-only/fcp-text-input.html [ Failure ] crbug.com/1050754 external/wpt/paint-timing/fcp-only/fcp-video-frame.html [ Failure ] crbug.com/1050754 external/wpt/paint-timing/fcp-only/fcp-video-poster.html [ Failure ] -crbug.com/1050754 external/wpt/payment-handler/can-make-payment-event.https.html [ Failure Pass ] crbug.com/1050754 external/wpt/payment-handler/idlharness.https.any.serviceworker.html [ Failure Pass ] crbug.com/1050754 external/wpt/payment-handler/idlharness.https.any.sharedworker.html [ Failure ] crbug.com/1050754 external/wpt/payment-handler/idlharness.https.any.worker.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version index d035e34..206a814 100644 --- a/third_party/blink/web_tests/external/Version +++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@ -Version: c4ad1de47c85a65efde93aae9362359475fac31a +Version: c1783313cda795ef118c6974015ee47de58a0f80
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 7111446b..f48ee71 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
@@ -113553,6 +113553,97 @@ ] ], "intrinsic-size": { + "col-wrap-001.html": [ + "45da5123896a7b6ebd430b1ea0b67f1965771ea7", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-002.html": [ + "0e61cf7846213a7237e574a248965fcd40695889", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-003.html": [ + "518bf9f65844e00998b7154f074b14d93e71df40", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-006.html": [ + "52560057c8327cabd99fa7c7813ec630db42e7ec", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-007.html": [ + "7f50f504569fa7e0a40e95fb881970f8145a6489", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-008.html": [ + "5edde9495d1382e774cd295cabf81766b0b41a7b", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "col-wrap-009.html": [ + "62514dbcffea07f87bf01930dd45e00d2e6a6af1", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "row-001.html": [ "a681d07d58a15559cc6594fb045ff9c42c6981b8", [ @@ -292948,7 +293039,7 @@ [] ], "compute-kind-widget-no-fallback-ref.html": [ - "241ecf93751bc3db73c4c586a6d695e0cdc51e7a", + "c0e56dc051000b907d5c7b66f9a014470c14c685", [] ], "input-security-auto-sensitive-text-input-ref.html": [ @@ -300126,7 +300217,7 @@ "non-cancelable-when-passive": { "resources": { "scrolling.js": [ - "1e96a7f49715db0822867727a1fcb14a30fdad48", + "43c693857928da167be4d0b163abde5acf2ede69", [] ], "touching.js": [ @@ -322272,6 +322363,14 @@ "largest-contentful-paint-helpers.js": [ "5012faf3b1be3388b9e342af5ee25265e4c3de22", [] + ], + "lcp-sw-from-cache.js": [ + "c650a0b7471bf499a00c2afdbd596b9688284dd1", + [] + ], + "lcp-sw.https.html": [ + "069a50eae9a90bce2f8fb1022fed5bd72b5a5d17", + [] ] } }, @@ -322344,7 +322443,7 @@ ] }, "lint.ignore": [ - "6c93ccafb44b1fe2e58e69e4dd2aac166df9de64", + "1f6f823f704c809133a31d321f67ea82b2cf9fb4", [] ], "loading": { @@ -328086,11 +328185,11 @@ [] ], "generate-report-once.py": [ - "076d5008751f5083353214362506bdf123a22919", + "163846a4b962c156442f4375dfec3168ee705c74", [] ], "generate-report.https.sub.html": [ - "ee40c46e3870db95c679c82263c8ed79bceff09b", + "f1f4e96300653af6c169ed41e42d3aaff144ebae", [] ], "middle-frame.https.sub.html": [ @@ -379785,6 +379884,20 @@ ] ], "intrinsic-size": { + "col-wrap-004.html": [ + "00cfa520690374562242cbca8fa19c0e1ca44520", + [ + null, + {} + ] + ], + "col-wrap-005.html": [ + "a753619c62f51b85fa0b51f0cb058bec589707c5", + [ + null, + {} + ] + ], "row-005.html": [ "0f34c6fbdb400c35af723c8be56c22bbe1612e96", [ @@ -390729,6 +390842,13 @@ {} ] ], + "contain-intrinsic-size-030.html": [ + "8acfabbf618290d83f6399db825d0710749173f4", + [ + null, + {} + ] + ], "contain-intrinsic-size-logical-003.html": [ "59ae8328f2dfaba25094bdf90cf6c0bc1abbdee0", [ @@ -492637,6 +492757,13 @@ {} ] ], + "image-sw-same-origin.https.html": [ + "3f375008c535a68f339f750eff2cc91bfc07ddcb", + [ + null, + {} + ] + ], "image-upscaling.html": [ "a4f7d8079d3b9a75f79b896743cbc018cc81c9ce", [ @@ -505592,10 +505719,12 @@ ] ], "can-make-payment-event.https.html": [ - "b2016a05ec34fd8148a1b43a102ca3f1b51b6526", + "28c001c654e1445dfa00193269fabadbf17fc2f0", [ null, - {} + { + "testdriver": true + } ] ], "idlharness.https.any.js": [ @@ -508543,12 +508672,11 @@ ] ], "pointerevent_touch-action-inherit_parent-none_touch.html": [ - "e8c205a042f283296c39d0d249739a2b0d23afe0", + "cf9e2f7e4e5c8d9128be453f47e2ef98e1bfa445", [ null, { - "testdriver": true, - "timeout": "long" + "testdriver": true } ] ], @@ -508563,7 +508691,7 @@ ] ], "pointerevent_touch-action-modified_touch.html": [ - "c53264d103da3c7a19664a9053184c43f93930bd", + "a91022504a7ebf8dee21a368b5911285ef614e1c", [ null, { @@ -521947,7 +522075,7 @@ ] ], "document-reporting-default-endpoint.https.sub.html": [ - "24c1216afaa99917c0afd84f6e3e09ac4ab63089", + "f1951e3469b90ec7040b7c2e6a9d2c0178d275b2", [ null, { @@ -521984,7 +522112,7 @@ ] ], "document-reporting-path-absolute.https.sub.html": [ - "e23dd85e7c4c5efa1aae87cdd85a5fb1ed143680", + "48be010a00be6db990a118f58543a900cb1da259", [ null, { @@ -552670,7 +552798,7 @@ ] ], "audioDecoder-codec-specific.https.any.js": [ - "994aaf1d17b73428eaf6b316aa73fd76e6e29b40", + "b53965c529d475c6fdc8df8061936f14391013ce", [ "webcodecs/audioDecoder-codec-specific.https.any.html?adts_aac", { @@ -554750,7 +554878,7 @@ ] ], "videoDecoder-codec-specific.https.any.js": [ - "f94bee82d18de1da1fa9d685005baac5327402d8", + "fab0b24220dc9be9f34186c851dc20fd21ee1c57", [ "webcodecs/videoDecoder-codec-specific.https.any.html?av1", { @@ -591812,6 +591940,13 @@ null, {} ] + ], + "nested-multicol-with-spanner-and-oof-crash-006.html": [ + "b969f925d3fa132a332fca462917f1f893973f05", + [ + null, + {} + ] ] }, "css-counter-styles": {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html new file mode 100644 index 0000000..aec3f9a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/custom-property-style-queries.html
@@ -0,0 +1,96 @@ +<!doctype html> +<title>CSS Container Queries Test: custom property style queries</title> +<link rel="help" href="https://drafts.csswg.org/css-contain-3/#style-container"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/cq-testcommon.js"></script> +<style> + #outer { + container-name: outer; + --inner: false; + --outer: true; + --inner-no-space:false; + --outer-no-space:true; + --inner-space-after:false ; + --outer-space-after:true ; + } + #inner { + --inner: true; + --outer: false; + --inner-no-space:true; + --outer-no-space:false; + --inner-space-after:true ; + --outer-space-after:false ; + } +</style> +<div id="outer"> + <div id="inner"> + <div id="target"></div> + </div> +</div> +<script> + function test_evaluation(query, expected) { + test((t) => { + let style_node = document.createElement('style'); + t.add_cleanup(() => { + style_node.remove(); + }); + style_node.innerText = `@container ${query} { #target { --applied:true; } }`; + + assert_equals(getComputedStyle(target).getPropertyValue('--applied'), ''); + document.head.append(style_node); + assert_equals(getComputedStyle(target).getPropertyValue('--applied'), expected ? 'true' : ''); + }, `${query}`); + } + + setup(() => assert_implements_container_queries()); + + test_evaluation('style(--inner: true)', true); + test_evaluation('style(--inner:true)', true); + test_evaluation('style(--inner:true )', true); + test_evaluation('style(--inner: true )', true); + test_evaluation('style(--inner-no-space: true)', true); + test_evaluation('style(--inner-no-space:true)', true); + test_evaluation('style(--inner-no-space:true )', true); + test_evaluation('style(--inner-no-space: true )', true); + test_evaluation('style(--inner-space-after: true)', true); + test_evaluation('style(--inner-space-after:true)', true); + test_evaluation('style(--inner-space-after:true )', true); + test_evaluation('style(--inner-space-after: true )', true); + test_evaluation('style(--outer: true)', false); + test_evaluation('style(--outer:true)', false); + test_evaluation('style(--outer:true )', false); + test_evaluation('style(--outer: true )', false); + test_evaluation('style(--outer-no-space: true)', false); + test_evaluation('style(--outer-no-space:true)', false); + test_evaluation('style(--outer-no-space:true )', false); + test_evaluation('style(--outer-no-space: true )', false); + test_evaluation('style(--outer-space-after: true)', false); + test_evaluation('style(--outer-space-after:true)', false); + test_evaluation('style(--outer-space-after:true )', false); + test_evaluation('style(--outer-space-after: true )', false); + test_evaluation('outer style(--inner: true)', false); + test_evaluation('outer style(--inner:true)', false); + test_evaluation('outer style(--inner:true )', false); + test_evaluation('outer style(--inner: true )', false); + test_evaluation('outer style(--inner-no-space: true)', false); + test_evaluation('outer style(--inner-no-space:true)', false); + test_evaluation('outer style(--inner-no-space:true )', false); + test_evaluation('outer style(--inner-no-space: true )', false); + test_evaluation('outer style(--inner-space-after: true)', false); + test_evaluation('outer style(--inner-space-after:true)', false); + test_evaluation('outer style(--inner-space-after:true )', false); + test_evaluation('outer style(--inner-space-after: true )', false); + test_evaluation('outer style(--outer: true)', true); + test_evaluation('outer style(--outer:true)', true); + test_evaluation('outer style(--outer:true )', true); + test_evaluation('outer style(--outer: true )', true); + test_evaluation('outer style(--outer-no-space: true)', true); + test_evaluation('outer style(--outer-no-space:true)', true); + test_evaluation('outer style(--outer-no-space:true )', true); + test_evaluation('outer style(--outer-no-space: true )', true); + test_evaluation('outer style(--outer-space-after: true)', true); + test_evaluation('outer style(--outer-space-after:true)', true); + test_evaluation('outer style(--outer-space-after:true )', true); + test_evaluation('outer style(--outer-space-after: true )', true); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-001.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-001.html new file mode 100644 index 0000000..45da512 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-001.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="column-wrap container's max-content width is big enough that items don't overflow when the container has fixed height and items have fixed width and height. Old algorithm gave max-content width of 50px." /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + width: 50px; + flex: 0 0 100px + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<div + style="display: flex; flex-flow: column wrap; height: 100px; width: max-content; background: green;" + class=flex> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-002.html new file mode 100644 index 0000000..0e61cf78 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-002.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="column-wrap container's max-content width is big enough that items don't overflow when the container has indefinite height but fixed max-height and items have fixed width and height. Old algorithm gave max-content width of 50px." /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + width: 50px; + flex: 0 0 100px + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<div + style="display: flex; flex-flow: column wrap; max-height: 100px; width: max-content; background: green;" + class=flex> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-003.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-003.html new file mode 100644 index 0000000..518bf9f6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-003.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="Cross-axis borders are accounted for when determining inline content sizes of column-wrap flexboxes." /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + width: 40px; + flex: 0 0 100px; + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<div + style="display: flex; flex-flow: column wrap; max-height: 100px; width: max-content; background: green; padding-left: 5px; border-left: 9px solid green; border-right: 6px solid green;"> + <div class="item"></div> + <div class="item"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-004.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-004.html new file mode 100644 index 0000000..00cfa52 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-004.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<meta name="assert" + content="Item with width:100% doesn't contribute to max-content size but does get 100px width after final layout. The item also overflows the container." /> + +<style> + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + flex: 0 0 100px; + outline: 1px solid; + } +</style> + +<div + style="display: flex; flex-flow: column wrap; width: max-content; height: 100px; background: green; position: relative;" + data-expected-width="100"> + <div class="item" style="width: 100px;"></div> + <div class="item" style="width: 100%;" data-expected-width="100" + data-offset-x="100"></div> +</div> + +<script> + checkLayout('body > div'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-005.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-005.html new file mode 100644 index 0000000..a753619 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-005.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<meta name="assert" + content="The item still has sufficient available size at the time fit-content is resolved. (A bug in the code could cause the right-most item to have a width of 75px.)"> + +<style> + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + flex: 0 0 100px; + outline: 1px solid; + } + + .grandchild { + width: 75px; + float: left; + } +</style> + +<div + style="display: flex; flex-flow: column wrap; width: max-content; height: 100px; background: green; position: relative;" + data-expected-width="250"> + <div class="item" style="width: 100px;"></div> + <div class="item" style="width: fit-content;" data-expected-width="150" + data-offset-x="100"> + <!-- This item has min-content=75 and max-content=150. --> + <div class="grandchild"></div> + <div class="grandchild"></div> + </div> +</div> + +<script> + checkLayout('body > div'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-006.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-006.html new file mode 100644 index 0000000..52560057 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-006.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="flex item order is accounted for when determining inline content sizes of column-wrap flexboxes."> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + width: 50px; + flex: 0 0 auto; + } + +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<!-- An implementation that ignored order could make the max-content size of this flexbox 150px. --> +<div + style="display: flex; flex-flow: column wrap; width: max-content; height: 100px; background: green;"> + <div class="item" style="height: 90px; order: 0;"></div> + <div class="item" style="height: 100px; order: 2;"></div> + <div class="item" style="height: 10px; order: 1;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-007.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-007.html new file mode 100644 index 0000000..7f50f50 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-007.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="column wrap max-content width calculation works for wrap-reverse containers" /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + flex: 0 0 100px; + } + +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<div + style="display: flex; flex-flow: column wrap-reverse; height: 100px; width: max-content; background: green;"> + <div class="item" style="width: 30px;"></div> + <div class="item" style="width: 70px;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-008.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-008.html new file mode 100644 index 0000000..5edde949 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-008.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="column wrap max-content width calculation works for align-content:space-around containers" /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + + .item { + /* Remove min-height so we don't have to think about it. */ + min-height: 0px; + flex: 0 0 100px; + } + +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +</p> + +<div id=reference-overlapped-red></div> + +<div + style="display: flex; flex-flow: column wrap; height: 100px; width: max-content; align-content: space-around; background: green;"> + <div class="item" style="width: 30px;"></div> + <div class="item" style="width: 70px;"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-009.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-009.html new file mode 100644 index 0000000..62514dbc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/intrinsic-size/col-wrap-009.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" + content="column wrap container's intrinsic width changes when its height changes." /> + +<style> + #reference-overlapped-red { + position: absolute; + background-color: red; + width: 100px; + height: 100px; + z-index: -1; + } + span { + width: 50px; + height: 100px; + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>. +<div id=reference-overlapped-red></div> + +<div style="display: flex; height: 200px;" id="target"> + <div + style="display: flex; flex-flow: column wrap; width: max-content; background: green;"> + <span></span> + <span></span> + </div> +</div> + +<script> + target.offsetLeft; + // With original height of 200px, both items fit in one flex line. + // With height 100px, there are two lines and the items are side-by-side. + target.style.height = "100px"; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/radial-gradient-transition-hint-crash.html b/third_party/blink/web_tests/external/wpt/css/css-images/radial-gradient-transition-hint-crash.html new file mode 100644 index 0000000..51519a19 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-images/radial-gradient-transition-hint-crash.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>Color transition hint between values that are far apart</title> +<link rel="help" href="https://drafts.csswg.org/css-images-4/#radial-gradients"> +<link rel="help" href="https://drafts.csswg.org/css-images-4/#color-transition-hint"> +<div style="width:400px;height:400px;"></div> +<style> +div { + background-image: radial-gradient(green -1540359700%, 0px, darkgrey 2%); +} +</style> +PASS if no crash
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units-dynamic.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units-dynamic.html new file mode 100644 index 0000000..68adff6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units-dynamic.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>@property: viewport units in initial value (dynamic)</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + iframe { + width: 400px; + height: 200px; + } +</style> +<iframe id=iframe srcdoc=" + <style> + @property --10vw { syntax: '<length>'; inherits: true; initial-value: 10vw} + @property --10vh { syntax: '<length>'; inherits: true; initial-value: 10vh} + div { + background: green; + width: var(--10vw); + height: var(--10vh); + } + </style> + <div style='width:10vw'></div> +"></iframe> +<script> + iframe.offsetTop; + + function waitForLoad(w) { + return new Promise(resolve => w.addEventListener('load', resolve)); + } + + promise_test(async (t) => { + await waitForLoad(window); + let element = iframe.contentDocument.querySelector('div'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vw'), '40px'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vh'), '20px'); + + iframe.style.width = '100px'; + assert_equals(getComputedStyle(element).getPropertyValue('--10vw'), '10px'); + assert_equals(getComputedStyle(element).getPropertyValue('--10vh'), '20px'); + }); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units.html b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units.html new file mode 100644 index 0000000..51520c2a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/at-property-viewport-units.html
@@ -0,0 +1,91 @@ +<!DOCTYPE html> +<title>@property: viewport units in initial value</title> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + iframe { + width: 400px; + height: 200px; + } +</style> +<iframe id=iframe srcdoc=" + <style> + @property --10vw { syntax: '<length>'; inherits: true; initial-value: 10vw} + @property --10vh { syntax: '<length>'; inherits: true; initial-value: 10vh} + @property --10vi { syntax: '<length>'; inherits: true; initial-value: 10vi} + @property --10vb { syntax: '<length>'; inherits: true; initial-value: 10vb} + @property --10vmin { syntax: '<length>'; inherits: true; initial-value: 10vmin} + @property --10vmax { syntax: '<length>'; inherits: true; initial-value: 10vmax} + + @property --10svw { syntax: '<length>'; inherits: true; initial-value: 10svw} + @property --10svh { syntax: '<length>'; inherits: true; initial-value: 10svh} + @property --10svi { syntax: '<length>'; inherits: true; initial-value: 10svi} + @property --10svb { syntax: '<length>'; inherits: true; initial-value: 10svb} + @property --10svmin { syntax: '<length>'; inherits: true; initial-value: 10svmin} + @property --10svmax { syntax: '<length>'; inherits: true; initial-value: 10svmax} + + @property --10lvw { syntax: '<length>'; inherits: true; initial-value: 10lvw} + @property --10lvh { syntax: '<length>'; inherits: true; initial-value: 10lvh} + @property --10lvi { syntax: '<length>'; inherits: true; initial-value: 10lvi} + @property --10lvb { syntax: '<length>'; inherits: true; initial-value: 10lvb} + @property --10lvmin { syntax: '<length>'; inherits: true; initial-value: 10lvmin} + @property --10lvmax { syntax: '<length>'; inherits: true; initial-value: 10lvmax} + + @property --10dvw { syntax: '<length>'; inherits: true; initial-value: 10dvw} + @property --10dvh { syntax: '<length>'; inherits: true; initial-value: 10dvh} + @property --10dvi { syntax: '<length>'; inherits: true; initial-value: 10dvi} + @property --10dvb { syntax: '<length>'; inherits: true; initial-value: 10dvb} + @property --10dvmin { syntax: '<length>'; inherits: true; initial-value: 10dvmin} + @property --10dvmax { syntax: '<length>'; inherits: true; initial-value: 10dvmax} + </style> + <div></div> +"></iframe> +<script> + iframe.offsetTop; + + function waitForLoad(w) { + return new Promise(resolve => { + if (w.document.readyState == 'complete') + resolve(); + else + w.addEventListener('load', resolve) + }); + } + + function test_unit(element, actual, expected) { + promise_test(async (t) => { + await waitForLoad(window); + let element = iframe.contentDocument.querySelector('div'); + assert_equals(getComputedStyle(element).getPropertyValue(`--${actual}`), expected); + },`${actual} is ${expected}`); + } + + test_unit(iframe, '10vw', '40px'); + test_unit(iframe, '10vh', '20px'); + test_unit(iframe, '10vi', '40px'); + test_unit(iframe, '10vb', '20px'); + test_unit(iframe, '10vmin', '20px'); + test_unit(iframe, '10vmax', '40px'); + + test_unit(iframe, '10svw', '40px'); + test_unit(iframe, '10svh', '20px'); + test_unit(iframe, '10svi', '40px'); + test_unit(iframe, '10svb', '20px'); + test_unit(iframe, '10svmin', '20px'); + test_unit(iframe, '10svmax', '40px'); + + test_unit(iframe, '10lvw', '40px'); + test_unit(iframe, '10lvh', '20px'); + test_unit(iframe, '10lvi', '40px'); + test_unit(iframe, '10lvb', '20px'); + test_unit(iframe, '10lvmin', '20px'); + test_unit(iframe, '10lvmax', '40px'); + + test_unit(iframe, '10dvw', '40px'); + test_unit(iframe, '10dvh', '20px'); + test_unit(iframe, '10dvi', '40px'); + test_unit(iframe, '10dvb', '20px'); + test_unit(iframe, '10dvmin', '20px'); + test_unit(iframe, '10dvmax', '40px'); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-030.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-030.html new file mode 100644 index 0000000..8acfabb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/contain-intrinsic-size-030.html
@@ -0,0 +1,135 @@ +<!DOCTYPE html> +<meta charset="utf8"> +<title>CSS contain-intrinsic-size: scroll containers</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scroll-container"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<style> +.test { + contain: size; + display: inline-block; + padding: 0; + border: 5px solid; +} +.test::before { + content: ''; + display: block; + width: 40px; + height: 20px; +} +.big-contents::before { + width: 400px; + height: 200px; +} +.overflow-auto { + overflow: auto; + scrollbar-gutter: stable; +} +.overflow-scroll { + overflow: scroll; +} +.overflow-hidden { + overflow: hidden; +} +.cis-none { + contain-intrinsic-size: none none; +} +.cis-height { + contain-intrinsic-size: none 50px; +} +.cis-width { + contain-intrinsic-size: 100px none; +} +.cis-both { + contain-intrinsic-size: 100px 50px; +} +</style> + +<div id="log"></div> + +<div class="test small-contents overflow-auto cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="40" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-auto cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="40" data-expected-scroll-height="50"></div> +<div class="test small-contents overflow-auto cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="100" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-auto cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="100" data-expected-scroll-height="50"></div> + +<div class="test small-contents overflow-scroll cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="40" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-scroll cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="40" data-expected-scroll-height="50"></div> +<div class="test small-contents overflow-scroll cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="100" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-scroll cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="100" data-expected-scroll-height="50"></div> + +<div class="test small-contents overflow-hidden cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="40" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-hidden cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="40" data-expected-scroll-height="50"></div> +<div class="test small-contents overflow-hidden cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="100" data-expected-scroll-height="20"></div> +<div class="test small-contents overflow-hidden cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="100" data-expected-scroll-height="50"></div> + + +<div class="test big-contents overflow-auto cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-auto cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-auto cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-auto cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> + +<div class="test big-contents overflow-scroll cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-scroll cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-scroll cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-scroll cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> + +<div class="test big-contents overflow-hidden cis-none" + data-expected-client-width="0" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-hidden cis-height" + data-expected-client-width="0" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-hidden cis-width" + data-expected-client-width="100" data-expected-client-height="0" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> +<div class="test big-contents overflow-hidden cis-both" + data-expected-client-width="100" data-expected-client-height="50" + data-expected-scroll-width="400" data-expected-scroll-height="200"></div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<script> +checkLayout(".test"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-no-fallback-ref.html b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-no-fallback-ref.html index 241ecf9..c0e56dc0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-no-fallback-ref.html +++ b/third_party/blink/web_tests/external/wpt/css/css-ui/compute-kind-widget-no-fallback-ref.html
@@ -3,6 +3,8 @@ <title>Reference: Compute kind of widget - no fallback</title> <style> #container { width: 500px; } + #container > #search-text-input { appearance: textfield; } + #container > #select-menulist-button { appearance: none; appearance: menulist-button; } </style> <div id="container"> <a>a</a>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/target-pseudo-in-has.html b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/target-pseudo-in-has.html index 3f0ea16..629a6a8 100644 --- a/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/target-pseudo-in-has.html +++ b/third_party/blink/web_tests/external/wpt/css/selectors/invalidation/target-pseudo-in-has.html
@@ -70,13 +70,8 @@ parent2.append(fragment2); - // Wait for :target to be detected. - await new Promise(r => requestAnimationFrame(r)); - // Wait again to avoid flaky test result. (https://crbug.com/1334635) - await new Promise(r => requestAnimationFrame(r)); - - assert_true(fragment2.matches(":target")); - assert_equals(getComputedStyle(parent2).color, GREEN, "parent2 should be green after re-appending :target child"); + // Skip to check parent2 color since there is nothing in the spec mentioning DOM mutations affecting the target element. + // - https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-fragid fragmentLink("fragment3").click();
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore index 6c93cca..1f6f823 100644 --- a/third_party/blink/web_tests/external/wpt/lint.ignore +++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -836,6 +836,7 @@ SET TIMEOUT: editing/crashtests/backcolor-in-nested-editing-host-td-from-DOMAttrModified.html SET TIMEOUT: editing/crashtests/contenteditable-will-be-blurred-by-focus-event-listener.html SET TIMEOUT: editing/crashtests/designMode-document-will-be-blurred-by-focus-event-listener.html +SET TIMEOUT: editing/crashtests/indent-outdent-after-closing-editable-dialog-element.html SET TIMEOUT: editing/crashtests/inserthtml-after-temporarily-removing-document-element.html SET TIMEOUT: editing/crashtests/inserthtml-in-text-adopted-to-other-document.html SET TIMEOUT: editing/crashtests/insertorderedlist-in-text-adopted-to-other-document.html
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html index b2016a05..28c001c6 100644 --- a/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html +++ b/third_party/blink/web_tests/external/wpt/payment-handler/can-make-payment-event.https.html
@@ -5,6 +5,8 @@ <link rel="manifest" href="manifest.json"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> <script> const instrumentKey = 'instrument-key'; @@ -121,6 +123,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return false.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); await promise_rejects_dom(t, 'NotSupportedError', request.show()); }, 'If a payment handler is not installed, then the payment method is not supported.'); @@ -143,6 +147,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return false.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); await promise_rejects_dom(t, 'NotSupportedError', request.show()); }, 'If CanMakePaymentEvent.respondWith(false) is called, then the payment method is not supported.'); @@ -165,6 +171,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return false.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); await promise_rejects_dom(t, 'NotSupportedError', request.show()); }, 'If CanMakePaymentEvent.respondWith(Promise.resolve(false)) is called, then the payment method is not supported.'); @@ -187,6 +195,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return true.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); const acceptPromise = request.show(); await request.abort(); await promise_rejects_dom(t, 'AbortError', acceptPromise); @@ -211,6 +221,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return true.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); const acceptPromise = request.show(); await request.abort(); await promise_rejects_dom(t, 'AbortError', acceptPromise); @@ -235,6 +247,8 @@ paymentRequestCanMakePaymentResult, 'canMakePayment() must return false.', ); + + await test_driver.bless('PaymentRequest.show() requires user activation'); await promise_rejects_dom(t, 'NotSupportedError', request.show()); }, 'If CanMakePaymentEvent.respondWith(Promise.reject(error)) is called, then the payment method is not supported.'); </script>
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/custom-property-style-queries-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/custom-property-style-queries-expected.txt new file mode 100644 index 0000000..c2a1b04 --- /dev/null +++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/custom-property-style-queries-expected.txt
@@ -0,0 +1,51 @@ +This is a testharness.js-based test. +FAIL style(--inner: true) assert_equals: expected "true" but got "" +FAIL style(--inner:true) assert_equals: expected "true" but got "" +FAIL style(--inner:true ) assert_equals: expected "true" but got "" +FAIL style(--inner: true ) assert_equals: expected "true" but got "" +PASS style(--inner-no-space: true) +PASS style(--inner-no-space:true) +FAIL style(--inner-no-space:true ) assert_equals: expected "true" but got "" +FAIL style(--inner-no-space: true ) assert_equals: expected "true" but got "" +FAIL style(--inner-space-after: true) assert_equals: expected "true" but got "" +FAIL style(--inner-space-after:true) assert_equals: expected "true" but got "" +PASS style(--inner-space-after:true ) +PASS style(--inner-space-after: true ) +PASS style(--outer: true) +PASS style(--outer:true) +PASS style(--outer:true ) +PASS style(--outer: true ) +PASS style(--outer-no-space: true) +PASS style(--outer-no-space:true) +PASS style(--outer-no-space:true ) +PASS style(--outer-no-space: true ) +PASS style(--outer-space-after: true) +PASS style(--outer-space-after:true) +PASS style(--outer-space-after:true ) +PASS style(--outer-space-after: true ) +PASS outer style(--inner: true) +PASS outer style(--inner:true) +PASS outer style(--inner:true ) +PASS outer style(--inner: true ) +PASS outer style(--inner-no-space: true) +PASS outer style(--inner-no-space:true) +PASS outer style(--inner-no-space:true ) +PASS outer style(--inner-no-space: true ) +PASS outer style(--inner-space-after: true) +PASS outer style(--inner-space-after:true) +PASS outer style(--inner-space-after:true ) +PASS outer style(--inner-space-after: true ) +FAIL outer style(--outer: true) assert_equals: expected "true" but got "" +FAIL outer style(--outer:true) assert_equals: expected "true" but got "" +FAIL outer style(--outer:true ) assert_equals: expected "true" but got "" +FAIL outer style(--outer: true ) assert_equals: expected "true" but got "" +PASS outer style(--outer-no-space: true) +PASS outer style(--outer-no-space:true) +FAIL outer style(--outer-no-space:true ) assert_equals: expected "true" but got "" +FAIL outer style(--outer-no-space: true ) assert_equals: expected "true" but got "" +FAIL outer style(--outer-space-after: true) assert_equals: expected "true" but got "" +FAIL outer style(--outer-space-after:true) assert_equals: expected "true" but got "" +PASS outer style(--outer-space-after:true ) +PASS outer style(--outer-space-after: true ) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png index 648ab38..16723db 100644 --- a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png index 1f9a4e4..7b28397 100644 --- a/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png index eaa1842..7c43d88 100644 --- a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-linear-gradients-color-hints-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png index ad44d768..ac1ae2e 100644 --- a/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png +++ b/third_party/blink/web_tests/platform/win/fast/gradients/unprefixed-repeating-gradient-color-hint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/embedder-coop-coep-blocked.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/embedder-coop-coep-blocked.https.html index bb75a8f..6d0ebf4 100644 --- a/third_party/blink/web_tests/wpt_internal/fenced_frame/embedder-coop-coep-blocked.https.html +++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/embedder-coop-coep-blocked.https.html
@@ -10,9 +10,12 @@ <script> promise_test(async(t) => { const fencedframe = attachFencedFrameContext(); - t.step_timeout(() => t.done(), 1000); - await fencedframe.execute(() => {}); - assert_unreached("fenced frame should not be loaded."); + const fencedframe_loaded = fencedframe.execute(() => {}); + const fencedframe_blocked = new Promise(r => t.step_timeout(r, 1000)); + assert_equals("blocked", await Promise.any([ + fencedframe_blocked.then(() => "blocked"), + fencedframe_loaded.then(() => "loaded"), + ]), "fenced frame should not be loaded."); }, 'Create fencedframe with COOP:same-origin and COEP:require-corp'); </script> -</body> \ No newline at end of file +</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/iframe-parent-changes-baseurl.https.window.js b/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/iframe-parent-changes-baseurl.https.window.js new file mode 100644 index 0000000..ad32b7b --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/iframe-parent-changes-baseurl.https.window.js
@@ -0,0 +1,129 @@ +// This test loads a variety of different iframe configurations. It then +// verifies that when the parent changes its baseURI, the child frames do not +// see the change. + +// Helper to load the iframes once they have been created and configured. +async function load_iframe(the_iframe) { + const the_iframe_load = new Promise(resolve => { + the_iframe.onload = resolve; + }); + document.body.appendChild(the_iframe); + await the_iframe_load; +} + +async function check_results(the_iframe, expected_base_uri) { + // Send a postMessage to trigger sending results from `the_iframe`. The + // following promise resolves with the value of the next message received via + // postMessage from `the_iframe`. + const child_base_uri = new Promise(r => onmessage = e => r(e.data)); + the_iframe.contentWindow.postMessage("base url", "*"); + const result = await child_base_uri; + assert_true(result.links_unchanged); + assert_equals(expected_base_uri, result.base_uri); +} + +onload = () => { + promise_test(async test => { + const html_src = + await fetch('./resources/baseurl-test.html').then(r => r.text()); + + // Create some links to verify parent behavior in baseURI change. + const link_rel1 = document.createElement("a"); + link_rel1.href = "resources/baseurl-test.html"; + link_rel1.id = 'link_rel1'; + document.body.appendChild(link_rel1); + const link_rel2 = document.createElement("a"); + // Note: link_rel2 below is relative to the origin (it starts with a /), + // and not the path, of the main document, and so will be different from + // link_rel1. + link_rel2.href = "/resources/baseurl-test.html"; + link_rel2.id = 'link_rel2'; + document.body.appendChild(link_rel2); + + // Verify the link urls are what we expect. + const base_url = new URL(document.baseURI); + assert_equals( + base_url.origin + '/resources/baseurl-test.html', link_rel2.href); + const last_slash_index = base_url.pathname.lastIndexOf('/'); + const test_page_url = base_url.origin + + base_url.pathname.substr(0, last_slash_index) + + '/resources/baseurl-test.html'; + assert_equals(test_page_url, link_rel1.href); + + // Create sandboxed srcdoc iframe for test. + const iframe1 = document.createElement("iframe"); + iframe1.sandbox = "allow-scripts"; + iframe1.srcdoc = html_src; + await load_iframe(iframe1); + + // Create regular srcdoc iframe for test. + const iframe2 = document.createElement("iframe"); + iframe2.srcdoc = html_src; + await load_iframe(iframe2); + + // Create data src iframe for test. + let data_src = 'data:text/html,' + html_src; + const iframe3 = document.createElement("iframe"); + iframe3.src = data_src; + await load_iframe(iframe3); + // Need to do a read-back here as '%0A' in original gets transcribed to '\n' + // during the load, and will be represented as such in the child's baseURI. + data_src = iframe3.src; + + // Create regular src iframe for test. + const iframe4 = document.createElement("iframe"); + iframe4.src = test_page_url; + await load_iframe(iframe4); + + // Create src-as-srcdoc frame to test. + const iframe5 = document.createElement("iframe"); + iframe5.src = 'about:srcdoc'; + await load_iframe(iframe5); + iframe5.contentDocument.write(html_src); + await test.step_wait(() => iframe5.contentWindow.script_loaded); + + // Create about:blank frame to test. + const iframe6 = document.createElement("iframe"); + iframe6.src = 'about:blank'; + await load_iframe(iframe6); + iframe6.contentDocument.write(html_src); + await test.step_wait(() => iframe6.contentWindow.script_loaded); + + // Trigger the test scenario by changing the parent's baseURI, then querying + // the child. + const old_base_uri = document.baseURI; + const base_element = document.createElement('base'); + const new_base_uri = "https://foo.com/"; + base_element.href = new_base_uri; + document.head.appendChild(base_element); + assert_equals(new_base_uri, document.baseURI); + + // Verify the parent link urls change as expected. + const new_test_url = new URL(new_base_uri); + assert_equals( + new_test_url.origin + '/resources/baseurl-test.html', link_rel2.href); + const new_last_slash_index = new_test_url.pathname.lastIndexOf('/'); + const new_test_page_url = new_test_url.origin + + new_test_url.pathname.substr(0, new_last_slash_index) + + '/resources/baseurl-test.html'; + assert_equals(new_test_page_url, link_rel1.href); + + // sandboxed srcdoc iframe + await check_results(iframe1, old_base_uri); + + // regular srcdoc iframe + await check_results(iframe2, old_base_uri); + + // data iframe + await check_results(iframe3, data_src); + + // regular same-site iframe + await check_results(iframe4, test_page_url); + + // about:srcdoc iframe + await check_results(iframe5, old_base_uri); + + // about:blank iframe + await check_results(iframe6, old_base_uri); + }, 'iframe doesn\'t see change in parent baseURI'); +}
diff --git a/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/resources/baseurl-test.html b/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/resources/baseurl-test.html new file mode 100644 index 0000000..be75ed9 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/semantics/embedded-content/the-iframe-element/resources/baseurl-test.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>baseurl test page</title> +<body> + <a href='foo.html' id='link_rel1'>txt</a> + <!-- Note: link_rel2 below is relative to the origin (it starts with a /), and + not the path, of the document, and so will be different from link_rel1. + --> + <a href='/foo.html' id='link_rel2'>txt</a> + <script> + let original_link_rel1 = link_rel1.href; + let original_link_rel2 = link_rel2.href; + window.addEventListener('message', (event) => { + // Verify the original link values haven't changed after the parent + // changes its baseURI. + const links_unchanged = + original_link_rel1 === link_rel1.href && + original_link_rel2 === link_rel2.href; + event.source.postMessage( + {'base_uri': document.baseURI, + 'links_unchanged': links_unchanged }, + event.origin); + }, false); + + // When the content is loaded with document.write, the following value is + // used to verify when the content has finished loading. + window.script_loaded = true; + </script> +</body>
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium index 21288f5..5872ed4f 100644 --- a/third_party/nearby/README.chromium +++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@ Name: Nearby Connections Library Short Name: Nearby URL: https://github.com/google/nearby -Version: 0a8f1f1c39af06dff550d8ca96c6e087994155b7 +Version: 55f20775d209871190580f0ef461ae1c816b1870 License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/tools/code_coverage/merge_js_source_maps/OWNERS b/tools/code_coverage/merge_js_source_maps/OWNERS new file mode 100644 index 0000000..7d4c1180 --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/OWNERS
@@ -0,0 +1,2 @@ +benreich@chromium.org +tiborg@chromium.org
diff --git a/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.gni b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.gni new file mode 100644 index 0000000..1463400 --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.gni
@@ -0,0 +1,31 @@ +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//ui/webui/webui_features.gni") + +template("merge_js_source_maps") { + assert(enable_webui_inline_sourcemaps) + + action(target_name) { + forward_variables_from(invoker, + [ + "sources", + "outputs", + "deps", + ]) + script = + "//tools/code_coverage/merge_js_source_maps/merge_js_source_maps.py" + args = [ "--manifest-files" ] + + rebase_path(invoker.manifest_files, root_out_dir) + [ "--sources" ] + + rebase_path(invoker.sources, root_out_dir) + [ "--outputs" ] + + rebase_path(invoker.outputs, root_out_dir) + inputs = + [ "//tools/code_coverage/merge_js_source_maps/merge_js_source_maps.js" ] + foreach(manifest, invoker.manifest_files) { + outputs += [ get_path_info(manifest, "dir") + "/" + + get_path_info(manifest, "name") + "__processed." + + get_path_info(manifest, "extension") ] + } + } +}
diff --git a/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.js b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.js new file mode 100644 index 0000000..46b2774 --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.js
@@ -0,0 +1,172 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Merges 2 inline sourcemaps for a input file and writes a new + * file to an output directory. + */ + +import fs from 'fs'; +import path from 'path'; + +import {ArgumentParser} from '../../../third_party/js_code_coverage/node_modules/argparse/argparse.js'; +import {SourceMapConsumer, SourceMapGenerator} from '../../../third_party/js_code_coverage/node_modules/source-map/source-map.js'; + +/** + * The prefix comment that indicates a data URL containing the sourcemap. + */ +const SOURCEMAPPING_DATA_URL_PREFIX = + '//# sourceMappingURL=data:application/json;base64,'; + +/** + * Decode a base64 encoded string representing a sourcemap to its utf-8 + * equivalent. + * @param {string} contents Base64 encoded string. + * @returns Decoded utf-8 string of the sourcemap. + */ +function decodeBase64SourceMap(contents) { + const removedLeadingComment = + contents.replace(SOURCEMAPPING_DATA_URL_PREFIX, ''); + const buf = Buffer.from(removedLeadingComment, 'base64'); + return buf.toString('utf-8'); +} + +/** + * Helper to identify if a supplied line is an inline sourcemap. + * @param {string} lineContents Contents of an individual line. + * @returns True if line is an inline sourcemap, null otherwise. + */ +function isSourceMapComment(lineContents) { + return lineContents && lineContents.startsWith(SOURCEMAPPING_DATA_URL_PREFIX); +} + +/** + * Convert `contents` into a valid dataURL sourceMappingURL comment. + * @param {string} contents A string representation of the sourcemap + * @returns A base64 encoded dataURL with the `SOURCEMAPPING_DATA_URL_PREFIX` + * prepended. + */ +function encodeBase64SourceMap(contents) { + const buf = Buffer.from(contents, 'utf-8'); + return SOURCEMAPPING_DATA_URL_PREFIX + buf.toString('base64'); +} + +/** + * Merge multiple sourcemaps to a single file. + * @param {!Array<string>} sourceMaps An array of stringified sourcemaps. + * @returns Returns a single sourcemap as a string. + */ +async function mergeSourcemaps(sourceMaps) { + let generator = null; + let originalSource = null; + for (const sourcemap of sourceMaps) { + const parsedMap = JSON.parse(sourcemap); + if (!originalSource) { + originalSource = parsedMap.sources[0]; + } + const consumer = await new SourceMapConsumer(parsedMap); + if (generator) { + generator.applySourceMap(consumer, originalSource); + } else { + generator = await SourceMapGenerator.fromSourceMap(consumer); + } + consumer.destroy(); + } + return generator.toString(); +} + +/** + * Processes all input files for multiple inlined sourcemaps and merges them. + * @param {!Array<string>} inputFiles The list of TS / JS files to extract + * sourcemaps from. + */ +async function processFiles(inputFiles, outputFiles) { + for (let i = 0; i < inputFiles.length; i++) { + const inputFile = inputFiles[i]; + const outputFile = outputFiles[i]; + const fileContents = fs.readFileSync(inputFile, 'utf-8'); + const inputLines = fileContents.split('\n'); + + // Skip any trailing blank lines to find the last non-null line. + let lastNonNullLine = inputLines.length - 1; + while (inputLines[lastNonNullLine].trim().length === 0 && + lastNonNullLine > 0) { + lastNonNullLine--; + } + + // If the last non-null line identified is not a sourcemap, ignore this file + // as it may have erroneously been marked for sourcemap merge. + if (!isSourceMapComment(inputLines[lastNonNullLine])) { + console.warn('Supplied file has no inline sourcemap', inputFile); + fs.copyFileSync(inputFile, outputFile); + continue; + } + + // Extract out all the inline sourcemaps and decode them to their string + // equivalent. + const sourceMaps = [decodeBase64SourceMap(inputLines[lastNonNullLine])]; + let sourceMapLineIdx = lastNonNullLine - 1; + while (isSourceMapComment(inputLines[sourceMapLineIdx]) && + sourceMapLineIdx > 0) { + const sourceMap = decodeBase64SourceMap(inputLines[sourceMapLineIdx]); + sourceMaps.push(sourceMap); + sourceMapLineIdx--; + } + + let mergedSourceMap = null; + try { + mergedSourceMap = await mergeSourcemaps(sourceMaps); + } catch (e) { + console.error(`Failed to merge inlined sourcemaps for ${inputFile}:`, e); + fs.copyFileSync(inputFile, outputFile); + continue; + } + + // Drop off the lines that were previously identified as inline sourcemap + // comments and replace them with the merged sourcemap. + let finalFileContent = + inputLines.slice(0, sourceMapLineIdx + 1).join('\n') + '\n'; + if (mergedSourceMap) { + finalFileContent += encodeBase64SourceMap(mergedSourceMap); + } + fs.writeFileSync(outputFile, finalFileContent); + } +} + +async function main() { + const parser = + new ArgumentParser({description: 'Merge multiple inlined sourcemaps'}); + + parser.add_argument('--sources', {help: 'Input files', nargs: '*'}); + parser.add_argument('--outputs', {help: 'Output files', nargs: '*'}); + parser.add_argument( + '--manifest-files', {help: 'Output files', nargs: '*', required: false}); + + const argv = parser.parse_args(); + await processFiles(argv.sources, argv.outputs); + + if (argv.manifest_files) { + // TODO(crbug/1337530): Currently we just remove the final directory of the + // `base_dir` key. This is definitely brittle and also subject to changes + // made to the output directory. Consider updating this to be more robust. + for (const manifestFile of argv.manifest_files) { + try { + const manifestFileContents = + fs.readFileSync(manifestFile).toString('utf-8'); + const manifest = JSON.parse(manifestFileContents); + manifest.base_dir = path.parse(manifest.base_dir).dir; + const parsedPath = path.parse(manifestFile); + fs.writeFileSync( + path.join( + parsedPath.dir, + (parsedPath.name + '__processed' + parsedPath.ext)), + JSON.stringify(manifest)); + } catch (e) { + console.log(e); + } + } + } +} + +(async () => main())(); \ No newline at end of file
diff --git a/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.py b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.py new file mode 100755 index 0000000..493724b --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/merge_js_source_maps.py
@@ -0,0 +1,32 @@ +#!/usr/bin/env vpython3 +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import sys +from pathlib import Path + +_HERE_DIR = Path(__file__).parent +_SOURCE_MAP_MERGER = (_HERE_DIR / 'merge_js_source_maps.js').resolve() + +_NODE_PATH = (_HERE_DIR.parent.parent.parent / 'third_party' / 'node').resolve() +sys.path.append(str(_NODE_PATH)) +import node + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--sources', required=True, nargs="*") + parser.add_argument('--outputs', required=True, nargs="*") + parser.add_argument('--manifest-files', required=True, nargs="*") + args = parser.parse_args(argv) + + node.RunNode([ + str(_SOURCE_MAP_MERGER), '--manifest-files', *args.manifest_files, + '--sources', *args.sources, '--outputs', *args.outputs + ]) + + +if __name__ == '__main__': + main(sys.argv[1:])
diff --git a/tools/code_coverage/merge_js_source_maps/package.json b/tools/code_coverage/merge_js_source_maps/package.json new file mode 100644 index 0000000..36aa154f --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/package.json
@@ -0,0 +1,9 @@ +{ + "name": "merge_js_source_maps", + "version": "0.1", + "description": "Merge 2 inline sourcemaps", + "main": "merge_js_source_maps.js", + "files": [ "merge_js_source_maps.js" ], + "license": "SEE LICENSE IN ../../../LICENSE", + "type" : "module" +}
diff --git a/tools/code_coverage/merge_js_source_maps/test/generated_file_pre_merge.js b/tools/code_coverage/merge_js_source_maps/test/generated_file_pre_merge.js new file mode 100644 index 0000000..706556f --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/test/generated_file_pre_merge.js
@@ -0,0 +1,32 @@ +'use strict'; +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// clang-format off +// /*grit-removed-lines:4*/ +// Enums aren't natively available in JS, this will ensure a rewritten TS +// sourcemap. +var ExampleEnum; +(function (ExampleEnum) { + ExampleEnum[ExampleEnum["SOME_EXAMPLE"] = 0] = "SOME_EXAMPLE"; + ExampleEnum[ExampleEnum["OTHER_EXAMPLE"] = 1] = "OTHER_EXAMPLE"; +})(ExampleEnum || (ExampleEnum = {})); +class SpecialTypeScriptProperties { + constructor() { + /* Protected variables are typescript only specifiers */ + this.protectedValue = 0; + } +} +class Derived extends SpecialTypeScriptProperties { + constructor() { + super(...arguments); + /* So are private variables */ + this.privateValue = 0; + } + method() { + console.log(this.privateValue); + return this.protectedValue; + } +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm9yaWdpbmFsX2ZpbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyMiBUaGUgQ2hyb21pdW0gQXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbi8vIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUuXG4vLyBjbGFuZy1mb3JtYXQgb2ZmXG5cbi8vIDxpZiBleHByPVwiaXNfbWFjb3N4XCI+XG5mdW5jdGlvbiB0aGlzSXNSZW1vdmVkKCk6IGJvb2xlYW4ge1xuICByZXR1cm4gdHJ1ZTtcbn1cbi8vIDwvaWY+XG5cbi8vIEVudW1zIGFyZW4ndCBuYXRpdmVseSBhdmFpbGFibGUgaW4gSlMsIHRoaXMgd2lsbCBlbnN1cmUgYSByZXdyaXR0ZW4gVFNcbi8vIHNvdXJjZW1hcC5cbmVudW0gRXhhbXBsZUVudW0ge1xuICBTT01FX0VYQU1QTEUgPSAwLFxuICBPVEhFUl9FWEFNUExFID0gMSxcbn1cblxuYWJzdHJhY3QgY2xhc3MgU3BlY2lhbFR5cGVTY3JpcHRQcm9wZXJ0aWVzIHtcbiAgLyogUHJvdGVjdGVkIHZhcmlhYmxlcyBhcmUgdHlwZXNjcmlwdCBvbmx5IHNwZWNpZmllcnMgKi9cbiAgcHJvdGVjdGVkIHByb3RlY3RlZFZhbHVlOiBudW1iZXIgPSAwO1xuXG4gIGFic3RyYWN0IG1ldGhvZCgpOiBudW1iZXI7XG59XG5cbmNsYXNzIERlcml2ZWQgZXh0ZW5kcyBTcGVjaWFsVHlwZVNjcmlwdFByb3BlcnRpZXMge1xuICAvKiBTbyBhcmUgcHJpdmF0ZSB2YXJpYWJsZXMgKi9cbiAgcHJpdmF0ZSBwcml2YXRlVmFsdWU6IG51bWJlciA9IDA7XG5cbiAgbWV0aG9kKCkge1xuICAgIGNvbnNvbGUubG9nKHRoaXMucHJpdmF0ZVZhbHVlKTtcbiAgICByZXR1cm4gdGhpcy5wcm90ZWN0ZWRWYWx1ZTtcbiAgfVxufVxuIl19 +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JpZ2luYWxfZmlsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByZXByb2Nlc3NlZC9vaXJpZ2luYWxfZmlsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsNERBQTREO0FBQzVELHlFQUF5RTtBQUN6RSw2QkFBNkI7QUFDN0IsbUJBQW1CO0FBRW5CLDJCQUEyQjtBQUUzQix5RUFBeUU7QUFDekUsYUFBYTtBQUNiLElBQUssV0FHSjtBQUhELFdBQUssV0FBVztJQUNkLDZEQUFnQixDQUFBO0lBQ2hCLCtEQUFpQixDQUFBO0FBQ25CLENBQUMsRUFISSxXQUFXLEtBQVgsV0FBVyxRQUdmO0FBRUQsTUFBZSwyQkFBMkI7SUFBMUM7UUFDRSx3REFBd0Q7UUFDOUMsbUJBQWMsR0FBVyxDQUFDLENBQUM7SUFHdkMsQ0FBQztDQUFBO0FBRUQsTUFBTSxPQUFRLFNBQVEsMkJBQTJCO0lBQWpEOztRQUNFLDhCQUE4QjtRQUN0QixpQkFBWSxHQUFXLENBQUMsQ0FBQztJQU1uQyxDQUFDO0lBSkMsTUFBTTtRQUNKLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQy9CLE9BQU8sSUFBSSxDQUFDLGNBQWMsQ0FBQztJQUM3QixDQUFDO0NBQ0Y7QUFFRCwwaERBQTBoRCJ9 \ No newline at end of file
diff --git a/tools/code_coverage/merge_js_source_maps/test/merge_js_source_maps_test.py b/tools/code_coverage/merge_js_source_maps/test/merge_js_source_maps_test.py new file mode 100755 index 0000000..6ccad61 --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/test/merge_js_source_maps_test.py
@@ -0,0 +1,154 @@ +#!/usr/bin/env vpython3 +# Copyright 2022 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import base64 +import json +import os +import shutil +import sys +import tempfile +import unittest + +from parameterized import parameterized +from pathlib import Path + +_HERE_DIR = Path(__file__).parent.resolve() +_SOURCE_MAP_MERGER = (_HERE_DIR.parent / 'merge_js_source_maps.js').resolve() + +# TODO(crbug/1337530): Move common sourcemap build rules and tests into a more +# generic location. +_SOURCE_MAP_TRANSLATOR = (_HERE_DIR.parent.parent / 'create_js_source_maps' / + 'test' / 'translate_source_map.js').resolve() + +_NODE_PATH = (_HERE_DIR.parent.parent.parent.parent / 'third_party' / + 'node').resolve() +sys.path.append(str(_NODE_PATH)) +import node + +_SOURCE_MAPPING_DATA_URL_PREFIX = \ + '//# sourceMappingURL=data:application/json;base64,' + + +class MergeSourceMapsTest(unittest.TestCase): + def setUp(self): + self._out_folder = None + + def tearDown(self): + if self._out_folder: + shutil.rmtree(self._out_folder) + + def _translate(self, source_map, line, column): + """ Translates from post-transform to pre-transform using a source map. + + Translates a line and column in some hypothetical processed JavaScript + back into the hypothetical original line and column using the indicated + source map. Returns the pre-processed line and column. + """ + stdout = node.RunNode([ + str(_SOURCE_MAP_TRANSLATOR), "--source_map", source_map, "--line", + str(line), "--column", + str(column) + ]) + result = json.loads(stdout) + return result['line'], result['column'] + + def testMergingTwoSourcemaps(self): + """ Test that merging 2 inline sourcemaps results in line numbers and + columns that refer to positions in the original file. + + The file `original_file.ts` contains a mix of GRiT <if expr> that get + removed and some TS specific properties that get rewritten to JS. These have + been ran through both `create_js_source_maps` build rule AND tsc to provide + a generated file with 2 inline sourcemaps. + """ + assert not self._out_folder + self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) + original_file_name = 'original_file.ts' + input_file_name = (_HERE_DIR / 'generated_file_pre_merge.js').resolve() + output_file_name = (Path(self._out_folder) / "merged_maps.out").resolve() + node.RunNode([ + str(_SOURCE_MAP_MERGER), + '--sources', + str(input_file_name), + '--outputs', + str(output_file_name), + ]) + + source_map = None + with open(output_file_name, encoding='utf-8') as f: + merged_source_map_lines = f.readlines() + for line in merged_source_map_lines: + stripped_line = line.strip() + if stripped_line.startswith(_SOURCE_MAPPING_DATA_URL_PREFIX): + source_map = base64.b64decode( + stripped_line.replace(_SOURCE_MAPPING_DATA_URL_PREFIX, + '')).decode('utf-8') + break + else: + self.fail('Failed to identify a merged sourcemap') + + # The sources key must refer to the original file name. + self.assertEqual(original_file_name, json.loads(source_map)['sources'][0]) + + # Check various mappings. + # The first line should not have an equivalent mapping: + # "use strict"; + line, column = self._translate(source_map, 1, 1) + self.assertEqual(line, None) + + # The IIFE representing the enum should map to the line, this large jump in + # line numbers represents the GRiT <if expr> that has been removed. + line, column = self._translate(source_map, 7, 1) + self.assertEqual(line, 12) + + # Certain things remain the same, such as console.log and these should just + # get their line / column repointed to the new location. + line, column = self._translate(source_map, 27, 8) + self.assertEqual(line, 31) + self.assertEqual(column, 0) + + def testManifestFilesAreRewritten(self): + """ Tests that when `manifest-files` are supplied the results are rewritten + to reflect the updated files location. + + This is critical to ensure any files that undergo a merge (or move) are + appropriately rewritten and subsequent steps can rely on the correct merged + file instead of the previous unmerged one. + """ + assert not self._out_folder + self._out_folder = tempfile.mkdtemp(dir=_HERE_DIR) + + manifest_file_contents = b'{"base_dir":"tsc/ts_library_out"}' + input_fd, manifest_file = tempfile.mkstemp(dir=self._out_folder, + text=True, + suffix=".json") + os.write(input_fd, manifest_file_contents) + os.close(input_fd) + + original_file_name = 'original_file.ts' + input_file_name = (_HERE_DIR / 'generated_file_pre_merge.js').resolve() + output_file_name = (Path(self._out_folder) / "merged_maps.out").resolve() + node.RunNode([ + str(_SOURCE_MAP_MERGER), + '--sources', + str(input_file_name), + '--outputs', + str(output_file_name), + '--manifest-files', + str(manifest_file), + ]) + + manifest_file_contents = '{"base_dir":"tsc"}' + manifest_file_path = Path(manifest_file) + remapped_manifest_file = manifest_file_path.parent.joinpath( + str(manifest_file_path.stem) + '__processed' + + str(manifest_file_path.suffix)) + self.assertTrue(remapped_manifest_file.exists()) + with remapped_manifest_file.open(encoding='utf-8') as f: + self.assertEqual(f.read(), manifest_file_contents) + + +if __name__ == '__main__': + unittest.main()
diff --git a/tools/code_coverage/merge_js_source_maps/test/original_file.ts b/tools/code_coverage/merge_js_source_maps/test/original_file.ts new file mode 100644 index 0000000..faf6f2d2 --- /dev/null +++ b/tools/code_coverage/merge_js_source_maps/test/original_file.ts
@@ -0,0 +1,33 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// clang-format off + +// <if expr="is_macosx"> +function thisIsRemoved(): boolean { + return true; +} +// </if> + +// Enums aren't natively available in JS, this will ensure a rewritten TS +// sourcemap. +enum ExampleEnum { + SOME_EXAMPLE = 0, + OTHER_EXAMPLE = 1, +} + +abstract class SpecialTypeScriptProperties { + /* Private variables are typescript only specifiers */ + protected protectedValue: number = 0; + + abstract method(): number; +} + +class Derived extends SpecialTypeScriptProperties { + private privateValue: number = 0; + + method() { + console.log(this.privateValue); + return this.protectedValue; + } +}
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index d42d005..09aac1c 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -652,6 +652,8 @@ 'chromecast-linux-builder-perf': 'cast_binary_size', 'chromeos-amd64-generic-lacros-builder-perf': 'chromeos_amd64-generic_lacros_official', 'chromeos-arm-generic-lacros-builder-perf': 'chromeos_arm-generic_lacros_official', + 'fuchsia-builder-perf-arm64': 'official_goma_fuchsia_arm64_perf', + 'fuchsia-builder-perf-x64': 'official_goma_fuchsia_x64_perf', 'linux-builder-perf': 'official_goma_linux_perf', 'linux-builder-perf-pgo': 'official_goma_linux_perf_pgo', 'linux-builder-perf-rel': 'official_goma_linux_perf', @@ -673,7 +675,6 @@ 'android_arm64-cfi-builder-perf-fyi': 'official_goma_minimal_symbols_android_thin_lto_opt_arm64', 'chromeos-kevin-builder-perf-fyi': 'chromeos_kevin_include_unwind_tables_official', 'fuchsia-builder-perf-fyi': 'official_goma_fuchsia_arm64_perf', - 'fuchsia-builder-perf-x64': 'official_goma_fuchsia_x64_perf', }, 'chromium.reclient.fyi': {
diff --git a/tools/mb/mb_config_expectations/chromium.perf.fyi.json b/tools/mb/mb_config_expectations/chromium.perf.fyi.json index ea89105b..0476fd4 100644 --- a/tools/mb/mb_config_expectations/chromium.perf.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.perf.fyi.json
@@ -56,21 +56,5 @@ "test_isolate_uses_emulator": false, "use_goma": true } - }, - "fuchsia-builder-perf-x64": { - "gn_args": { - "ffmpeg_branding": "Chrome", - "fuchsia_additional_boot_images": [ - "//third_party/fuchsia-sdk/images-internal/chromebook-x64-release/" - ], - "is_chrome_branded": true, - "is_official_build": true, - "proprietary_codecs": true, - "symbol_level": 1, - "target_cpu": "x64", - "target_os": "fuchsia", - "test_isolate_uses_emulator": false, - "use_goma": true - } } } \ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/chromium.perf.json b/tools/mb/mb_config_expectations/chromium.perf.json index c877f7bc..f193ff06 100644 --- a/tools/mb/mb_config_expectations/chromium.perf.json +++ b/tools/mb/mb_config_expectations/chromium.perf.json
@@ -124,6 +124,39 @@ "use_thin_lto": true } }, + "fuchsia-builder-perf-arm64": { + "gn_args": { + "ffmpeg_branding": "Chrome", + "fuchsia_additional_boot_images": [ + "//third_party/fuchsia-sdk/images-internal/astro-release/", + "//third_party/fuchsia-sdk/images-internal/sherlock-release/" + ], + "is_chrome_branded": true, + "is_official_build": true, + "proprietary_codecs": true, + "symbol_level": 1, + "target_cpu": "arm64", + "target_os": "fuchsia", + "test_isolate_uses_emulator": false, + "use_goma": true + } + }, + "fuchsia-builder-perf-x64": { + "gn_args": { + "ffmpeg_branding": "Chrome", + "fuchsia_additional_boot_images": [ + "//third_party/fuchsia-sdk/images-internal/chromebook-x64-release/" + ], + "is_chrome_branded": true, + "is_official_build": true, + "proprietary_codecs": true, + "symbol_level": 1, + "target_cpu": "x64", + "target_os": "fuchsia", + "test_isolate_uses_emulator": false, + "use_goma": true + } + }, "linux-builder-perf": { "gn_args": { "chrome_pgo_phase": 0,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 3fe69a6..37c5f08 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -4366,6 +4366,13 @@ <int value="39" label="Disconnect host VPN"/> </enum> +<enum name="ArcNotificationExpandState"> + <int value="0" label="Fixed size"/> + <int value="1" label="Expandable"/> + <int value="2" label="Expanded"/> + <int value="3" label="Collapsed"/> +</enum> + <enum name="ArcNotificationStyle"> <int value="0" label="No Style"/> <int value="1" label="Big Picture Style"/>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml index c5c4b28..c60ab00 100644 --- a/tools/metrics/histograms/metadata/apps/histograms.xml +++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -1653,16 +1653,6 @@ </summary> </histogram> -<histogram name="Apps.AppListSearchCommenced" units="searches" - expires_after="2022-12-01"> - <owner>yulunwu@chromium.org</owner> - <owner>tapted@chromium.org</owner> - <summary> - The number of searches that are started in the app list. This is gathered - each time the app list search box transitions from empty to non-empty. - </summary> -</histogram> - <histogram name="Apps.AppListSearchQueryLength" units="characters" expires_after="2022-08-01"> <obsolete>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index 0c9f1d9..166f7e0 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1392,6 +1392,20 @@ </summary> </histogram> +<histogram name="Arc.Notifications.ExpandState" + enum="ArcNotificationExpandState" expires_after="2022-10-05"> + <owner>yaoqq@google.com</owner> + <owner>arc-framework@google.com</owner> + <summary> + Records the expand state of an Arc notification, the status of + fixed_size/expandable is logged when the notification is created, while the + status of expanded/collapsed is logged when the user expands/collapses it. + Need to note that if it's a grouped notification we are tracking the state + of the summary(top level) notification of the group, since we can't easily + acquire the state for the child notifications of the group. + </summary> +</histogram> + <histogram name="Arc.Notifications.InlineReplyEnabled" enum="BooleanEnabled" expires_after="2022-10-07"> <owner>yaoqq@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml index cdf2b66..4ac6ee4 100644 --- a/tools/metrics/histograms/metadata/browser/histograms.xml +++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -99,6 +99,7 @@ <variant name=".Audio"/> <variant name=".Ent"/> <variant name=".General"/> + <variant name=".MediaAppPdf"/> <variant name=".OnboardingExperience"/> <variant name=".Performance"/> <variant name=".PersonalizationAvatar"/>
diff --git a/tools/metrics/histograms/metadata/input/histograms.xml b/tools/metrics/histograms/metadata/input/histograms.xml index 8e09cc1f..2f8abea 100644 --- a/tools/metrics/histograms/metadata/input/histograms.xml +++ b/tools/metrics/histograms/metadata/input/histograms.xml
@@ -112,7 +112,7 @@ </histogram> <histogram name="InputMethod.Assistive.Disabled.Emoji" - enum="IMEAssistiveDisabledReason" expires_after="2022-05-01"> + enum="IMEAssistiveDisabledReason" expires_after="2022-12-04"> <owner>myy@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary> @@ -277,7 +277,7 @@ </histogram> <histogram name="InputMethod.Assistive.TimeToAccept.MultiWord" units="ms" - expires_after="2022-06-09"> + expires_after="2022-12-04"> <owner>curtismcmullan@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary> @@ -314,7 +314,7 @@ </histogram> <histogram name="InputMethod.Assistive.TimeToDismiss.MultiWord" units="ms" - expires_after="2022-06-09"> + expires_after="2022-12-04"> <owner>curtismcmullan@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary> @@ -338,7 +338,7 @@ </histogram> <histogram name="InputMethod.Assistive.UserPref.Emoji" enum="BooleanEnabled" - expires_after="2022-05-01"> + expires_after="2022-12-04"> <owner>myy@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary> @@ -349,7 +349,7 @@ </histogram> <histogram name="InputMethod.Assistive.UserPref.MultiWord" - enum="BooleanEnabled" expires_after="2022-06-05"> + enum="BooleanEnabled" expires_after="2022-12-04"> <owner>curtismcmullan@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary> @@ -361,7 +361,7 @@ </histogram> <histogram name="InputMethod.Assistive.UserPref.PersonalInfo" - enum="BooleanEnabled" expires_after="2021-10-04"> + enum="BooleanEnabled" expires_after="2022-12-04"> <owner>myy@google.com</owner> <owner>essential-inputs-team@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml index 48edb2e..f2ff850f 100644 --- a/tools/metrics/histograms/metadata/net/histograms.xml +++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -626,6 +626,18 @@ </summary> </histogram> +<histogram name="Net.DNS.DnsConfig.Nsswitch.NisServiceInHosts" + enum="BooleanIncluded" expires_after="2022-11-27"> + <owner>horo@chromium.org</owner> + <owner>src/net/OWNERS</owner> + <summary> + Whether or not NIS service is registered in the "hosts:" database + of an nsswitch.conf. Recorded when a resolv.conf file is successfully read + by Chrome and the DNS configuration read from the resolv.conf file is + considered compatible with Chrome. + </summary> +</histogram> + <histogram name="Net.DNS.DnsConfig.Nsswitch.NumServices" units="#services" expires_after="2022-01-15"> <owner>ericorth@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml index 48813a3..2c1c65f 100644 --- a/tools/metrics/histograms/metadata/others/histograms.xml +++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -5243,7 +5243,7 @@ </histogram> <histogram name="Drive.PushNotificationInitiallyEnabled" enum="BooleanEnabled" - expires_after="2022-09-18"> + expires_after="M116"> <owner>simmonsjosh@google.com</owner> <owner>src/ui/file_manager/OWNERS</owner> <summary> @@ -10642,7 +10642,7 @@ </histogram> <histogram name="ReadingList.BookmarkBarState.On{Frequency}AddToReadingList" - enum="BookmarkBarState" expires_after="2022-08-20"> + enum="BookmarkBarState" expires_after="2022-11-20"> <owner>corising@chromium.org</owner> <owner>chrome-desktop-ui-sea@google.com</owner> <summary> @@ -10725,7 +10725,7 @@ </histogram> <histogram name="ReadingList.SyncStateMatchesBookmarks" enum="BooleanMatched" - expires_after="2022-08-20"> + expires_after="2022-11-20"> <owner>corising@chromium.org</owner> <owner>chrome-desktop-ui-sea@google.com</owner> <summary> @@ -10751,7 +10751,7 @@ </histogram> <histogram name="ReadingList.WebUI.InitialEntriesRenderTime" units="ms" - expires_after="2022-08-20"> + expires_after="2022-11-20"> <owner>corising@chromium.org</owner> <owner>chrome-desktop-ui-sea@google.com</owner> <summary> @@ -10798,7 +10798,7 @@ </histogram> <histogram name="ReadingList.WindowDisplayedDuration" units="ms" - expires_after="2022-08-21"> + expires_after="2022-11-21"> <owner>corising@chromium.org</owner> <owner>chrome-desktop-ui-sea@google.com</owner> <summary> @@ -10810,7 +10810,7 @@ </histogram> <histogram name="ReadingList.{ReadStatus}.Count.{Variation}" units="count" - expires_after="2022-08-20"> + expires_after="2022-11-20"> <owner>corising@chromium.org</owner> <owner>chrome-desktop-ui-sea@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml index 81f9188a..ba1e3a2cc 100644 --- a/tools/metrics/histograms/metadata/power/histograms.xml +++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -665,23 +665,29 @@ </token> </histogram> -<histogram name="Power.BacklightLevelOnAC" units="%" expires_after="2023-05-12"> +<histogram name="Power.BacklightLevel{PrivacyScreenState}{PowerSource}" + units="%" expires_after="2023-05-12"> <owner>puthik@chromium.org</owner> + <owner>mqg@chromium.org</owner> <owner>chromeos-platform-power@google.com</owner> <summary> - The level of the backlight as a percentage when the user is on AC. Sampled - every 30 seconds. Chrome OS only. - </summary> -</histogram> - -<histogram name="Power.BacklightLevelOnBattery" units="%" - expires_after="2023-05-12"> - <owner>puthik@chromium.org</owner> - <owner>chromeos-platform-power@google.com</owner> - <summary> - The level of the backlight as a percentage when the user is on battery. + The level of the backlight as a percentage when the privacy screen is in + {PrivacyScreenState} state and the device is powered by {PowerSource}. Sampled every 30 seconds. Chrome OS only. </summary> + <token key="PrivacyScreenState"> + <variant name="" + summary="All Chrome OS, irrespective of whether privacy screens are + present, or privacy screen state."/> + <variant name="PrivacyScreenDisabled" + summary="Chrome OS with privacy screens only."/> + <variant name="PrivacyScreenEnabled" + summary="Chrome OS with privacy screens only."/> + </token> + <token key="PowerSource"> + <variant name="OnAC"/> + <variant name="OnBattery"/> + </token> </histogram> <histogram name="Power.BatteryChargeHealth" units="%" @@ -1031,7 +1037,7 @@ <histogram name="Power.DarkMode{prefers-color-scheme}.InferredDarkPageColorScheme" - enum="BooleanIsDarkColorScheme" expires_after="2022-09-24"> + enum="BooleanIsDarkColorScheme" expires_after="2023-09-24"> <owner>michaelbai@chromium.org</owner> <owner>pdr@chromium.org</owner> <owner>peter@chromium.org</owner>
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 94d8843..c4613c1 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -331,8 +331,10 @@ std::vector<std::string> names; for (const auto& node_id : ids) { if (AXPlatformNode* header = delegate->GetFromNodeID(node_id)) { - if (AtkObject* atk_header = header->GetNativeViewAccessible()) - names.push_back(atk_object_get_name(atk_header)); + if (AtkObject* atk_header = header->GetNativeViewAccessible()) { + if (const gchar* name = atk_object_get_name(atk_header)) + names.push_back(name); + } } }
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc index 3de50f05..f842d40 100644 --- a/ui/accessibility/platform/ax_platform_node_base.cc +++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -194,6 +194,7 @@ name += extra_text; } + DCHECK(base::IsStringUTF8AllowingNoncharacters(name)) << "Invalid UTF8"; return name; } return std::string();
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm index 426b6ce73..3abaab3 100644 --- a/ui/accessibility/platform/ax_platform_node_cocoa.mm +++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -685,16 +685,22 @@ } - (BOOL)isImage { - bool isImage = + bool has_image_semantics = ui::IsImage(_node->GetRole()) && - !_node->GetBoolAttribute(ax::mojom::BoolAttribute::kCanvasHasFallback); - DCHECK(!([[self accessibilityRole] isEqualToString:NSAccessibilityImageRole] ^ - isImage)) - << "Internal and native roles do not match when determining if this " - "object is an image. " - << "Chrome role: " << ui::ToString(_node->GetRole()) - << ", NSAccessibility role: " << [self accessibilityRole]; - return isImage; + !_node->GetBoolAttribute(ax::mojom::BoolAttribute::kCanvasHasFallback) && + !_node->GetChildCount() && + _node->GetNameFrom() != ax::mojom::NameFrom::kAttributeExplicitlyEmpty; +#if DCHECK_IS_ON() + bool is_native_image = + [[self accessibilityRole] isEqualToString:NSAccessibilityImageRole]; + DCHECK_EQ(is_native_image, has_image_semantics) + << "\nPresence/lack of native image role do not match the expected " + "internal semantics:" + << "\n* Chrome role: " << ui::ToString(_node->GetRole()) + << "\n* NSAccessibility role: " << [self accessibilityRole] + << "\n* AXNode: " << *_node; +#endif + return has_image_semantics; } - (NSString*)getName {
diff --git a/ui/display/manager/json_converter.cc b/ui/display/manager/json_converter.cc index ae123b2..94bb90a 100644 --- a/ui/display/manager/json_converter.cc +++ b/ui/display/manager/json_converter.cc
@@ -184,12 +184,7 @@ return AddLegacyValuesFromValue(dict, layout); } -bool DisplayLayoutToJson(const DisplayLayout& layout, base::Value* value) { - if (!value->is_dict()) - return false; - - base::Value::Dict& dict = value->GetDict(); - +void DisplayLayoutToJson(const DisplayLayout& layout, base::Value::Dict& dict) { dict.Set(kDefaultUnifiedKey, layout.default_unified); dict.Set(kPrimaryIdKey, base::NumberToString(layout.primary_id)); @@ -206,8 +201,6 @@ placement_list.Append(std::move(placement_value)); } dict.Set(kDisplayPlacementKey, std::move(placement_list)); - - return true; } } // namespace display
diff --git a/ui/display/manager/json_converter.h b/ui/display/manager/json_converter.h index ccf9253..90c8d1c0 100644 --- a/ui/display/manager/json_converter.h +++ b/ui/display/manager/json_converter.h
@@ -19,8 +19,9 @@ DISPLAY_MANAGER_EXPORT bool JsonToDisplayLayout(const base::Value& value, DisplayLayout* layout); -DISPLAY_MANAGER_EXPORT bool DisplayLayoutToJson(const DisplayLayout& layout, - base::Value* value); +// This will modify `dict` in place. +DISPLAY_MANAGER_EXPORT void DisplayLayoutToJson(const DisplayLayout& layout, + base::Value::Dict& dict); } // namespace display
diff --git a/ui/display/manager/json_converter_unittest.cc b/ui/display/manager/json_converter_unittest.cc index b6aabff3..4b36613a 100644 --- a/ui/display/manager/json_converter_unittest.cc +++ b/ui/display/manager/json_converter_unittest.cc
@@ -28,8 +28,8 @@ layout.placement_list[1].position = DisplayPlacement::LEFT; layout.placement_list[1].offset = 30; - base::Value value(base::Value::Type::DICTIONARY); - DisplayLayoutToJson(layout, &value); + base::Value::Dict value; + DisplayLayoutToJson(layout, value); const char data[] = "{\n" @@ -51,7 +51,7 @@ ASSERT_TRUE(result.has_value()) << result.error().message << " at " << result.error().line << ":" << result.error().column; - EXPECT_EQ(value, *result); + EXPECT_EQ(base::Value(std::move(value)), *result); DisplayLayout read_layout; EXPECT_TRUE(JsonToDisplayLayout(*result, &read_layout));
diff --git a/ui/file_manager/file_manager/background/js/drive_sync_handler.js b/ui/file_manager/file_manager/background/js/drive_sync_handler.js index 0d96dad..83d9b21c 100644 --- a/ui/file_manager/file_manager/background/js/drive_sync_handler.js +++ b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
@@ -350,6 +350,8 @@ break; case 'no_server_space': item.message = strf('SYNC_NO_SERVER_SPACE'); + item.learnMoreLink = str('GOOGLE_DRIVE_MANAGE_STORAGE_URL'); + // This error will reappear every time sync is retried, so we use // a fixed ID to avoid spamming the user. item.id = DriveSyncHandlerImpl.DRIVE_SYNC_ERROR_PREFIX + @@ -357,6 +359,8 @@ break; case 'no_server_space_organization': item.message = strf('SYNC_NO_SERVER_SPACE_ORGANIZATION'); + item.learnMoreLink = str('GOOGLE_DRIVE_MANAGE_STORAGE_URL'); + // This error will reappear every time sync is retried, so we use // a fixed ID to avoid spamming the user. item.id = DriveSyncHandlerImpl.DRIVE_SYNC_ERROR_ORGANIZATION_PREFIX +
diff --git a/ui/file_manager/file_manager/common/js/progress_center_common.js b/ui/file_manager/file_manager/common/js/progress_center_common.js index c56ffdf..1ce46f9 100644 --- a/ui/file_manager/file_manager/common/js/progress_center_common.js +++ b/ui/file_manager/file_manager/common/js/progress_center_common.js
@@ -132,6 +132,13 @@ * @type {number} */ this.remainingTime; + + /** + * Link to be opened when users click the "Learn more" button. + * The "Learn more" button won't be displayed if this is falsy. + * @type {?string} + */ + this.learnMoreLink; } /**
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_button.html b/ui/file_manager/file_manager/foreground/elements/xf_button.html index b0faa56c..f37dc4a 100644 --- a/ui/file_manager/file_manager/foreground/elements/xf_button.html +++ b/ui/file_manager/file_manager/foreground/elements/xf_button.html
@@ -57,7 +57,7 @@ position: relative; } - :host(:not([data-category='dismiss'])) { + :host(:not([data-category='dismiss']):not([data-category='learn-more'])) { width: 36px; } @@ -65,9 +65,26 @@ display: none; } - :host(:not([data-category='dismiss'])) #dismiss { + :host([data-category='learn-more']) #icon { display: none; } + + #dismiss { + display: none; + } + + #learn-more { + display: none; + } + + :host([data-category='dismiss']) #dismiss { + display: unset; + } + + :host([data-category='learn-more']) #learn-more { + display: unset; + } </style> <cr-button id='dismiss'>$i18n{DRIVE_WELCOME_DISMISS}</cr-button> +<cr-button id='learn-more'>$i18n{LEARN_MORE_LABEL}</cr-button> <cr-icon-button id='icon'></cr-icon-button>
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.html b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.html index 51bbb53..ee1b2f1 100644 --- a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.html +++ b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.html
@@ -1,6 +1,6 @@ <style> :host { - max-width: 400px; + max-width: 504px; outline: none; } #container { @@ -43,24 +43,24 @@ 75% { max-height: calc(192px + 28px); opacity: 0; - width: 400px; + width: 504px; } 100% { max-height: calc(192px + 28px); opacity: 1; - width: 400px; + width: 504px; } } @keyframes setexpand { 0% { max-height: calc(192px + 28px); - max-width: 400px; + max-width: 504px; opacity: 1; } 25% { max-height: calc(192px + 28px); - max-width: 400px; + max-width: 504px; opacity: 0; } 100% { @@ -71,7 +71,7 @@ } .expanded { animation: setcollapse 200ms forwards; - width: 400px; + width: 504px; } .collapsed { animation: setexpand 200ms forwards; @@ -83,7 +83,7 @@ max-height: calc(192px + 28px); opacity: 1; overflow-y: auto; - width: 400px; + width: 504px; } xf-panel-item:not(:only-child) { --multi-progress-height: 92px;
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.html b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.html index cbb90ca..4b63ce0 100644 --- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.html +++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.html
@@ -6,7 +6,7 @@ display: flex; flex-direction: row; height: 68px; - width: 400px; + width: 504px; } .xf-button {
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js index 3eceaeb..adf2783 100644 --- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js +++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
@@ -157,6 +157,12 @@ secondaryButton.onclick = assert(this.onclick); secondaryButton.dataset.category = 'dismiss'; buttonSpacer.insertAdjacentElement('afterend', secondaryButton); + if (this.dataset.learnMoreLink) { + primaryButton = document.createElement('xf-button'); + primaryButton.id = 'primary-action'; + primaryButton.dataset.category = 'learn-more'; + buttonSpacer.insertAdjacentElement('afterend', primaryButton); + } break; case this.panelTypeInfo: break;
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents.js b/ui/file_manager/file_manager/foreground/js/directory_contents.js index 78ef8c7..95d788f 100644 --- a/ui/file_manager/file_manager/foreground/js/directory_contents.js +++ b/ui/file_manager/file_manager/foreground/js/directory_contents.js
@@ -683,39 +683,16 @@ * @public {!Array<string>} * @const */ - this.prefetchPropertyNames = FileListContext.createPrefetchPropertyNames_(); + this.prefetchPropertyNames = Array.from(new Set([ + ...constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES, + ...constants.ACTIONS_MODEL_METADATA_PREFETCH_PROPERTY_NAMES, + ...constants.FILE_SELECTION_METADATA_PREFETCH_PROPERTY_NAMES, + ...constants.DLP_METADATA_PREFETCH_PROPERTY_NAMES, + ])); /** @public {!VolumeManager} */ this.volumeManager = volumeManager; } - - /** - * @return {!Array<string>} - * @private - */ - static createPrefetchPropertyNames_() { - const set = {}; - for (let i = 0; - i < constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES.length; - i++) { - set[constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES[i]] = true; - } - for (let i = 0; - i < constants.ACTIONS_MODEL_METADATA_PREFETCH_PROPERTY_NAMES.length; - i++) { - set[constants.ACTIONS_MODEL_METADATA_PREFETCH_PROPERTY_NAMES[i]] = true; - } - for (let i = 0; - i < constants.FILE_SELECTION_METADATA_PREFETCH_PROPERTY_NAMES.length; - i++) { - set[constants.FILE_SELECTION_METADATA_PREFETCH_PROPERTY_NAMES[i]] = true; - } - for (let i = 0; i < constants.DLP_METADATA_PREFETCH_PROPERTY_NAMES.length; - i++) { - set[constants.DLP_METADATA_PREFETCH_PROPERTY_NAMES[i]] = true; - } - return Object.keys(set); - } } /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js index 3d13a06d..e57f51f33 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js +++ b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
@@ -310,10 +310,11 @@ panelItem.signalCallback = (signal) => { if (signal === 'cancel' && item.cancelCallback) { item.cancelCallback(); - } - if (signal === 'dismiss') { + } else if (signal === 'dismiss') { this.feedbackHost_.removePanelItem(panelItem); this.dismissErrorItemCallback(item.id); + } else if (signal === 'learn-more') { + window.open(item.learnMoreLink, '_blank'); } }; panelItem.progress = item.progressRateInPercent.toString(); @@ -350,6 +351,9 @@ this.feedbackHost_.removePanelItem(panelItem); break; case ProgressItemState.ERROR: + if (item.learnMoreLink) { + panelItem.dataset.learnMoreLink = item.learnMoreLink; + } panelItem.panelType = panelItem.panelTypeError; panelItem.primaryText = item.message; panelItem.secondaryText = '';
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js index bf9ca1c..436c998 100644 --- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js +++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
@@ -40,68 +40,93 @@ * When using iron-icon's, more than one icon can be specified by setting * the |ironIcon| property to a comma-delimited list of keys. */ -import {Polymer, html} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import '../shared_vars_css.m.js'; import '//resources/polymer/v3_0/iron-icon/iron-icon.js'; -Polymer({ - is: 'cr-icon-button', +import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; - _template: html`{__html_template__}`, +export class CrIconButtonElement extends PolymerElement { + static get is() { + return 'cr-icon-button'; + } - properties: { - disabled: { - type: Boolean, - value: false, - reflectToAttribute: true, - observer: 'disabledChanged_', - }, + static get template() { + return html`{__html_template__}`; + } + static get properties() { + return { + disabled: { + type: Boolean, + value: false, + reflectToAttribute: true, + observer: 'disabledChanged_', + }, + + /** + * Use this property in order to configure the "tabindex" attribute. + */ + customTabIndex: { + type: Number, + observer: 'applyTabIndex_', + }, + + ironIcon: { + type: String, + observer: 'onIronIconChanged_', + reflectToAttribute: true, + }, + + /** @private */ + multipleIcons_: { + type: Boolean, + reflectToAttribute: true, + }, + }; + } + + constructor() { + super(); /** - * Use this property in order to configure the "tabindex" attribute. + * It is possible to activate a tab when the space key is pressed down. When + * this element has focus, the keyup event for the space key should not + * perform a 'click'. |spaceKeyDown_| tracks when a space pressed and + * handled by this element. Space keyup will only result in a 'click' when + * |spaceKeyDown_| is true. |spaceKeyDown_| is set to false when element + * loses focus. + * @private {boolean} */ - customTabIndex: { - type: Number, - observer: 'applyTabIndex_', - }, + this.spaceKeyDown_ = false; + } - ironIcon: { - type: String, - observer: 'onIronIconChanged_', - reflectToAttribute: true, - }, + /** @override */ + ready() { + super.ready(); + this.setAttribute('aria-disabled', 'false'); + if (!this.hasAttribute('role')) { + this.setAttribute('role', 'button'); + } + if (!this.hasAttribute('tabindex')) { + this.setAttribute('tabindex', '0'); + } - /** @private */ - multipleIcons_: { - type: Boolean, - reflectToAttribute: true, - }, - }, + this.addEventListener('blur', this.onBlur_.bind(this)); + this.addEventListener('click', this.onClick_.bind(this)); + this.addEventListener( + 'keydown', e => this.onKeyDown_(/** @type {!KeyboardEvent} */ (e))); + this.addEventListener( + 'keyup', e => this.onKeyUp_(/** @type {!KeyboardEvent} */ (e))); + } - hostAttributes: { - 'aria-disabled': 'false', - role: 'button', - tabindex: 0, - }, - - listeners: { - blur: 'onBlur_', - click: 'onClick_', - keydown: 'onKeyDown_', - keyup: 'onKeyUp_', - }, - - /** - * It is possible to activate a tab when the space key is pressed down. When - * this element has focus, the keyup event for the space key should not - * perform a 'click'. |spaceKeyDown_| tracks when a space pressed and handled - * by this element. Space keyup will only result in a 'click' when - * |spaceKeyDown_| is true. |spaceKeyDown_| is set to false when element loses - * focus. - * @private {boolean} - */ - spaceKeyDown_: false, + /** @param {string} className */ + toggleClass(className) { + if (this.classList.contains(className)) { + this.classList.remove(className); + } else { + this.classList.add(className); + } + } /** * @param {boolean} newValue @@ -117,7 +142,7 @@ } this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false'); this.applyTabIndex_(); - }, + } /** * Updates the tabindex HTML attribute to the actual value. @@ -129,12 +154,12 @@ value = this.disabled ? -1 : 0; } this.setAttribute('tabindex', value); - }, + } /** @private */ onBlur_() { this.spaceKeyDown_ = false; - }, + } /** * @param {!Event} e @@ -144,7 +169,7 @@ if (this.disabled) { e.stopImmediatePropagation(); } - }, + } /** @private */ onIronIconChanged_() { @@ -163,7 +188,7 @@ .forEach(child => child.setAttribute('role', 'none')); } }); - }, + } /** * @param {!KeyboardEvent} e @@ -185,7 +210,7 @@ } else if (e.key === ' ') { this.spaceKeyDown_ = true; } - }, + } /** * @param {!KeyboardEvent} e @@ -201,5 +226,7 @@ this.spaceKeyDown_ = false; this.click(); } - }, -}); + } +} + +customElements.define(CrIconButtonElement.is, CrIconButtonElement);
diff --git a/weblayer/browser/browser_controls_navigation_state_handler.cc b/weblayer/browser/browser_controls_navigation_state_handler.cc index afe0790a..77eb497 100644 --- a/weblayer/browser/browser_controls_navigation_state_handler.cc +++ b/weblayer/browser/browser_controls_navigation_state_handler.cc
@@ -74,9 +74,7 @@ void BrowserControlsNavigationStateHandler::DidFinishLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url) { - const bool is_main_frame = - render_frame_host->GetMainFrame() == render_frame_host; - if (is_main_frame) + if (render_frame_host->IsInPrimaryMainFrame()) ScheduleStopDelayedForceShow(); } @@ -84,12 +82,8 @@ content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code) { - const bool is_main_frame = - render_frame_host->GetMainFrame() == render_frame_host; - if (is_main_frame) + if (render_frame_host->IsInPrimaryMainFrame()) { ScheduleStopDelayedForceShow(); - if (render_frame_host->IsActive() && - (render_frame_host == web_contents()->GetPrimaryMainFrame())) { UpdateState(); } }
diff --git a/weblayer/browser/browser_controls_navigation_state_handler_browsertest.cc b/weblayer/browser/browser_controls_navigation_state_handler_browsertest.cc new file mode 100644 index 0000000..10ba2356 --- /dev/null +++ b/weblayer/browser/browser_controls_navigation_state_handler_browsertest.cc
@@ -0,0 +1,80 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/browser/web_contents.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "weblayer/browser/browser_controls_navigation_state_handler.h" +#include "weblayer/browser/browser_controls_navigation_state_handler_delegate.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +namespace weblayer { + +class BrowserConrolsNavigationStateHandlerBrowserTest + : public WebLayerBrowserTest { + public: + BrowserConrolsNavigationStateHandlerBrowserTest() = default; + ~BrowserConrolsNavigationStateHandlerBrowserTest() override = default; + + // WebLayerBrowserTest: + void SetUpOnMainThread() override { + WebLayerBrowserTest::SetUpOnMainThread(); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + content::WebContents* web_contents() { + return static_cast<TabImpl*>(shell()->tab())->web_contents(); + } + + private: +}; + +class TestBrowserControlsNavigationStateHandlerDelegate + : public BrowserControlsNavigationStateHandlerDelegate { + public: + // BrowserControlsNavigationStateHandlerDelegate: + void OnBrowserControlsStateStateChanged( + ControlsVisibilityReason reason, + cc::BrowserControlsState state) override { + state_ = state; + if (quit_callback_) + std::move(quit_callback_).Run(); + } + void OnUpdateBrowserControlsStateBecauseOfProcessSwitch( + bool did_commit) override {} + + void WaitForStateChanged() { + base::RunLoop run_loop; + quit_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + cc::BrowserControlsState state() { return state_; } + + private: + base::OnceClosure quit_callback_; + cc::BrowserControlsState state_ = cc::BrowserControlsState::kBoth; +}; + +// Tests that BrowserConrolsNavigationStateHandler informs that the status is +// updated according to navigation progress. +IN_PROC_BROWSER_TEST_F(BrowserConrolsNavigationStateHandlerBrowserTest, Basic) { + TestBrowserControlsNavigationStateHandlerDelegate test_delegate; + BrowserControlsNavigationStateHandler + browser_controls_navigation_state_handler(web_contents(), &test_delegate); + GURL test_url(embedded_test_server()->GetURL("/simple_page.html")); + NavigateAndWaitForStart(test_url, shell()->tab()); + // `test_delegate` should get the status is updated to `kShown` on + // DidStartNavigation(); + EXPECT_EQ(test_delegate.state(), cc::BrowserControlsState::kShown); + test_delegate.WaitForStateChanged(); + // `test_delegate` should get the status is updated to `kBoth` on + // DidFinishLoad(); + EXPECT_EQ(web_contents()->GetLastCommittedURL(), test_url); + EXPECT_EQ(test_delegate.state(), cc::BrowserControlsState::kBoth); +} + +} // namespace weblayer
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java index b5d8ae6..175d910 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java
@@ -78,27 +78,27 @@ * resources. This is useful when {@link Icon} is not available. */ private int getFallbackAndroidResource(int appResourceId) { - if (appResourceId == R.drawable.ic_play_arrow_white_36dp) { + if (appResourceId == R.drawable.ic_play_arrow_white_24dp) { return android.R.drawable.ic_media_play; } - if (appResourceId == R.drawable.ic_pause_white_36dp) { + if (appResourceId == R.drawable.ic_pause_white_24dp) { return android.R.drawable.ic_media_pause; } - if (appResourceId == R.drawable.ic_stop_white_36dp) { + if (appResourceId == R.drawable.ic_stop_white_24dp) { // There's no ic_media_stop. This standin is at least a square. In practice this // shouldn't ever come up as stop is only used in (Chrome) cast notifications. return android.R.drawable.checkbox_off_background; } - if (appResourceId == R.drawable.ic_skip_previous_white_36dp) { + if (appResourceId == R.drawable.ic_skip_previous_white_24dp) { return android.R.drawable.ic_media_previous; } - if (appResourceId == R.drawable.ic_skip_next_white_36dp) { + if (appResourceId == R.drawable.ic_skip_next_white_24dp) { return android.R.drawable.ic_media_next; } - if (appResourceId == R.drawable.ic_fast_forward_white_36dp) { + if (appResourceId == R.drawable.ic_fast_forward_white_24dp) { return android.R.drawable.ic_media_ff; } - if (appResourceId == R.drawable.ic_fast_rewind_white_36dp) { + if (appResourceId == R.drawable.ic_fast_rewind_white_24dp) { return android.R.drawable.ic_media_rew; } if (appResourceId == R.drawable.audio_playing) {
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn index 91c7716..a842c0c 100644 --- a/weblayer/public/java/BUILD.gn +++ b/weblayer/public/java/BUILD.gn
@@ -182,8 +182,10 @@ "org/chromium/browserfragment/interfaces/IBrowserSandboxCallback.aidl", "org/chromium/browserfragment/interfaces/IBrowserSandboxService.aidl", "org/chromium/browserfragment/interfaces/IRequestNavigationCallback.aidl", + "org/chromium/browserfragment/interfaces/ITabCallback.aidl", "org/chromium/browserfragment/interfaces/ITabNavigationControllerProxy.aidl", "org/chromium/browserfragment/interfaces/ITabObserverDelegate.aidl", + "org/chromium/browserfragment/interfaces/ITabParams.aidl", "org/chromium/browserfragment/interfaces/ITabProxy.aidl", ] } @@ -227,6 +229,7 @@ "org/chromium/weblayer/BrowserFragmentTabDelegate.java", "org/chromium/weblayer/BrowserSandboxService.java", "org/chromium/weblayer/TabNavigationControllerProxy.java", + "org/chromium/weblayer/TabParams.java", "org/chromium/weblayer/TabProxy.java", ] resources_package = "org.chromium.weblayer"
diff --git a/weblayer/public/java/org/chromium/browserfragment/Tab.java b/weblayer/public/java/org/chromium/browserfragment/Tab.java index 598820d..b14825d 100644 --- a/weblayer/public/java/org/chromium/browserfragment/Tab.java +++ b/weblayer/public/java/org/chromium/browserfragment/Tab.java
@@ -8,6 +8,7 @@ import androidx.annotation.NonNull; +import org.chromium.browserfragment.interfaces.ITabParams; import org.chromium.browserfragment.interfaces.ITabProxy; /** @@ -17,15 +18,20 @@ private ITabProxy mTabProxy; private TabNavigationController mTabNavigationController; - Tab(ITabProxy tabProxy) { - mTabProxy = tabProxy; + private String mGuid; - try { - mTabNavigationController = - new TabNavigationController(mTabProxy.getNavigationController()); - } catch (RemoteException e) { - // TODO(swestphal): Raise exception. - } + Tab(@NonNull ITabParams tabParams) { + assert tabParams.tabProxy != null; + assert tabParams.tabGuid != null; + assert tabParams.navigationControllerProxy != null; + + mTabProxy = tabParams.tabProxy; + mGuid = tabParams.tabGuid; + mTabNavigationController = new TabNavigationController(tabParams.navigationControllerProxy); + } + + public String getGuid() { + return mGuid; } /** @@ -47,4 +53,17 @@ public TabNavigationController getNavigationController() { return mTabNavigationController; } + + @Override + public int hashCode() { + return mGuid.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof Tab) { + return this == obj || mGuid.equals(((Tab) obj).getGuid()); + } + return false; + } }
diff --git a/weblayer/public/java/org/chromium/browserfragment/TabManager.java b/weblayer/public/java/org/chromium/browserfragment/TabManager.java index c6be8e2..1b57996 100644 --- a/weblayer/public/java/org/chromium/browserfragment/TabManager.java +++ b/weblayer/public/java/org/chromium/browserfragment/TabManager.java
@@ -6,10 +6,15 @@ import android.os.RemoteException; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.concurrent.futures.CallbackToFutureAdapter; + +import com.google.common.util.concurrent.ListenableFuture; import org.chromium.browserfragment.interfaces.IBrowserFragmentDelegate; -import org.chromium.browserfragment.interfaces.ITabProxy; +import org.chromium.browserfragment.interfaces.ITabCallback; +import org.chromium.browserfragment.interfaces.ITabParams; /** * Class for interaction with Browser Tabs. @@ -20,24 +25,43 @@ public class TabManager { private IBrowserFragmentDelegate mDelegate; + private final class TabCallback extends ITabCallback.Stub { + private CallbackToFutureAdapter.Completer<Tab> mCompleter; + + TabCallback(CallbackToFutureAdapter.Completer<Tab> completer) { + mCompleter = completer; + } + + @Override + public void onResult(@Nullable ITabParams tabParams) { + if (tabParams != null) { + mCompleter.set(new Tab(tabParams)); + return; + } + mCompleter.set(null); + } + }; + TabManager(IBrowserFragmentDelegate delegate) { mDelegate = delegate; } /** - * Returns the currently active Tab; null if no Tab is active. + * Returns a ListenableFuture for the currently active Tab; The tab can be null if no Tab is + * active. * - * @return the currently active Tab. + * @return ListenableFuture for the active Tab. */ - @Nullable - public Tab getActiveTab() { - try { - ITabProxy tabProxy = mDelegate.getActiveTab(); - if (tabProxy != null) { - return new Tab(tabProxy); + @NonNull + public ListenableFuture<Tab> getActiveTab() { + return CallbackToFutureAdapter.getFuture(completer -> { + try { + mDelegate.getActiveTab(new TabCallback(completer)); + } catch (RemoteException e) { + completer.setException(e); } - } catch (RemoteException e) { - } - return null; + // Debug string. + return "Active Tab Future"; + }); } }
diff --git a/weblayer/public/java/org/chromium/browserfragment/TabObserverDelegate.java b/weblayer/public/java/org/chromium/browserfragment/TabObserverDelegate.java index fc3bb3b..7614f96 100644 --- a/weblayer/public/java/org/chromium/browserfragment/TabObserverDelegate.java +++ b/weblayer/public/java/org/chromium/browserfragment/TabObserverDelegate.java
@@ -12,7 +12,7 @@ import org.chromium.base.ObserverList; import org.chromium.browserfragment.interfaces.ITabObserverDelegate; -import org.chromium.browserfragment.interfaces.ITabProxy; +import org.chromium.browserfragment.interfaces.ITabParams; /** * TabObserverDelegate notifies TabObservers of Tab-events in weblayer. @@ -41,9 +41,12 @@ } @Override - public void notifyActiveTabChanged(@Nullable ITabProxy activeTab) { + public void notifyActiveTabChanged(@Nullable ITabParams tabParams) { mHandler.post(() -> { - Tab tab = new Tab(activeTab); + Tab tab = null; + if (tabParams != null) { + tab = new Tab(tabParams); + } for (TabObserver observer : mTabObservers) { observer.onActiveTabChanged(tab); } @@ -51,9 +54,9 @@ } @Override - public void notifyTabAdded(@NonNull ITabProxy tabProxy) { + public void notifyTabAdded(@NonNull ITabParams tabParams) { mHandler.post(() -> { - Tab tab = new Tab(tabProxy); + Tab tab = new Tab(tabParams); for (TabObserver observer : mTabObservers) { observer.onTabAdded(tab); } @@ -61,9 +64,9 @@ } @Override - public void notifyTabRemoved(@NonNull ITabProxy tabProxy) { + public void notifyTabRemoved(@NonNull ITabParams tabParams) { mHandler.post(() -> { - Tab tab = new Tab(tabProxy); + Tab tab = new Tab(tabParams); for (TabObserver observer : mTabObservers) { observer.onTabRemoved(tab); }
diff --git a/weblayer/public/java/org/chromium/browserfragment/interfaces/IBrowserFragmentDelegate.aidl b/weblayer/public/java/org/chromium/browserfragment/interfaces/IBrowserFragmentDelegate.aidl index 017f37e..193e4dc 100644 --- a/weblayer/public/java/org/chromium/browserfragment/interfaces/IBrowserFragmentDelegate.aidl +++ b/weblayer/public/java/org/chromium/browserfragment/interfaces/IBrowserFragmentDelegate.aidl
@@ -6,10 +6,11 @@ import android.os.Bundle; import org.chromium.browserfragment.interfaces.IBrowserFragmentDelegateClient; +import org.chromium.browserfragment.interfaces.ITabCallback; import org.chromium.browserfragment.interfaces.ITabObserverDelegate; import org.chromium.browserfragment.interfaces.ITabProxy; -interface IBrowserFragmentDelegate { +oneway interface IBrowserFragmentDelegate { void setClient(in IBrowserFragmentDelegateClient client) = 1; void attachViewHierarchy(in IBinder hostToken) = 2; @@ -29,6 +30,6 @@ void onCleared() = 16; // Tab operations. - ITabProxy getActiveTab() = 14; + void getActiveTab(ITabCallback callback) = 14; void setTabObserverDelegate(ITabObserverDelegate tabObserverDelegate) = 15; } \ No newline at end of file
diff --git a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabCallback.aidl b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabCallback.aidl new file mode 100644 index 0000000..ee74c6f --- /dev/null +++ b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabCallback.aidl
@@ -0,0 +1,11 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.browserfragment.interfaces; + +import org.chromium.browserfragment.interfaces.ITabParams; + +oneway interface ITabCallback { + void onResult(in ITabParams tabParams) = 1; +}
diff --git a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabObserverDelegate.aidl b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabObserverDelegate.aidl index f05e875..2a9e9aa 100644 --- a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabObserverDelegate.aidl +++ b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabObserverDelegate.aidl
@@ -4,11 +4,11 @@ package org.chromium.browserfragment.interfaces; -import org.chromium.browserfragment.interfaces.ITabProxy; +import org.chromium.browserfragment.interfaces.ITabParams; oneway interface ITabObserverDelegate { - void notifyActiveTabChanged(in ITabProxy activeTab) = 1; - void notifyTabAdded(in ITabProxy tab) = 2; - void notifyTabRemoved(in ITabProxy tab) = 3; + void notifyActiveTabChanged(in ITabParams tabParams) = 1; + void notifyTabAdded(in ITabParams tabParams) = 2; + void notifyTabRemoved(in ITabParams tabParams) = 3; void notifyWillDestroyBrowserAndAllTabs() = 4; } \ No newline at end of file
diff --git a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabParams.aidl b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabParams.aidl new file mode 100644 index 0000000..8e78cbce --- /dev/null +++ b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabParams.aidl
@@ -0,0 +1,14 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.browserfragment.interfaces; + +import org.chromium.browserfragment.interfaces.ITabProxy; +import org.chromium.browserfragment.interfaces.ITabNavigationControllerProxy; + +parcelable ITabParams { + ITabProxy tabProxy; + String tabGuid; + ITabNavigationControllerProxy navigationControllerProxy; +} \ No newline at end of file
diff --git a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabProxy.aidl b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabProxy.aidl index 4ff9e50..6c718ba 100644 --- a/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabProxy.aidl +++ b/weblayer/public/java/org/chromium/browserfragment/interfaces/ITabProxy.aidl
@@ -6,7 +6,6 @@ import org.chromium.browserfragment.interfaces.ITabNavigationControllerProxy; -interface ITabProxy { +oneway interface ITabProxy { void setActive() = 1; - ITabNavigationControllerProxy getNavigationController() = 2; }
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentDelegate.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentDelegate.java index d930505..0605e3c 100644 --- a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentDelegate.java +++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentDelegate.java
@@ -13,12 +13,11 @@ import android.view.SurfaceControlViewHost; import android.view.WindowManager; -import androidx.annotation.Nullable; - import org.chromium.browserfragment.interfaces.IBrowserFragmentDelegate; import org.chromium.browserfragment.interfaces.IBrowserFragmentDelegateClient; +import org.chromium.browserfragment.interfaces.ITabCallback; import org.chromium.browserfragment.interfaces.ITabObserverDelegate; -import org.chromium.browserfragment.interfaces.ITabProxy; +import org.chromium.browserfragment.interfaces.ITabParams; /** * This class acts as a proxy between the embedding app's BrowserFragment and @@ -85,13 +84,19 @@ } @Override - @Nullable - public ITabProxy getActiveTab() { - Tab activeTab = mTabDelegate.getActiveTab(); - if (activeTab != null) { - return new TabProxy(activeTab); - } - return null; + public void getActiveTab(ITabCallback tabCallback) { + mHandler.post(() -> { + Tab activeTab = mFragment.getBrowser().getActiveTab(); + try { + if (activeTab != null) { + ITabParams tabParams = TabParams.buildParcelable(activeTab); + tabCallback.onResult(tabParams); + } else { + tabCallback.onResult(null); + } + } catch (RemoteException e) { + } + }); } @Override
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentTabDelegate.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentTabDelegate.java index de0b5e1aa..33d76b9 100644 --- a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentTabDelegate.java +++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentTabDelegate.java
@@ -3,20 +3,20 @@ // found in the LICENSE file. package org.chromium.weblayer; + import android.os.RemoteException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.chromium.browserfragment.interfaces.ITabObserverDelegate; +import org.chromium.browserfragment.interfaces.ITabParams; /** * This class acts as a proxy between the Tab events happening in * weblayer and the TabManager in browserfragment. */ class BrowserFragmentTabDelegate extends TabListCallback { - private Tab mActiveTab; - private ITabObserverDelegate mTabObserver; private final NewTabCallback mNewTabCallback = new NewTabCallback() { @@ -29,9 +29,14 @@ } @Override - public void onActiveTabChanged(@Nullable Tab activeTab) { - mActiveTab = activeTab; - maybeRunOnTabObserver(observer -> observer.notifyActiveTabChanged(new TabProxy(activeTab))); + public void onActiveTabChanged(@Nullable Tab tab) { + maybeRunOnTabObserver(observer -> { + ITabParams tabParams = null; + if (tab != null) { + tabParams = TabParams.buildParcelable(tab); + } + observer.notifyActiveTabChanged(tabParams); + }); } @Override @@ -39,12 +44,18 @@ // This is a requirement to open new tabs. tab.setNewTabCallback(mNewTabCallback); - maybeRunOnTabObserver(observer -> observer.notifyTabAdded(new TabProxy(tab))); + maybeRunOnTabObserver(observer -> { + ITabParams tabParams = TabParams.buildParcelable(tab); + observer.notifyTabAdded(tabParams); + }); } @Override public void onTabRemoved(@NonNull Tab tab) { - maybeRunOnTabObserver(observer -> observer.notifyTabRemoved(new TabProxy(tab))); + maybeRunOnTabObserver(observer -> { + ITabParams tabParams = TabParams.buildParcelable(tab); + observer.notifyTabRemoved(tabParams); + }); } @Override @@ -64,9 +75,4 @@ } } } - - @Nullable - Tab getActiveTab() { - return mActiveTab; - } } \ No newline at end of file
diff --git a/weblayer/public/java/org/chromium/weblayer/TabParams.java b/weblayer/public/java/org/chromium/weblayer/TabParams.java new file mode 100644 index 0000000..a647844 --- /dev/null +++ b/weblayer/public/java/org/chromium/weblayer/TabParams.java
@@ -0,0 +1,24 @@ +// Copyright 2022 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import androidx.annotation.NonNull; + +import org.chromium.browserfragment.interfaces.ITabParams; + +/** + * Parameters for {@link Tab}. + */ +class TabParams { + static ITabParams buildParcelable(@NonNull Tab tab) { + ITabParams parcel = new ITabParams(); + parcel.tabProxy = new TabProxy(tab); + parcel.tabGuid = tab.getGuid(); + parcel.navigationControllerProxy = + new TabNavigationControllerProxy(tab.getNavigationController()); + + return parcel; + } +} \ No newline at end of file
diff --git a/weblayer/public/java/org/chromium/weblayer/TabProxy.java b/weblayer/public/java/org/chromium/weblayer/TabProxy.java index f98e0a2..1a50b47 100644 --- a/weblayer/public/java/org/chromium/weblayer/TabProxy.java +++ b/weblayer/public/java/org/chromium/weblayer/TabProxy.java
@@ -7,7 +7,6 @@ import android.os.Handler; import android.os.Looper; -import org.chromium.browserfragment.interfaces.ITabNavigationControllerProxy; import org.chromium.browserfragment.interfaces.ITabProxy; /** @@ -17,17 +16,12 @@ class TabProxy extends ITabProxy.Stub { private Handler mHandler = new Handler(Looper.getMainLooper()); - private final ITabNavigationControllerProxy mTabNavigationControllerProxy; - private int mTabId; private String mGuid; TabProxy(Tab tab) { mTabId = tab.getId(); mGuid = tab.getGuid(); - - mTabNavigationControllerProxy = - new TabNavigationControllerProxy(tab.getNavigationController()); } private Tab getTab() { @@ -41,9 +35,4 @@ tab.getBrowser().setActiveTab(tab); }); } - - @Override - public ITabNavigationControllerProxy getNavigationController() { - return mTabNavigationControllerProxy; - } } \ No newline at end of file
diff --git a/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java b/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java index 72a0db4..fb45452 100644 --- a/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java +++ b/weblayer/shell/android/browserfragment_shell_apk/src/org/chromium/browserfragment/shell/BrowserFragmentShellActivity.java
@@ -11,6 +11,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -29,6 +30,8 @@ public class BrowserFragmentShellActivity extends AppCompatActivity { private static final String TAG = "BrowserFragmentShell"; + private static final String BROWSER_FRAGMENT_TAG = "BROWSER_FRAGMENT_TAG"; + private Context mContext; private TabManager mTabManager; @@ -82,13 +85,17 @@ }); ListenableFuture<TabManager> tabManagerFuture = fragment.getTabManager(); + AsyncFunction<TabManager, Tab> getActiveTabTask = tabManager -> { + mTabManager = tabManager; + return tabManager.getActiveTab(); + }; + ListenableFuture<Tab> activeTabFuture = Futures.transformAsync( + tabManagerFuture, getActiveTabTask, mContext.getMainExecutor()); - Futures.addCallback(tabManagerFuture, new FutureCallback<TabManager>() { + Futures.addCallback(activeTabFuture, new FutureCallback<Tab>() { @Override - public void onSuccess(TabManager tabManager) { - mTabManager = tabManager; - Tab tab = tabManager.getActiveTab(); - tab.getNavigationController().navigate("https://google.com"); + public void onSuccess(Tab activeTab) { + activeTab.getNavigationController().navigate("https://google.com"); } @Override public void onFailure(Throwable thrown) {} @@ -97,33 +104,42 @@ getSupportFragmentManager() .beginTransaction() .setReorderingAllowed(true) - .add(R.id.fragment_container_view, fragment) + .add(R.id.fragment_container_view, fragment, BROWSER_FRAGMENT_TAG) .commit(); } @Override public void onBackPressed() { - if (mTabManager == null) { - // BrowserFragment not yet initialized. + BrowserFragment fragment = (BrowserFragment) getSupportFragmentManager().findFragmentByTag( + BROWSER_FRAGMENT_TAG); + if (fragment == null || mTabManager == null) { + // BrowserFragment not initialized. super.onBackPressed(); + return; } - Tab activeTab = mTabManager.getActiveTab(); - if (activeTab == null) { - // TODO(swestphal): Check if there are any tabs? - super.onBackPressed(); - } - TabNavigationController navigationController = activeTab.getNavigationController(); + ListenableFuture<Tab> activeTabFuture = mTabManager.getActiveTab(); - ListenableFuture<Boolean> canGoBackFuture = navigationController.canGoBack(); - - Futures.addCallback(canGoBackFuture, new FutureCallback<Boolean>() { + Futures.addCallback(activeTabFuture, new FutureCallback<Tab>() { @Override - public void onSuccess(Boolean canGoBack) { - if (canGoBack) { - navigationController.goBack(); - } else { - BrowserFragmentShellActivity.super.onBackPressed(); - } + public void onSuccess(Tab activeTab) { + TabNavigationController tabNavigationController = + activeTab.getNavigationController(); + + Futures.addCallback( + tabNavigationController.canGoBack(), new FutureCallback<Boolean>() { + @Override + public void onSuccess(Boolean canGoBack) { + if (canGoBack) { + tabNavigationController.goBack(); + return; + } + BrowserFragmentShellActivity.super.onBackPressed(); + } + @Override + public void onFailure(Throwable thrown) { + BrowserFragmentShellActivity.super.onBackPressed(); + } + }, mContext.getMainExecutor()); } @Override public void onFailure(Throwable thrown) {
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn index 8f8ca44..a1ab3897 100644 --- a/weblayer/test/BUILD.gn +++ b/weblayer/test/BUILD.gn
@@ -217,6 +217,7 @@ "../browser/android/metrics/metrics_test_helper.h", "../browser/android/metrics/ukm_browsertest.cc", "../browser/autofill_browsertest.cc", + "../browser/browser_controls_navigation_state_handler_browsertest.cc", "../browser/safe_browsing/client_side_detection_service_browsertest.cc", "../browser/safe_browsing/client_side_detection_service_factory_browsertest.cc", "../browser/safe_browsing/safe_browsing_browsertest.cc",