diff --git a/.vpython b/.vpython index f210f49..712ddf1 100644 --- a/.vpython +++ b/.vpython
@@ -226,3 +226,55 @@ name: "infra/python/wheels/mock-py2_py3" version: "version:2.0.0" > + +# Used by: +# chrome/test/chromedriver/test/run_webdriver_tests.py + +wheel < + name: "infra/python/wheels/pytest-py2_py3" + version: "version:3.5.0" +> + +wheel < + name: "infra/python/wheels/attrs-py2_py3" + version: "version:17.4.0" +> + +wheel < + name: "infra/python/wheels/six-py2_py3" + version: "version:1.10.0" +> + +wheel < + name: "infra/python/wheels/more-itertools-py2_py3" + version: "version:4.1.0" +> + +wheel < + name: "infra/python/wheels/scandir/${vpython_platform}" + version: "version:1.7" +> + +wheel < + name: "infra/python/wheels/pluggy-py2_py3" + version: "version:0.6.0" +> + +wheel < + name: "infra/python/wheels/py-py2_py3" + version: "version:1.5.3" +> + +wheel < + name: "infra/python/wheels/funcsigs-py2_py3" + version: "version:1.0.2" +> +wheel: < + name: "infra/python/wheels/psutil/${vpython_platform}" + version: "version:5.2.2" +> + +wheel: < + name: "infra/python/wheels/colorama-py2_py3" + version: "version:0.4.1" +>
diff --git a/BUILD.gn b/BUILD.gn index 53155d0..3bf0898 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -927,6 +927,27 @@ ] } + if (!is_chromeos && !is_ios && !is_fuchsia && !is_android) { + # WPT Webdriver tests runner + # chrome/test/chromedriver/test/run_webdriver_tests.py + group("webdriver_wpt_tests") { + testonly = true + data = [ + "//third_party/blink/tools/blinkpy/third_party/wpt/wpt/tools/webdriver/", + "//third_party/blink/web_tests/external/wpt/webdriver/", + "//chrome/test/chromedriver/test/run_webdriver_tests.py", + "//chrome/test/chromedriver/server/server.py", + "//chrome/test/chromedriver/util.py", + "//chrome/test/chromedriver/chrome_paths.py", + "//testing/xvfb.py", + ] + data_deps = [ + "//chrome:chrome", + "//chrome/test/chromedriver", + ] + } + } + # https://www.chromium.org/developers/testing/webkit-layout-tests # The _exparchive at the end of the name indicates to the isolate recipe
diff --git a/DEPS b/DEPS index dbd6eabd..312d05f 100644 --- a/DEPS +++ b/DEPS
@@ -126,11 +126,11 @@ # 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': '98385ba6c5c691c092372b9010f8375c005e0448', + 'skia_revision': 'f36ad269e88a1b2fec16e6404cd6cd36bc202115', # 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': '003f99ea6ea916e527e8eacd733cdd5c919e6ae5', + 'v8_revision': '69a830d35c1947dcc8d5570bbb023263651e1e46', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -138,7 +138,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '740db7fd279529530019546457f048f06f4fbc8e', + 'angle_revision': '603ad164a6fcfac0fce282c604b292db5046b870', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -146,7 +146,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '1b8d2cc9056a4e22480963cdbf8bc34edd2dcd0b', + 'pdfium_revision': '82105494f27a592f628749bdf252d13e73e84cf2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -186,7 +186,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '11e283fea2536277797e09893a547cbd094e4426', + 'catapult_revision': '38769c1f96a321e99a04743d2326cab6ff8fa6fb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -250,7 +250,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': '7771f58c7f2429b9f1c532f1f3196423e5b9fb80', + 'dawn_revision': '01a3e9b6383f2169d46e64488a4adb5a0273c15c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -449,7 +449,7 @@ }, 'src/ios/third_party/webkit/src': { - 'url': 'https://chromium.googlesource.com/external/Webkit', + 'url': Var('chromium_git') + '/external/github.com/WebKit/webkit.git' + '@' + 'c56dd8a91c62afcfd0de4a5fe6dbb46484ea1fb5', 'condition': 'checkout_ios and checkout_ios_webkit' }, @@ -729,7 +729,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '971e4cb2b43d928c59a3e0e58d30dec03e8f2a9e', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f0a00cd7f17bdf5627cdb5da8faf8302cb243bf6', 'condition': 'checkout_linux', }, @@ -754,7 +754,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '61d0c292535e8e6c1102f198ec1ef47f50075ceb', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '06d1040fab75709fd0cbea3d3cbfa8774cb826f0', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1089,7 +1089,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '10ef8b33f128b6def49291c2c0de2e0e2eb84437', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '2af00334929272b1ec4ce4dd17480a50bbbc589b', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1252,7 +1252,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'db52df17f0d012983dc281e4864c71485a86bd0e', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'e98954c35e97dfe4d50fe6bd75587b14ffb6568e', + Var('webrtc_git') + '/src.git' + '@' + '7ca375c8ca54f5ab093204b9aa2bf446be10bcbd', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1293,7 +1293,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@96982ab4da6c91f6c47c129af5b91d76977c03c3', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8200b1f0ea5598d7828f69506f53bbad91bf3238', 'condition': 'checkout_src_internal', },
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 8bb4787..74b6c02 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -709,6 +709,8 @@ "system/model/update_model.h", "system/model/virtual_keyboard_model.cc", "system/model/virtual_keyboard_model.h", + "system/network/active_network_icon.cc", + "system/network/active_network_icon.h", "system/network/auto_connect_notifier.cc", "system/network/auto_connect_notifier.h", "system/network/network_feature_pod_button.cc",
diff --git a/ash/app_list/model/search/search_model.cc b/ash/app_list/model/search/search_model.cc index 1710c6ac..63a7385 100644 --- a/ash/app_list/model/search/search_model.cc +++ b/ash/app_list/model/search/search_model.cc
@@ -8,6 +8,8 @@ #include <string> #include <utility> +#include "base/bind.h" + namespace app_list { SearchModel::SearchModel() @@ -29,11 +31,27 @@ SearchResult::DisplayType display_type, const std::set<std::string>& excludes, size_t max_results) { + base::RepeatingCallback<bool(const SearchResult&)> filter_function = + base::BindRepeating( + [](const SearchResult::DisplayType& display_type, + const std::set<std::string>& excludes, + const SearchResult& r) -> bool { + return excludes.count(r.id()) == 0 && + display_type == r.display_type(); + }, + display_type, excludes); + return SearchModel::FilterSearchResultsByFunction(results, filter_function, + max_results); +} + +std::vector<SearchResult*> SearchModel::FilterSearchResultsByFunction( + SearchResults* results, + const base::RepeatingCallback<bool(const SearchResult&)>& result_filter, + size_t max_results) { std::vector<SearchResult*> matches; for (size_t i = 0; i < results->item_count(); ++i) { SearchResult* item = results->GetItemAt(i); - if (item->display_type() == display_type && - excludes.count(item->id()) == 0) { + if (result_filter.Run(*item)) { matches.push_back(item); if (matches.size() == max_results) break;
diff --git a/ash/app_list/model/search/search_model.h b/ash/app_list/model/search/search_model.h index 2216bd2b..115856f9 100644 --- a/ash/app_list/model/search/search_model.h +++ b/ash/app_list/model/search/search_model.h
@@ -13,6 +13,7 @@ #include "ash/app_list/model/app_list_model_export.h" #include "ash/app_list/model/search/search_box_model.h" #include "ash/app_list/model/search/search_result.h" +#include "base/callback.h" #include "ui/base/models/list_model.h" namespace app_list { @@ -47,6 +48,13 @@ const std::set<std::string>& excludes, size_t max_results); + // Filter the given |results| by those which |result_filter| returns true for. + // The returned list is truncated to |max_results|. + static std::vector<SearchResult*> FilterSearchResultsByFunction( + SearchResults* results, + const base::RepeatingCallback<bool(const SearchResult&)>& result_filter, + size_t max_results); + SearchBoxModel* search_box() { return search_box_.get(); } SearchResults* results() { return results_.get(); }
diff --git a/ash/app_list/views/search_result_tile_item_list_view.cc b/ash/app_list/views/search_result_tile_item_list_view.cc index 5127752..4567fba 100644 --- a/ash/app_list/views/search_result_tile_item_list_view.cc +++ b/ash/app_list/views/search_result_tile_item_list_view.cc
@@ -6,6 +6,7 @@ #include <stddef.h> +#include <algorithm> #include <memory> #include "ash/app_list/app_list_util.h" @@ -16,6 +17,8 @@ #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/app_list/internal_app_id_constants.h" +#include "base/bind.h" +#include "base/callback.h" #include "base/i18n/rtl.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/insets.h" @@ -50,13 +53,16 @@ : search_result_page_view_(search_result_page_view), search_box_(search_box), is_play_store_app_search_enabled_( - app_list_features::IsPlayStoreAppSearchEnabled()) { + app_list_features::IsPlayStoreAppSearchEnabled()), + is_app_reinstall_recommendation_enabled_( + app_list_features::IsAppReinstallZeroStateEnabled()) { SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kHorizontal, gfx::Insets(kItemListVerticalSpacing, kItemListHorizontalSpacing), kBetweenItemSpacing)); for (size_t i = 0; i < kMaxNumSearchResultTiles; ++i) { - if (is_play_store_app_search_enabled_) { + if (is_app_reinstall_recommendation_enabled_ || + is_play_store_app_search_enabled_) { views::Separator* separator = new views::Separator; separator->SetVisible(false); separator->SetBorder(views::CreateEmptyBorder( @@ -97,23 +103,12 @@ } int SearchResultTileItemListView::DoUpdate() { - base::string16 raw_query = search_box_->text(); - base::string16 query; - base::TrimWhitespace(raw_query, base::TRIM_ALL, &query); - - SearchResult::DisplayType display_type = - app_list_features::IsZeroStateSuggestionsEnabled() - ? (query.empty() ? ash::SearchResultDisplayType::kRecommendation - : ash::SearchResultDisplayType::kTile) - : ash::SearchResultDisplayType::kTile; - // Do not display the continue reading app in the search result list. - std::vector<SearchResult*> display_results = - SearchModel::FilterSearchResultsByDisplayType( - results(), display_type, - /*excludes=*/{app_list::kInternalAppIdContinueReading}, - kMaxNumSearchResultTiles); + std::vector<SearchResult*> display_results = GetDisplayResults(); SearchResult::ResultType previous_type = ash::SearchResultType::kUnknown; + ash::SearchResultDisplayType previous_display_type = + ash::SearchResultDisplayType::kNone; + for (size_t i = 0; i < kMaxNumSearchResultTiles; ++i) { if (i >= display_results.size()) { if (is_play_store_app_search_enabled_) @@ -125,8 +120,10 @@ SearchResult* item = display_results[i]; tile_views_[i]->SetResult(item); - if (is_play_store_app_search_enabled_) { - if (i > 0 && item->result_type() != previous_type) { + if (is_play_store_app_search_enabled_ || + is_app_reinstall_recommendation_enabled_) { + if (i > 0 && (item->result_type() != previous_type || + item->display_type() != previous_display_type)) { // Add a separator to separate search results of different types. // The strategy here is to only add a separator only if current search // result type is different from the previous one. The strategy is @@ -139,6 +136,7 @@ } previous_type = item->result_type(); + previous_display_type = item->display_type(); } set_container_score( @@ -147,6 +145,53 @@ return display_results.size(); } +std::vector<SearchResult*> SearchResultTileItemListView::GetDisplayResults() { + base::string16 raw_query = search_box_->text(); + base::string16 query; + base::TrimWhitespace(raw_query, base::TRIM_ALL, &query); + + // We ask for kMaxNumSearchResultTiles total results, and we prefer reinstall + // candidates if appropriate. we fetch |reinstall_results| first, and + // front-fill the rest from the regular result types. + auto reinstall_filter = + base::BindRepeating([](const SearchResult& r) -> bool { + return r.display_type() == + ash::SearchResultDisplayType::kRecommendation && + r.result_type() == ash::SearchResultType::kPlayStoreReinstallApp; + }); + std::vector<SearchResult*> reinstall_results = + is_app_reinstall_recommendation_enabled_ && query.empty() + ? SearchModel::FilterSearchResultsByFunction( + results(), reinstall_filter, kMaxNumSearchResultTiles) + : std::vector<SearchResult*>(); + + SearchResult::DisplayType display_type = + app_list_features::IsZeroStateSuggestionsEnabled() + ? (query.empty() ? ash::SearchResultDisplayType::kRecommendation + : ash::SearchResultDisplayType::kTile) + : ash::SearchResultDisplayType::kTile; + size_t display_num = kMaxNumSearchResultTiles - reinstall_results.size(); + + // Do not display the continue reading app in the search result list. + auto non_reinstall_filter = base::BindRepeating( + [](const SearchResult::DisplayType& display_type, + const SearchResult& r) -> bool { + return r.display_type() == display_type && + r.result_type() != + ash::SearchResultType::kPlayStoreReinstallApp && + r.id() != app_list::kInternalAppIdContinueReading; + }, + display_type); + std::vector<SearchResult*> display_results = + SearchModel::FilterSearchResultsByFunction( + results(), non_reinstall_filter, display_num); + + // Append the reinstalls to the display results. + display_results.insert(display_results.end(), reinstall_results.begin(), + reinstall_results.end()); + return display_results; +} + bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) { // Let the FocusManager handle Left/Right keys. if (!IsUnhandledUpDownKeyEvent(event))
diff --git a/ash/app_list/views/search_result_tile_item_list_view.h b/ash/app_list/views/search_result_tile_item_list_view.h index b180fab..27cd79f 100644 --- a/ash/app_list/views/search_result_tile_item_list_view.h +++ b/ash/app_list/views/search_result_tile_item_list_view.h
@@ -47,6 +47,8 @@ // Overridden from SearchResultContainerView: int DoUpdate() override; + std::vector<SearchResult*> GetDisplayResults(); + std::vector<SearchResultTileItemView*> tile_views_; std::vector<views::Separator*> separator_views_; @@ -57,6 +59,8 @@ const bool is_play_store_app_search_enabled_; + const bool is_app_reinstall_recommendation_enabled_; + DISALLOW_COPY_AND_ASSIGN(SearchResultTileItemListView); };
diff --git a/ash/app_list/views/search_result_tile_item_list_view_unittest.cc b/ash/app_list/views/search_result_tile_item_list_view_unittest.cc index 93297ac7..f3d6a70a 100644 --- a/ash/app_list/views/search_result_tile_item_list_view_unittest.cc +++ b/ash/app_list/views/search_result_tile_item_list_view_unittest.cc
@@ -4,6 +4,7 @@ #include "ash/app_list/views/search_result_tile_item_list_view.h" +#include <algorithm> #include <memory> #include <utility> @@ -28,34 +29,47 @@ constexpr int kMaxNumSearchResultTiles = 6; constexpr int kInstalledApps = 4; constexpr int kPlayStoreApps = 2; +constexpr int kRecommendedApps = 1; } // namespace class SearchResultTileItemListViewTest : public views::ViewsTestBase, - public ::testing::WithParamInterface<bool> { + public ::testing::WithParamInterface<std::pair<bool, bool>> { public: SearchResultTileItemListViewTest() = default; ~SearchResultTileItemListViewTest() override = default; protected: void CreateSearchResultTileItemListView() { + std::vector<base::Feature> enabled_features, disabled_features; // Enable fullscreen app list for parameterized Play Store app search // feature. // Zero State affects the UI behavior significantly. This test tests the // UI behavior with zero state being disable. // TODO(crbug.com/925195): Write new test cases for zero state. if (IsPlayStoreAppSearchEnabled()) { - scoped_feature_list_.InitWithFeatures( - {app_list_features::kEnablePlayStoreAppSearch}, - {app_list_features::kEnableZeroStateSuggestions}); + enabled_features.push_back(app_list_features::kEnablePlayStoreAppSearch); } else { - scoped_feature_list_.InitWithFeatures( - {}, {app_list_features::kEnablePlayStoreAppSearch, - app_list_features::kEnableZeroStateSuggestions}); + disabled_features.push_back(app_list_features::kEnablePlayStoreAppSearch); } + if (IsReinstallAppRecommendationEnabled()) { + enabled_features.push_back( + app_list_features::kEnableAppReinstallZeroState); + } else { + disabled_features.push_back( + app_list_features::kEnableAppReinstallZeroState); + } + + disabled_features.push_back(app_list_features::kEnableZeroStateSuggestions); + + scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); + ASSERT_EQ(IsPlayStoreAppSearchEnabled(), app_list_features::IsPlayStoreAppSearchEnabled()); + ASSERT_EQ(IsReinstallAppRecommendationEnabled(), + app_list_features::IsAppReinstallZeroStateEnabled()); + // Sets up the views. textfield_ = std::make_unique<views::Textfield>(); view_ = std::make_unique<SearchResultTileItemListView>( @@ -63,7 +77,9 @@ view_->SetResults(view_delegate_.GetSearchModel()->results()); } - bool IsPlayStoreAppSearchEnabled() const { return GetParam(); } + bool IsPlayStoreAppSearchEnabled() const { return GetParam().first; } + + bool IsReinstallAppRecommendationEnabled() const { return GetParam().second; } SearchResultTileItemListView* view() { return view_.get(); } @@ -103,6 +119,20 @@ } } + if (IsReinstallAppRecommendationEnabled()) { + for (int i = 0; i < kRecommendedApps; ++i) { + std::unique_ptr<TestSearchResult> result = + std::make_unique<TestSearchResult>(); + result->set_result_id(base::StringPrintf("RecommendedApp %d", i)); + result->set_display_type(ash::SearchResultDisplayType::kRecommendation); + result->set_result_type(ash::SearchResultType::kPlayStoreReinstallApp); + result->set_title( + base::UTF8ToUTF16(base::StringPrintf("RecommendedApp %d", i))); + result->SetRating(1 + i); + results->Add(std::move(result)); + } + } + // Adding results calls SearchResultContainerView::ScheduleUpdate(). // It will post a delayed task to update the results and relayout. RunPendingMessages(); @@ -138,20 +168,30 @@ SetUpSearchResults(); const int results = GetResultCount(); - const int expected_results = IsPlayStoreAppSearchEnabled() - ? kInstalledApps + kPlayStoreApps - : kInstalledApps; + int expected_results = kInstalledApps; + + if (IsPlayStoreAppSearchEnabled()) { + expected_results += kPlayStoreApps; + } + if (IsReinstallAppRecommendationEnabled()) { + expected_results += kRecommendedApps; + } + expected_results = std::min(kMaxNumSearchResultTiles, expected_results); + EXPECT_EQ(expected_results, results); - // When the Play Store app search feature is enabled, for each results, - // we added a separator for result type grouping. - const int expected_child_count = IsPlayStoreAppSearchEnabled() + + const bool separators_enabled = + IsPlayStoreAppSearchEnabled() || IsReinstallAppRecommendationEnabled(); + // When the Play Store app search feature or app reinstallation feature is + // enabled, for each result, we added a separator for result type grouping. + const int expected_child_count = separators_enabled ? kMaxNumSearchResultTiles * 2 : kMaxNumSearchResultTiles; EXPECT_EQ(expected_child_count, view()->child_count()); /// Test accessibility descriptions of tile views. - const int first_child = IsPlayStoreAppSearchEnabled() ? 1 : 0; - const int child_step = IsPlayStoreAppSearchEnabled() ? 2 : 1; + const int first_child = separators_enabled ? 1 : 0; + const int child_step = separators_enabled ? 2 : 1; for (int i = 0; i < kInstalledApps; ++i) { ui::AXNodeData node_data; @@ -163,7 +203,12 @@ node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)); } - for (int i = kInstalledApps; i < expected_results; ++i) { + const int expected_install_apps = + expected_results - + (IsReinstallAppRecommendationEnabled() ? kRecommendedApps : 0) - + kInstalledApps; + for (int i = kInstalledApps; i < (kInstalledApps + expected_install_apps); + ++i) { ui::AXNodeData node_data; view() ->child_at(first_child + i * child_step) @@ -175,15 +220,42 @@ node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)); } + // Recommendations. + const int start_index = kInstalledApps + expected_install_apps; + for (int i = kInstalledApps + expected_install_apps; i < expected_results; + ++i) { + ui::AXNodeData node_data; + view() + ->child_at(first_child + i * child_step) + ->GetAccessibleNodeData(&node_data); + EXPECT_EQ(ax::mojom::Role::kButton, node_data.role); + EXPECT_EQ(base::StringPrintf("RecommendedApp %d, Star rating %d.0", + i - start_index, i + 1 - start_index), + node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)); + } + ResetOpenResultCount(); for (int i = 0; i < results; ++i) { ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE); for (int j = 0; j <= i; ++j) view()->tile_views_for_test()[i]->OnKeyEvent(&event); - EXPECT_EQ(i + 1, GetOpenResultCount(i)); + // When both app reinstalls and play store apps are enabled, we actually + // instantiate 7 results, but only show 6. So we have to look, for exactly 1 + // result, a "skip" ahead for the reinstall result. + if (IsReinstallAppRecommendationEnabled() && + IsPlayStoreAppSearchEnabled() && i == (results - 1)) { + EXPECT_EQ(i + 1, GetOpenResultCount(i + 1)); + } else { + EXPECT_EQ(i + 1, GetOpenResultCount(i)); + } } } -INSTANTIATE_TEST_CASE_P(, SearchResultTileItemListViewTest, testing::Bool()); +INSTANTIATE_TEST_CASE_P(, + SearchResultTileItemListViewTest, + testing::ValuesIn({std::make_pair(false, false), + std::make_pair(false, true), + std::make_pair(true, false), + std::make_pair(true, true)})); } // namespace app_list
diff --git a/ash/app_list/views/search_result_tile_item_view.cc b/ash/app_list/views/search_result_tile_item_view.cc index 7939782..0880836c 100644 --- a/ash/app_list/views/search_result_tile_item_view.cc +++ b/ash/app_list/views/search_result_tile_item_view.cc
@@ -72,6 +72,8 @@ pagination_model_(pagination_model), is_play_store_app_search_enabled_( app_list_features::IsPlayStoreAppSearchEnabled()), + is_app_reinstall_recommendation_enabled_( + app_list_features::IsAppReinstallZeroStateEnabled()), show_in_apps_page_(show_in_apps_page), weak_ptr_factory_(this) { SetFocusBehavior(FocusBehavior::ALWAYS); @@ -87,7 +89,8 @@ AddChildView(icon_); if (is_play_store_app_search_enabled_ || - app_list_features::IsAppShortcutSearchEnabled()) { + app_list_features::IsAppShortcutSearchEnabled() || + is_app_reinstall_recommendation_enabled_) { badge_ = new views::ImageView; badge_->set_can_process_events_within_subtree(false); badge_->SetVerticalAlignment(views::ImageView::LEADING); @@ -104,7 +107,8 @@ title_->SetAllowCharacterBreak(true); AddChildView(title_); - if (is_play_store_app_search_enabled_) { + if (is_play_store_app_search_enabled_ || + is_app_reinstall_recommendation_enabled_) { rating_ = new views::Label; rating_->SetEnabledColor(kSearchAppRatingColor); rating_->SetLineHeight(kTileTextLineHeight); @@ -263,7 +267,6 @@ if (event.key_code() == ui::VKEY_RETURN) { if (IsSuggestedAppTile()) LogAppLaunch(); - RecordSearchResultOpenSource(result(), view_delegate_->GetModel(), view_delegate_->GetSearchModel()); view_delegate_->OpenSearchResult(result()->id(), event.flags());
diff --git a/ash/app_list/views/search_result_tile_item_view.h b/ash/app_list/views/search_result_tile_item_view.h index 92c3920..2fc1926 100644 --- a/ash/app_list/views/search_result_tile_item_view.h +++ b/ash/app_list/views/search_result_tile_item_view.h
@@ -115,6 +115,7 @@ SkColor parent_background_color_ = SK_ColorTRANSPARENT; const bool is_play_store_app_search_enabled_; + const bool is_app_reinstall_recommendation_enabled_; const bool show_in_apps_page_; // True if shown in app list's apps page. std::unique_ptr<AppListMenuModelAdapter> context_menu_;
diff --git a/ash/app_list/views/suggestion_chip_container_view.cc b/ash/app_list/views/suggestion_chip_container_view.cc index 5e2e044e..b77c84d 100644 --- a/ash/app_list/views/suggestion_chip_container_view.cc +++ b/ash/app_list/views/suggestion_chip_container_view.cc
@@ -13,6 +13,8 @@ #include "ash/app_list/views/search_result_suggestion_chip_view.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_features.h" +#include "base/bind.h" +#include "base/callback.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/focus/focus_manager.h" @@ -61,10 +63,14 @@ if (IgnoreUpdateAndLayout()) return num_results(); + auto exclude_reinstall_filter = [](const SearchResult& r) -> bool { + return r.display_type() == ash::SearchResultDisplayType::kRecommendation && + r.result_type() != ash::SearchResultType::kPlayStoreReinstallApp; + }; std::vector<SearchResult*> display_results = - SearchModel::FilterSearchResultsByDisplayType( - results(), ash::SearchResultDisplayType::kRecommendation, - /*excludes=*/{}, AppListConfig::instance().num_start_page_tiles()); + SearchModel::FilterSearchResultsByFunction( + results(), base::BindRepeating(exclude_reinstall_filter), + AppListConfig::instance().num_start_page_tiles()); // Update search results here, but wait until layout to add them as child // views when we know this view's bounds.
diff --git a/ash/display/display_configuration_controller.cc b/ash/display/display_configuration_controller.cc index ec6f43e..207114a 100644 --- a/ash/display/display_configuration_controller.cc +++ b/ash/display/display_configuration_controller.cc
@@ -16,13 +16,10 @@ #include "base/bind.h" #include "base/time/time.h" #include "chromeos/system/devicemode.h" -#include "ui/base/class_property.h" #include "ui/base/l10n/l10n_util.h" #include "ui/display/display_layout.h" #include "ui/display/manager/display_manager.h" -DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ScreenRotationAnimator*); - namespace ash { namespace { @@ -37,12 +34,6 @@ const int64_t kCycleDisplayThrottleTimeoutMs = 4000; const int64_t kSetPrimaryDisplayThrottleTimeoutMs = 500; -// A property key to store the ScreenRotationAnimator of the window; Used for -// screen rotation. -DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ash::ScreenRotationAnimator, - kScreenRotationAnimatorKey, - nullptr); - bool g_disable_animator_for_test = false; display::DisplayPositionInUnifiedMatrix GetUnifiedModeShelfCellPosition() { @@ -216,13 +207,6 @@ display_animator_.reset(new DisplayAnimator()); } -void DisplayConfigurationController::SetScreenRotationAnimatorForTest( - int64_t display_id, - std::unique_ptr<ScreenRotationAnimator> animator) { - aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id); - root_window->SetProperty(kScreenRotationAnimatorKey, animator.release()); -} - // Private void DisplayConfigurationController::SetThrottleTimeout(int64_t throttle_ms) { @@ -267,13 +251,7 @@ DisplayConfigurationController::GetScreenRotationAnimatorForDisplay( int64_t display_id) { aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id); - ScreenRotationAnimator* animator = - root_window->GetProperty(kScreenRotationAnimatorKey); - if (!animator) { - animator = new ScreenRotationAnimator(root_window); - root_window->SetProperty(kScreenRotationAnimatorKey, animator); - } - return animator; + return ScreenRotationAnimator::GetForRootWindow(root_window); } } // namespace ash
diff --git a/ash/display/display_configuration_controller.h b/ash/display/display_configuration_controller.h index cfd39495..2c81305 100644 --- a/ash/display/display_configuration_controller.h +++ b/ash/display/display_configuration_controller.h
@@ -96,10 +96,6 @@ // Allow tests to enable or disable animations. void SetAnimatorForTest(bool enable); - void SetScreenRotationAnimatorForTest( - int64_t display_id, - std::unique_ptr<ScreenRotationAnimator> animator); - private: class DisplayChangeLimiter;
diff --git a/ash/display/display_configuration_controller_test_api.cc b/ash/display/display_configuration_controller_test_api.cc index a15c127..fa0266b 100644 --- a/ash/display/display_configuration_controller_test_api.cc +++ b/ash/display/display_configuration_controller_test_api.cc
@@ -6,6 +6,7 @@ #include "ash/display/display_configuration_controller.h" #include "ash/rotator/screen_rotation_animator.h" +#include "ash/shell.h" namespace ash { @@ -26,8 +27,9 @@ void DisplayConfigurationControllerTestApi::SetScreenRotationAnimatorForDisplay( int64_t display_id, std::unique_ptr<ScreenRotationAnimator> animator) { - controller_->SetScreenRotationAnimatorForTest(display_id, - std::move(animator)); + aura::Window* root_window = Shell::GetRootWindowForDisplayId(display_id); + ScreenRotationAnimator::SetScreenRotationAnimatorForTest(root_window, + std::move(animator)); } } // namespace ash
diff --git a/ash/focus_cycler.cc b/ash/focus_cycler.cc index 7046cdb..30697217 100644 --- a/ash/focus_cycler.cc +++ b/ash/focus_cycler.cc
@@ -107,7 +107,7 @@ bool FocusCycler::FocusWidget(views::Widget* widget) { // If the target is PIP window, temporarily make it activatable. if (wm::GetWindowState(widget->GetNativeWindow())->IsPip()) - widget->widget_delegate()->set_can_activate(true); + widget->widget_delegate()->SetCanActivate(true); // Note: It is not necessary to set the focus directly to the pane since that // will be taken care of by the widget activation.
diff --git a/ash/ime/ime_mode_indicator_view.cc b/ash/ime/ime_mode_indicator_view.cc index ff4af7f3..5f477dd 100644 --- a/ash/ime/ime_mode_indicator_view.cc +++ b/ash/ime/ime_mode_indicator_view.cc
@@ -51,7 +51,7 @@ ImeModeIndicatorView::ImeModeIndicatorView(const gfx::Rect& cursor_bounds, const base::string16& label) : cursor_bounds_(cursor_bounds), label_view_(new views::Label(label)) { - set_can_activate(false); + SetCanActivate(false); set_accept_events(false); set_shadow(views::BubbleBorder::BIG_SHADOW); SetArrow(views::BubbleBorder::TOP_CENTER);
diff --git a/ash/public/cpp/app_list/app_list_struct_traits.h b/ash/public/cpp/app_list/app_list_struct_traits.h index e7394aa..b644a72 100644 --- a/ash/public/cpp/app_list/app_list_struct_traits.h +++ b/ash/public/cpp/app_list/app_list_struct_traits.h
@@ -113,6 +113,8 @@ return ash::mojom::SearchResultType::kLauncher; case ash::SearchResultType::kAnswerCard: return ash::mojom::SearchResultType::kAnswerCard; + case ash::SearchResultType::kPlayStoreReinstallApp: + return ash::mojom::SearchResultType::kPlayStoreReinstallApp; case ash::SearchResultType::kUnknown: break; } @@ -150,6 +152,9 @@ case ash::mojom::SearchResultType::kAnswerCard: *out = ash::SearchResultType::kAnswerCard; return true; + case ash::mojom::SearchResultType::kPlayStoreReinstallApp: + *out = ash::SearchResultType::kPlayStoreReinstallApp; + return true; } NOTREACHED(); return false;
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h index 13e4212..b8e78a7 100644 --- a/ash/public/cpp/app_list/app_list_types.h +++ b/ash/public/cpp/app_list/app_list_types.h
@@ -47,6 +47,7 @@ kOmnibox, // Results from Omnibox. kLauncher, // Results from launcher search (currently only from Files). kAnswerCard, // WebContents based answer card. + kPlayStoreReinstallApp, // Reinstall recommendations from PlayStore. // Add new values here. };
diff --git a/ash/public/cpp/ash_pref_names.cc b/ash/public/cpp/ash_pref_names.cc index 1e46c3b..91f6ee8 100644 --- a/ash/public/cpp/ash_pref_names.cc +++ b/ash/public/cpp/ash_pref_names.cc
@@ -183,6 +183,12 @@ const char kNightLightCustomStartTime[] = "ash.night_light.custom_start_time"; const char kNightLightCustomEndTime[] = "ash.night_light.custom_end_time"; +// Double prefs storing the most recent valid geoposition, which is only used +// when the device lacks connectivity and we're unable to retrieve a valid +// geoposition to calculate the sunset / sunrise times. +const char kNightLightCachedLatitude[] = "ash.night_light.cached_latitude"; +const char kNightLightCachedLongitude[] = "ash.night_light.cached_longitude"; + // Whether the Chrome OS lock screen is allowed. const char kAllowScreenLock[] = "allow_screen_lock";
diff --git a/ash/public/cpp/ash_pref_names.h b/ash/public/cpp/ash_pref_names.h index 143550e..c1bae25 100644 --- a/ash/public/cpp/ash_pref_names.h +++ b/ash/public/cpp/ash_pref_names.h
@@ -72,6 +72,8 @@ ASH_PUBLIC_EXPORT extern const char kNightLightScheduleType[]; ASH_PUBLIC_EXPORT extern const char kNightLightCustomStartTime[]; ASH_PUBLIC_EXPORT extern const char kNightLightCustomEndTime[]; +ASH_PUBLIC_EXPORT extern const char kNightLightCachedLatitude[]; +ASH_PUBLIC_EXPORT extern const char kNightLightCachedLongitude[]; ASH_PUBLIC_EXPORT extern const char kAllowScreenLock[]; ASH_PUBLIC_EXPORT extern const char kEnableAutoScreenLock[];
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom index 112067d..315e34e8 100644 --- a/ash/public/interfaces/app_list.mojom +++ b/ash/public/interfaces/app_list.mojom
@@ -119,6 +119,7 @@ kOmnibox, // Results from Omninbox. kLauncher, // Results from launcher search (currently only from Files). kAnswerCard, // WebContents based answer card. + kPlayStoreReinstallApp, // Reinstall recommendations from PlayStore. // Add new values here. };
diff --git a/ash/rotator/screen_rotation_animator.cc b/ash/rotator/screen_rotation_animator.cc index 4aa542ee..a3e6e73 100644 --- a/ash/rotator/screen_rotation_animator.cc +++ b/ash/rotator/screen_rotation_animator.cc
@@ -20,6 +20,7 @@ #include "components/viz/common/frame_sinks/copy_output_result.h" #include "third_party/khronos/GLES2/gl2.h" #include "ui/aura/window.h" +#include "ui/base/class_property.h" #include "ui/compositor/callback_layer_animation_observer.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_element.h" @@ -40,6 +41,8 @@ #include "ui/gfx/transform_util.h" #include "ui/wm/core/window_util.h" +DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ScreenRotationAnimator*); + namespace ash { namespace { @@ -54,6 +57,12 @@ const int kCounterClockWiseRotationFactor = 1; const int kClockWiseRotationFactor = -1; +// A property key to store the ScreenRotationAnimator of the window; Used for +// screen rotation. +DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ScreenRotationAnimator, + kScreenRotationAnimatorKey, + nullptr); + display::Display::Rotation GetCurrentScreenRotation(int64_t display_id) { return Shell::Get() ->display_manager() @@ -161,6 +170,24 @@ } // namespace +// static +ScreenRotationAnimator* ScreenRotationAnimator::GetForRootWindow( + aura::Window* root_window) { + auto* animator = root_window->GetProperty(kScreenRotationAnimatorKey); + if (!animator) { + animator = new ScreenRotationAnimator(root_window); + root_window->SetProperty(kScreenRotationAnimatorKey, animator); + } + return animator; +} + +// static +void ScreenRotationAnimator::SetScreenRotationAnimatorForTest( + aura::Window* root_window, + std::unique_ptr<ScreenRotationAnimator> animator) { + root_window->SetProperty(kScreenRotationAnimatorKey, animator.release()); +} + ScreenRotationAnimator::ScreenRotationAnimator(aura::Window* root_window) : root_window_(root_window), screen_rotation_state_(IDLE), @@ -299,6 +326,10 @@ animation_scale_mode_ = std::make_unique<ui::ScopedAnimationDurationScaleMode>( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + + for (auto& observer : screen_rotation_animator_observers_) + observer.OnScreenCopiedBeforeRotation(); + SetRotation(rotation_request->display_id, rotation_request->old_rotation, rotation_request->new_rotation, rotation_request->source); @@ -312,8 +343,10 @@ std::unique_ptr<ScreenRotationRequest> rotation_request, std::unique_ptr<viz::CopyOutputResult> result) { animation_scale_mode_.reset(); - if (IgnoreCopyResult(rotation_request->id, rotation_request_id_)) + if (IgnoreCopyResult(rotation_request->id, rotation_request_id_)) { + NotifyAnimationFinished(/*canceled=*/true); return; + } // In the following cases, abort animation: // 1) if the display was removed, // 2) if the |root_window| was changed for |display_id|, @@ -495,12 +528,12 @@ } } -void ScreenRotationAnimator::AddScreenRotationAnimatorObserver( +void ScreenRotationAnimator::AddObserver( ScreenRotationAnimatorObserver* observer) { screen_rotation_animator_observers_.AddObserver(observer); } -void ScreenRotationAnimator::RemoveScreenRotationAnimatorObserver( +void ScreenRotationAnimator::RemoveObserver( ScreenRotationAnimatorObserver* observer) { screen_rotation_animator_observers_.RemoveObserver(observer); } @@ -518,9 +551,7 @@ return; } - // This is only used in test to notify animator observer. - for (auto& observer : screen_rotation_animator_observers_) - observer.OnScreenRotationAnimationFinished(this); + NotifyAnimationFinished(/*canceled=*/false); } bool ScreenRotationAnimator::IsRotating() const { @@ -541,4 +572,9 @@ mask_layer_tree_owner_.reset(); } +void ScreenRotationAnimator::NotifyAnimationFinished(bool canceled) { + for (auto& observer : screen_rotation_animator_observers_) + observer.OnScreenRotationAnimationFinished(this, canceled); +} + } // namespace ash
diff --git a/ash/rotator/screen_rotation_animator.h b/ash/rotator/screen_rotation_animator.h index d11aa37..ccbc7cc 100644 --- a/ash/rotator/screen_rotation_animator.h +++ b/ash/rotator/screen_rotation_animator.h
@@ -33,12 +33,13 @@ } // namespace ui namespace ash { - class ScreenRotationAnimatorObserver; // Utility to perform a screen rotation with an animation. class ASH_EXPORT ScreenRotationAnimator { public: + static ScreenRotationAnimator* GetForRootWindow(aura::Window* root_window); + explicit ScreenRotationAnimator(aura::Window* root_window); virtual ~ScreenRotationAnimator(); @@ -53,10 +54,8 @@ display::Display::RotationSource source, DisplayConfigurationController::RotationAnimation mode); - void AddScreenRotationAnimatorObserver( - ScreenRotationAnimatorObserver* observer); - void RemoveScreenRotationAnimatorObserver( - ScreenRotationAnimatorObserver* observer); + void AddObserver(ScreenRotationAnimatorObserver* observer); + void RemoveObserver(ScreenRotationAnimatorObserver* observer); // When screen rotation animation is ended or aborted, calls |Rotate()| with // the pending rotation request if the request queue is not empty. Otherwise @@ -70,6 +69,10 @@ // orientation if |IsRotating()| is false. display::Display::Rotation GetTargetRotation() const; + static void SetScreenRotationAnimatorForTest( + aura::Window* root_window, + std::unique_ptr<ScreenRotationAnimator> animator); + protected: using CopyCallback = base::OnceCallback<void(std::unique_ptr<viz::CopyOutputResult> result)>; @@ -158,6 +161,8 @@ // |rotation_degrees| arc. void AnimateRotation(std::unique_ptr<ScreenRotationRequest> rotation_request); + void NotifyAnimationFinished(bool canceled); + void set_disable_animation_timers_for_test(bool disable_timers) { disable_animation_timers_for_test_ = disable_timers; }
diff --git a/ash/rotator/screen_rotation_animator_observer.h b/ash/rotator/screen_rotation_animator_observer.h index 5689a96..9f6963d3 100644 --- a/ash/rotator/screen_rotation_animator_observer.h +++ b/ash/rotator/screen_rotation_animator_observer.h
@@ -15,9 +15,13 @@ public: ScreenRotationAnimatorObserver() {} + // This will be called when the screen is copied before rotation. + virtual void OnScreenCopiedBeforeRotation() = 0; + // This will be called when the animation is ended or aborted. virtual void OnScreenRotationAnimationFinished( - ScreenRotationAnimator* animator) = 0; + ScreenRotationAnimator* animator, + bool canceled) = 0; protected: virtual ~ScreenRotationAnimatorObserver() {}
diff --git a/ash/rotator/screen_rotation_animator_unittest.cc b/ash/rotator/screen_rotation_animator_unittest.cc index f73657a8..c0d8f3a 100644 --- a/ash/rotator/screen_rotation_animator_unittest.cc +++ b/ash/rotator/screen_rotation_animator_unittest.cc
@@ -60,15 +60,18 @@ public: AnimationObserver() = default; - bool notified() const { return notified_; } + bool copy_notified() const { return copy_notified_; } + bool finish_notified() const { return finish_notified_; } - void OnScreenRotationAnimationFinished( - ScreenRotationAnimator* animator) override { - notified_ = true; + void OnScreenCopiedBeforeRotation() override { copy_notified_ = true; } + void OnScreenRotationAnimationFinished(ScreenRotationAnimator* animator, + bool canceled) override { + finish_notified_ = true; } private: - bool notified_ = false; + bool copy_notified_ = false; + bool finish_notified_ = false; DISALLOW_COPY_AND_ASSIGN(AnimationObserver); }; @@ -261,40 +264,46 @@ TEST_F(ScreenRotationAnimatorSlowAnimationTest, ShouldNotifyObserver) { SetDisplayRotation(display_id(), display::Display::ROTATE_0); AnimationObserver observer; - animator()->AddScreenRotationAnimatorObserver(&observer); - EXPECT_FALSE(observer.notified()); + animator()->AddObserver(&observer); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); animator()->Rotate(display::Display::ROTATE_90, display::Display::RotationSource::USER, DisplayConfigurationController::ANIMATION_SYNC); - EXPECT_FALSE(observer.notified()); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); test_api()->CompleteAnimations(); - EXPECT_TRUE(observer.notified()); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_TRUE(observer.finish_notified()); EXPECT_FALSE(test_api()->HasActiveAnimations()); - animator()->RemoveScreenRotationAnimatorObserver(&observer); + animator()->RemoveObserver(&observer); } TEST_F(ScreenRotationAnimatorSlowAnimationTest, ShouldNotifyObserverOnce) { SetDisplayRotation(display_id(), display::Display::ROTATE_0); AnimationObserver observer; - animator()->AddScreenRotationAnimatorObserver(&observer); - EXPECT_FALSE(observer.notified()); + animator()->AddObserver(&observer); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); animator()->Rotate(display::Display::ROTATE_90, display::Display::RotationSource::USER, DisplayConfigurationController::ANIMATION_SYNC); - EXPECT_FALSE(observer.notified()); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); animator()->Rotate(display::Display::ROTATE_180, display::Display::RotationSource::USER, DisplayConfigurationController::ANIMATION_SYNC); - EXPECT_FALSE(observer.notified()); + EXPECT_FALSE(observer.finish_notified()); test_api()->CompleteAnimations(); - EXPECT_TRUE(observer.notified()); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_TRUE(observer.finish_notified()); EXPECT_FALSE(test_api()->HasActiveAnimations()); - animator()->RemoveScreenRotationAnimatorObserver(&observer); + animator()->RemoveObserver(&observer); } TEST_F(ScreenRotationAnimatorSlowAnimationTest, RotatesToDifferentRotation) { @@ -389,6 +398,41 @@ EXPECT_FALSE(GetTray()->visible()); } +TEST_F(ScreenRotationAnimatorSmoothAnimationTest, Observer) { + const int64_t display_id = display_manager()->GetDisplayAt(0).id(); + + SetScreenRotationAnimator( + Shell::GetRootWindowForDisplayId(display_id), + base::BindRepeating( + &ScreenRotationAnimatorSmoothAnimationTest::QuitWaitForCopyCallback, + base::Unretained(this)), + base::BindRepeating( + &ScreenRotationAnimatorSmoothAnimationTest::QuitWaitForCopyCallback, + base::Unretained(this))); + AnimationObserver observer; + animator()->AddObserver(&observer); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); + + SetDisplayRotation(display_id, display::Display::ROTATE_0); + animator()->Rotate(display::Display::ROTATE_90, + display::Display::RotationSource::USER, + DisplayConfigurationController::ANIMATION_ASYNC); + EXPECT_TRUE(animator()->IsRotating()); + WaitForCopyCallback(); + EXPECT_TRUE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); + + WaitForCopyCallback(); + EXPECT_TRUE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); + test_api()->CompleteAnimations(); + EXPECT_FALSE(test_api()->HasActiveAnimations()); + EXPECT_EQ(display::Display::ROTATE_90, GetDisplayRotation(display_id)); + EXPECT_TRUE(observer.copy_notified()); + EXPECT_TRUE(observer.finish_notified()); +} + // Test enable smooth screen rotation code path. TEST_F(ScreenRotationAnimatorSmoothAnimationTest, RotatesToDifferentRotationWithCopyCallback) { @@ -399,6 +443,11 @@ base::Bind( &ScreenRotationAnimatorSmoothAnimationTest::QuitWaitForCopyCallback, base::Unretained(this))); + AnimationObserver observer; + animator()->AddObserver(&observer); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); + SetDisplayRotation(display_id, display::Display::ROTATE_0); animator()->Rotate(display::Display::ROTATE_90, display::Display::RotationSource::USER, @@ -407,9 +456,14 @@ EXPECT_EQ(display::Display::ROTATE_90, animator()->GetTargetRotation()); EXPECT_NE(display::Display::ROTATE_90, GetDisplayRotation(display_id)); + EXPECT_FALSE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); WaitForCopyCallback(); EXPECT_TRUE(test_api()->HasActiveAnimations()); + EXPECT_TRUE(observer.copy_notified()); + EXPECT_FALSE(observer.finish_notified()); + EXPECT_EQ(display::Display::ROTATE_90, animator()->GetTargetRotation()); // Once copy is made, the rotation is set to the target, with the // image that was rotated to the original orientation. @@ -418,6 +472,8 @@ test_api()->CompleteAnimations(); EXPECT_FALSE(test_api()->HasActiveAnimations()); EXPECT_EQ(display::Display::ROTATE_90, GetDisplayRotation(display_id)); + EXPECT_TRUE(observer.copy_notified()); + EXPECT_TRUE(observer.finish_notified()); } // If the rotating external secondary display is removed before the first copy
diff --git a/ash/shelf/shelf_tooltip_bubble.cc b/ash/shelf/shelf_tooltip_bubble.cc index 1812ca3..e0b3374 100644 --- a/ash/shelf/shelf_tooltip_bubble.cc +++ b/ash/shelf/shelf_tooltip_bubble.cc
@@ -37,7 +37,7 @@ : ShelfBubble(anchor, alignment, background_color) { set_margins(gfx::Insets(kTooltipTopBottomMargin, kTooltipLeftRightMargin)); set_close_on_deactivate(false); - set_can_activate(false); + SetCanActivate(false); set_accept_events(false); set_shadow(views::BubbleBorder::NO_ASSETS); SetLayoutManager(std::make_unique<views::FillLayout>());
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc index 2f17e5c..79db8beb 100644 --- a/ash/system/message_center/arc/arc_notification_content_view.cc +++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -743,7 +743,7 @@ // Make the widget active. if (activate) { - GetWidget()->widget_delegate()->set_can_activate(true); + GetWidget()->widget_delegate()->SetCanActivate(true); GetWidget()->Activate(); if (surface_) @@ -751,7 +751,7 @@ else activate_on_attach_ = true; } else { - GetWidget()->widget_delegate()->set_can_activate(false); + GetWidget()->widget_delegate()->SetCanActivate(false); } }
diff --git a/ash/system/model/system_tray_model.cc b/ash/system/model/system_tray_model.cc index 3489fc0..8d0f44f 100644 --- a/ash/system/model/system_tray_model.cc +++ b/ash/system/model/system_tray_model.cc
@@ -13,6 +13,7 @@ #include "ash/system/model/tracing_model.h" #include "ash/system/model/update_model.h" #include "ash/system/model/virtual_keyboard_model.h" +#include "ash/system/network/active_network_icon.h" #include "ash/system/status_area_widget.h" #include "ash/system/unified/unified_system_tray.h" #include "base/logging.h" @@ -26,7 +27,8 @@ session_length_limit_(std::make_unique<SessionLengthLimitModel>()), tracing_(std::make_unique<TracingModel>()), update_model_(std::make_unique<UpdateModel>()), - virtual_keyboard_(std::make_unique<VirtualKeyboardModel>()) {} + virtual_keyboard_(std::make_unique<VirtualKeyboardModel>()), + active_network_icon_(std::make_unique<ActiveNetworkIcon>()) {} SystemTrayModel::~SystemTrayModel() = default;
diff --git a/ash/system/model/system_tray_model.h b/ash/system/model/system_tray_model.h index 74721f9..7a5d70aa 100644 --- a/ash/system/model/system_tray_model.h +++ b/ash/system/model/system_tray_model.h
@@ -13,6 +13,7 @@ namespace ash { +class ActiveNetworkIcon; class ClockModel; class EnterpriseDomainModel; class LocaleModel; @@ -62,6 +63,9 @@ TracingModel* tracing() { return tracing_.get(); } UpdateModel* update_model() { return update_model_.get(); } VirtualKeyboardModel* virtual_keyboard() { return virtual_keyboard_.get(); } + ActiveNetworkIcon* active_network_icon() { + return active_network_icon_.get(); + } const mojom::SystemTrayClientPtr& client_ptr() { return client_ptr_; } @@ -73,6 +77,7 @@ std::unique_ptr<TracingModel> tracing_; std::unique_ptr<UpdateModel> update_model_; std::unique_ptr<VirtualKeyboardModel> virtual_keyboard_; + std::unique_ptr<ActiveNetworkIcon> active_network_icon_; // TODO(tetsui): Add following as a sub-model of SystemTrayModel: // * BluetoothModel
diff --git a/ash/system/network/active_network_icon.cc b/ash/system/network/active_network_icon.cc new file mode 100644 index 0000000..9473d0e --- /dev/null +++ b/ash/system/network/active_network_icon.cc
@@ -0,0 +1,248 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/network/active_network_icon.h" + +#include "ash/resources/vector_icons/vector_icons.h" +#include "ash/strings/grit/ash_strings.h" +#include "ash/system/network/network_icon.h" +#include "ash/system/tray/tray_constants.h" +#include "chromeos/network/network_handler.h" +#include "chromeos/network/network_state.h" +#include "chromeos/network/network_state_handler.h" +#include "third_party/cros_system_api/dbus/service_constants.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/paint_vector_icon.h" + +using chromeos::NetworkState; +using chromeos::NetworkTypePattern; + +namespace ash { + +namespace { + +bool IsTrayIcon(network_icon::IconType icon_type) { + return icon_type == network_icon::ICON_TYPE_TRAY_REGULAR || + icon_type == network_icon::ICON_TYPE_TRAY_OOBE; +} + +SkColor GetDefaultColorForIconType(network_icon::IconType icon_type) { + if (icon_type == network_icon::ICON_TYPE_TRAY_REGULAR) + return kTrayIconColor; + if (icon_type == network_icon::ICON_TYPE_TRAY_OOBE) + return kOobeTrayIconColor; + return kUnifiedMenuIconColor; +} + +const NetworkState* GetConnectingOrConnected( + const NetworkState* connecting_network, + const NetworkState* connected_network) { + if (connecting_network && + (!connected_network || connecting_network->IsReconnecting() || + connecting_network->connect_requested())) { + // If we are connecting to a network, and there is either no connected + // network, or the connection was user requested, or shill triggered a + // reconnection, use the connecting network. + return connecting_network; + } + return connected_network; +} + +} // namespace + +ActiveNetworkIcon::ActiveNetworkIcon() { + // NetworkHandler may not be initialized in tests. + if (!chromeos::NetworkHandler::IsInitialized()) + return; + network_state_handler_ = + chromeos::NetworkHandler::Get()->network_state_handler(); + DCHECK(network_state_handler_); + network_state_handler_->AddObserver(this, FROM_HERE); + UpdateActiveNetworks(); +} + +ActiveNetworkIcon::~ActiveNetworkIcon() { + if (network_state_handler_) + network_state_handler_->RemoveObserver(this, FROM_HERE); +} + +base::string16 ActiveNetworkIcon::GetDefaultLabel( + network_icon::IconType icon_type) { + if (!default_network_) { + if (cellular_uninitialized_msg_ != 0) + return l10n_util::GetStringUTF16(cellular_uninitialized_msg_); + return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED); + } + return network_icon::GetLabelForNetwork(default_network_, icon_type); +} + +gfx::ImageSkia ActiveNetworkIcon::GetSingleImage( + network_icon::IconType icon_type, + bool* animating) { + // If no network, check for cellular initializing. + if (!default_network_ && cellular_uninitialized_msg_ != 0) { + if (animating) + *animating = true; + return network_icon::GetConnectingImageForNetworkType(shill::kTypeCellular, + icon_type); + } + return GetDefaultImageImpl(default_network_, icon_type, animating); +} + +gfx::ImageSkia ActiveNetworkIcon::GetDualImagePrimary( + network_icon::IconType icon_type, + bool* animating) { + const NetworkState* default_network = default_network_; + if (default_network && + default_network->Matches(NetworkTypePattern::Cellular())) { + if (default_network->IsConnectedState()) { + // TODO: Show proper technology badges. + if (animating) + *animating = false; + return gfx::CreateVectorIcon(kNetworkBadgeTechnologyLteIcon, + GetDefaultColorForIconType(icon_type)); + } + // If Cellular is connecting, use the active non cellular network. + default_network = active_non_cellular_; + } + return GetDefaultImageImpl(default_network, icon_type, animating); +} + +gfx::ImageSkia ActiveNetworkIcon::GetDualImageCellular( + network_icon::IconType icon_type, + bool* animating) { + if (!network_state_handler_->IsTechnologyAvailable( + NetworkTypePattern::Cellular())) { + if (animating) + *animating = false; + return gfx::ImageSkia(); + } + + if (cellular_uninitialized_msg_ != 0) { + if (animating) + *animating = true; + return network_icon::GetConnectingImageForNetworkType(shill::kTypeCellular, + icon_type); + } + + if (!active_cellular_) { + if (animating) + *animating = false; + return network_icon::GetDisconnectedImageForNetworkType( + shill::kTypeCellular); + } + + return network_icon::GetImageForNonVirtualNetwork( + active_cellular_, icon_type, false /* show_vpn_badge */, animating); +} + +gfx::ImageSkia ActiveNetworkIcon::GetDefaultImageImpl( + const NetworkState* default_network, + network_icon::IconType icon_type, + bool* animating) { + if (!default_network_) + return GetDefaultImageForNoNetwork(icon_type, animating); + + // Don't show connected Ethernet in the tray unless a VPN is present. + if (default_network->Matches(NetworkTypePattern::Ethernet()) && + IsTrayIcon(icon_type) && !active_vpn_) { + if (animating) + *animating = false; + return gfx::ImageSkia(); + } + + // Connected network with a connecting VPN. + if (default_network->IsConnectedState() && active_vpn_ && + active_vpn_->IsConnectingState()) { + if (animating) + *animating = true; + return network_icon::GetConnectedNetworkWithConnectingVpnImage( + default_network, icon_type); + } + + // Default behavior: connected or connecting network, possibly with VPN badge. + bool show_vpn_badge = !!active_vpn_; + return network_icon::GetImageForNonVirtualNetwork(default_network, icon_type, + show_vpn_badge, animating); +} + +gfx::ImageSkia ActiveNetworkIcon::GetDefaultImageForNoNetwork( + network_icon::IconType icon_type, + bool* animating) { + if (animating) + *animating = false; + if (network_state_handler_ && + network_state_handler_->IsTechnologyEnabled(NetworkTypePattern::WiFi())) { + // WiFi is enabled but disconnected, show an empty wedge. + return network_icon::GetBasicImage(icon_type, shill::kTypeWifi, + false /* connected */); + } + // WiFi is disabled, show a full icon with a strikethrough. + return network_icon::GetImageForWiFiEnabledState(false /* not enabled*/, + icon_type); +} + +void ActiveNetworkIcon::UpdateActiveNetworks() { + std::vector<const NetworkState*> active_networks; + network_state_handler_->GetActiveNetworkListByType( + NetworkTypePattern::Default(), &active_networks); + ActiveNetworksChanged(active_networks); +} + +void ActiveNetworkIcon::DeviceListChanged() { + UpdateActiveNetworks(); +} + +void ActiveNetworkIcon::ActiveNetworksChanged( + const std::vector<const NetworkState*>& active_networks) { + active_cellular_ = nullptr; + active_vpn_ = nullptr; + + const NetworkState* connected_network = nullptr; + const NetworkState* connected_non_cellular = nullptr; + const NetworkState* connecting_network = nullptr; + const NetworkState* connecting_non_cellular = nullptr; + for (const NetworkState* network : active_networks) { + if (network->Matches(NetworkTypePattern::VPN())) { + if (!active_vpn_) + active_vpn_ = network; + continue; + } + if (network->Matches(NetworkTypePattern::Cellular())) { + if (!active_cellular_) + active_cellular_ = network; + } + if (network->IsConnectedState()) { + if (!connected_network) + connected_network = network; + if (!connected_non_cellular && + !network->Matches(NetworkTypePattern::Cellular())) { + connected_non_cellular = network; + } + continue; + } + if (network->Matches(NetworkTypePattern::Wireless()) && + network->IsActive()) { + if (!connecting_network) + connecting_network = network; + if (!connecting_non_cellular && + !network->Matches(NetworkTypePattern::Cellular())) { + connecting_non_cellular = network; + } + } + } + + default_network_ = + GetConnectingOrConnected(connecting_network, connected_network); + active_non_cellular_ = + GetConnectingOrConnected(connecting_non_cellular, connected_non_cellular); + + cellular_uninitialized_msg_ = network_icon::GetCellularUninitializedMsg(); +} + +void ActiveNetworkIcon::OnShuttingDown() { + network_state_handler_ = nullptr; +} + +} // namespace ash
diff --git a/ash/system/network/active_network_icon.h b/ash/system/network/active_network_icon.h new file mode 100644 index 0000000..7dfe0f67 --- /dev/null +++ b/ash/system/network/active_network_icon.h
@@ -0,0 +1,93 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SYSTEM_NETWORK_ACTIVE_NETWORK_ICON_H_ +#define ASH_SYSTEM_NETWORK_ACTIVE_NETWORK_ICON_H_ + +#include <memory> +#include <vector> + +#include "ash/ash_export.h" +#include "ash/system/network/network_icon.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "chromeos/network/network_state_handler_observer.h" + +namespace chromeos { +class NetworkState; +class NetworkStateHandler; +} // namespace chromeos + +namespace gfx { +class ImageSkia; +} // namespace gfx + +namespace ash { + +// Tracks changes to the active networks and provides an interface to +// network_icon for the default network. This class supports two interfaces: +// * Single: A single icon is shown to represent the active network state. +// * Dual: One or two icons are shown to represent the active network state: +// ** Primary: The state of the primary active network. If Cellular, a +// a technology badge is used to represent the network. +// ** Cellular (enabled devices only): The state of the Cellular connection if +// available regardless of whether it is the active network. +// NOTE : GetSingleDefaultImage and GetDefaultLabel are tested in +// network_icon_unittest.cc. TODO(stevenjb): Test other public methods. +class ASH_EXPORT ActiveNetworkIcon + : public chromeos::NetworkStateHandlerObserver { + public: + ActiveNetworkIcon(); + ~ActiveNetworkIcon() override; + + // Returns the label for the primary active network.. + base::string16 GetDefaultLabel(network_icon::IconType icon_type); + + // Single image mode. Returns a network icon (which may be empty) and sets + // |animating| if provided. + gfx::ImageSkia GetSingleImage(network_icon::IconType icon_type, + bool* animating); + + // Dual image mode. Returns the primary icon (which may be empty) and sets + // |animating| if provided. + gfx::ImageSkia GetDualImagePrimary(network_icon::IconType icon_type, + bool* animating); + + // Dual image mode. Returns the Cellular icon (which may be empty) and sets + // |animating| if provided. + gfx::ImageSkia GetDualImageCellular(network_icon::IconType icon_type, + bool* animating); + + private: + gfx::ImageSkia GetDefaultImageImpl( + const chromeos::NetworkState* default_network, + network_icon::IconType icon_type, + bool* animating); + + // Called when there is no default network., Provides an empty or disabled + // wifi icon and sets |animating| if provided to false. + gfx::ImageSkia GetDefaultImageForNoNetwork(network_icon::IconType icon_type, + bool* animating); + + void UpdateActiveNetworks(); + + // chromeos::NetworkStateHandlerObserver + void DeviceListChanged() override; + void ActiveNetworksChanged(const std::vector<const chromeos::NetworkState*>& + active_networks) override; + void OnShuttingDown() override; + + chromeos::NetworkStateHandler* network_state_handler_ = nullptr; + const chromeos::NetworkState* default_network_ = nullptr; + const chromeos::NetworkState* active_non_cellular_ = nullptr; + const chromeos::NetworkState* active_cellular_ = nullptr; + const chromeos::NetworkState* active_vpn_ = nullptr; + int cellular_uninitialized_msg_ = 0; + + DISALLOW_COPY_AND_ASSIGN(ActiveNetworkIcon); +}; + +} // namespace ash + +#endif // ASH_SYSTEM_NETWORK_ACTIVE_NETWORK_ICON_H_
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc index 31b7e52..d92db02 100644 --- a/ash/system/network/network_feature_pod_button.cc +++ b/ash/system/network/network_feature_pod_button.cc
@@ -6,6 +6,8 @@ #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" +#include "ash/system/model/system_tray_model.h" +#include "ash/system/network/active_network_icon.h" #include "ash/system/network/network_icon.h" #include "ash/system/network/network_icon_animation.h" #include "ash/system/tray/system_tray_notifier.h" @@ -86,11 +88,10 @@ } void NetworkFeaturePodButton::Update() { - gfx::ImageSkia image; bool animating = false; - network_icon::GetDefaultNetworkImageAndLabel( - network_icon::ICON_TYPE_DEFAULT_VIEW, &image, nullptr, &animating); - + gfx::ImageSkia image = + Shell::Get()->system_tray_model()->active_network_icon()->GetSingleImage( + network_icon::ICON_TYPE_DEFAULT_VIEW, &animating); if (animating) network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); else
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc index 7880b50..d5cb0986 100644 --- a/ash/system/network/network_icon.cc +++ b/ash/system/network/network_icon.cc
@@ -219,16 +219,6 @@ GetSizeForIconType(icon_type), index, GetPaddingForIconType(icon_type)); } -// Returns an image to represent either a fully connected network or a -// disconnected network. -const gfx::ImageSkia GetBasicImage(bool connected, - IconType icon_type, - const std::string& network_type) { - DCHECK_NE(shill::kTypeVPN, network_type); - return GetImageForIndex(ImageTypeForNetworkType(network_type), icon_type, - connected ? kNumNetworkImages - 1 : 0); -} - gfx::ImageSkia* ConnectingWirelessImage(ImageType image_type, IconType icon_type, double animation) { @@ -393,10 +383,13 @@ bool NetworkIconImpl::UpdateCellularState(const NetworkState* network) { bool dirty = false; - const Badge technology_badge = BadgeForNetworkTechnology(network, icon_type_); - if (technology_badge != technology_badge_) { - technology_badge_ = technology_badge; - dirty = true; + if (!features::IsSeparateNetworkIconsEnabled()) { + const Badge technology_badge = + BadgeForNetworkTechnology(network, icon_type_); + if (technology_badge != technology_badge_) { + technology_badge_ = technology_badge; + dirty = true; + } } bool is_roaming = network->IndicateRoaming(); if (is_roaming != is_roaming_) { @@ -467,6 +460,14 @@ //------------------------------------------------------------------------------ // Public interface +const gfx::ImageSkia GetBasicImage(IconType icon_type, + const std::string& network_type, + bool connected) { + DCHECK_NE(shill::kTypeVPN, network_type); + return GetImageForIndex(ImageTypeForNetworkType(network_type), icon_type, + connected ? kNumNetworkImages - 1 : 0); +} + gfx::ImageSkia GetImageForNonVirtualNetwork(const NetworkState* network, IconType icon_type, bool show_vpn_badge, @@ -478,7 +479,7 @@ if (!network->visible()) { if (animating) *animating = false; - return GetBasicImage(false /* is_connected */, icon_type, network_type); + return GetBasicImage(icon_type, network_type, false /* connected */); } if (network->IsConnectingState()) { @@ -520,7 +521,7 @@ } gfx::ImageSkia image = - GetBasicImage(true /* connected */, icon_type, shill::kTypeWifi); + GetBasicImage(icon_type, shill::kTypeWifi, true /* connected */); Badges badges; if (!enabled) { badges.center = {&kNetworkBadgeOffIcon, @@ -555,7 +556,7 @@ gfx::ImageSkia GetDisconnectedImageForNetworkType( const std::string& network_type) { - return GetBasicImage(false /* not connected */, ICON_TYPE_LIST, network_type); + return GetBasicImage(ICON_TYPE_LIST, network_type, false /* connected */); } gfx::ImageSkia GetImageForNewWifiNetwork(SkColor icon_color, @@ -571,13 +572,7 @@ base::string16 GetLabelForNetwork(const chromeos::NetworkState* network, IconType icon_type) { - if (!network) { - int uninitialized_msg = GetCellularUninitializedMsg(); - if (uninitialized_msg != 0) - return l10n_util::GetStringUTF16(uninitialized_msg); - return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED); - } - + DCHECK(network); std::string activation_state = network->activation_state(); if (icon_type == ICON_TYPE_LIST || icon_type == ICON_TYPE_MENU_LIST) { // Show "<network>: [Connecting|Activating|Reconnecting]..." @@ -665,107 +660,6 @@ return 0; } -const NetworkState* GetDefaultNetworkForIcon() { - NetworkStateHandler* network_state_handler = - NetworkHandler::Get()->network_state_handler(); - const NetworkState* connected_network = - network_state_handler->ConnectedNetworkByType( - NetworkTypePattern::NonVirtual()); - const NetworkState* connecting_network = - network_state_handler->ConnectingNetworkByType( - NetworkTypePattern::Wireless()); - - // If we are connecting to a network, and there is either no connected - // network, or the connection was user requested, or shill triggered a - // reconnection, use the connecting network. - if (connecting_network && - (!connected_network || connecting_network->IsReconnecting() || - connecting_network->connect_requested())) { - return connecting_network; - } - - if (connected_network) - return connected_network; - - const NetworkState* cellular = - network_state_handler->FirstNetworkByType(NetworkTypePattern::Cellular()); - if (cellular && - cellular->activation_state() == shill::kActivationStateActivating) { - return cellular; - } - - return nullptr; -} - -void GetDefaultNetworkImageAndLabel(IconType icon_type, - gfx::ImageSkia* image, - base::string16* label, - bool* animating) { - NetworkStateHandler* network_state_handler = - NetworkHandler::Get()->network_state_handler(); - - const NetworkState* network = GetDefaultNetworkForIcon(); - if (label) - *label = GetLabelForNetwork(network, icon_type); - - if (!network) { - // If no network, check for cellular initializing. - if (GetCellularUninitializedMsg() != 0) { - *image = - GetConnectingImageForNetworkType(shill::kTypeCellular, icon_type); - if (animating) - *animating = true; - return; - } - // Otherwise show a WiFi icon. - if (network_state_handler->IsTechnologyEnabled( - NetworkTypePattern::WiFi())) { - // WiFi is enabled but disconnected, show an empty wedge. - *image = - GetBasicImage(false /* not connected */, icon_type, shill::kTypeWifi); - } else { - // WiFi is disabled, show a full icon with a strikethrough. - *image = GetImageForWiFiEnabledState(false /* not enabled*/, icon_type); - } - if (animating) - *animating = false; - return; - } - - // Get the active (connecting or connected) VPN for badging and determining - // whether to show the Ethernet icon. - const NetworkState* active_vpn = nullptr; - if (network->IsConnectedState()) { - active_vpn = - network_state_handler->FirstNetworkByType(NetworkTypePattern::VPN()); - if (active_vpn && !active_vpn->IsConnectingOrConnected()) - active_vpn = nullptr; - } - - // Don't show connected Ethernet in the tray unless a VPN is present. - if (IsTrayIcon(icon_type) && - network->Matches(NetworkTypePattern::Ethernet()) && !active_vpn) { - *image = gfx::ImageSkia(); - if (animating) - *animating = false; - return; - } - - // Connected network with a connecting VPN. - if (network->IsConnectedState() && active_vpn && - active_vpn->IsConnectingState()) { - *image = GetConnectedNetworkWithConnectingVpnImage(network, icon_type); - if (animating) - *animating = true; - return; - } - - // Default behavior: connected or connecting network, possibly with VPN badge. - bool show_vpn_badge = !!active_vpn; - *image = GetImageForNonVirtualNetwork(network, icon_type, show_vpn_badge, - animating); -} - void PurgeNetworkIconCache() { NetworkStateHandler::NetworkStateList networks; NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkList(
diff --git a/ash/system/network/network_icon.h b/ash/system/network/network_icon.h index c6fca38..f0eba5c 100644 --- a/ash/system/network/network_icon.h +++ b/ash/system/network/network_icon.h
@@ -32,6 +32,12 @@ // Strength of a wireless signal. enum class SignalStrength { NONE, WEAK, MEDIUM, STRONG, NOT_WIRELESS }; +// Returns an image to represent either a fully connected network or a +// disconnected network. +const gfx::ImageSkia GetBasicImage(IconType icon_type, + const std::string& network_type, + bool connected); + // Returns and caches an image for non VPN |network| which must not be null. // Use this for non virtual networks and for the default (tray) icon. // |icon_type| determines the color theme. @@ -50,22 +56,28 @@ IconType icon_type, bool* animating = nullptr); -// Gets an image for a Wi-Fi network, either full strength or strike-through +// Returns an image for a Wi-Fi network, either full strength or strike-through // based on |enabled|. ASH_EXPORT gfx::ImageSkia GetImageForWiFiEnabledState( bool enabled, IconType = ICON_TYPE_DEFAULT_VIEW); -// Gets the connecting image for a shill network non-VPN type. +// Returns the connecting image for a shill network non-VPN type. gfx::ImageSkia GetConnectingImageForNetworkType(const std::string& network_type, IconType icon_type); -// Gets the disconnected image for a shill network type. +// Returns the connected image for |connected_network| and |network_type| with a +// connecting VPN badge. +gfx::ImageSkia GetConnectedNetworkWithConnectingVpnImage( + const chromeos::NetworkState* connected_network, + IconType icon_type); + +// Returns the disconnected image for a shill network type. gfx::ImageSkia GetDisconnectedImageForNetworkType( const std::string& network_type); -// Gets the full strength image for a Wi-Fi network using |icon_color| for the -// main icon and |badge_color| for the badge. +// Returns the full strength image for a Wi-Fi network using |icon_color| for +// the main icon and |badge_color| for the badge. ASH_EXPORT gfx::ImageSkia GetImageForNewWifiNetwork(SkColor icon_color, SkColor badge_color); @@ -79,13 +91,6 @@ // is uninitialized. ASH_EXPORT int GetCellularUninitializedMsg(); -// Sets the |icon| and |label| for |icon_type|. |animating| is an optional out -// parameter that is set to true when the returned image should be animated. -ASH_EXPORT void GetDefaultNetworkImageAndLabel(IconType icon_type, - gfx::ImageSkia* image, - base::string16* label, - bool* animating = nullptr); - // Called when the list of networks changes. Retrieves the list of networks // from the global NetworkStateHandler instance and removes cached entries // that are no longer in the list.
diff --git a/ash/system/network/network_icon_unittest.cc b/ash/system/network/network_icon_unittest.cc index eebc61aa..2843a6f 100644 --- a/ash/system/network/network_icon_unittest.cc +++ b/ash/system/network/network_icon_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include "ash/strings/grit/ash_strings.h" +#include "ash/system/network/active_network_icon.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -23,6 +24,9 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/image/image_unittest_util.h" +// This tests both the helper functions in network_icon, and ActiveNetworkIcon +// which is a primary consumer of the helper functions. + namespace ash { namespace network_icon { @@ -47,15 +51,18 @@ chromeos::NetworkHandler::Initialize(); base::RunLoop().RunUntilIdle(); + + active_network_icon_ = std::make_unique<ActiveNetworkIcon>(); } void TearDown() override { + active_network_icon_.reset(); PurgeNetworkIconCache(); + chromeos::NetworkHandler::Shutdown(); ShutdownNetworkState(); chromeos::NetworkStateTest::TearDown(); - chromeos::DBusThreadManager::Shutdown(); } @@ -113,6 +120,14 @@ return gfx::Image(image_skia); } + void GetDefaultNetworkImageAndLabel(IconType icon_type, + gfx::ImageSkia* image, + base::string16* label, + bool* animating) { + *image = active_network_icon_->GetSingleImage(icon_type, animating); + *label = active_network_icon_->GetDefaultLabel(icon_type); + } + // The icon for a Tether network should be the same as one for a cellular // network. The icon for a Tether network should be different from one for a // Wi-Fi network. The icon for a cellular network should be different from one @@ -184,6 +199,8 @@ std::string wifi2_path_; std::string cellular_path_; + std::unique_ptr<ActiveNetworkIcon> active_network_icon_; + DISALLOW_COPY_AND_ASSIGN(NetworkIconTest); }; @@ -280,8 +297,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); EXPECT_EQ( @@ -356,8 +373,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -378,8 +395,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); @@ -413,8 +430,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -450,8 +467,8 @@ // Verify that the default network is connecting icon for the initial default // network (even though the default network as reported by shill actually // changed). - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); @@ -474,8 +491,8 @@ std::unique_ptr<chromeos::NetworkState> reference_network_2 = CreateStandaloneNetworkState("reference2", shill::kTypeCellular, shill::kStateOnline, 65); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -490,8 +507,8 @@ std::unique_ptr<chromeos::NetworkState> reference_network_3 = CreateStandaloneNetworkState("reference3", shill::kTypeWifi, shill::kStateOnline, 45); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -518,8 +535,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -555,8 +572,8 @@ // another network connected and used as default. // TODO(tbarzic): Consider changing network icon logic to use a connected // network icon if a network is connected while a network is reconnecting. - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); @@ -571,8 +588,8 @@ SetServiceProperty(cellular_path(), shill::kStateProperty, base::Value(shill::kStateReady)); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); std::unique_ptr<chromeos::NetworkState> reference_network_2 = @@ -586,8 +603,8 @@ SetServiceProperty(cellular_path(), shill::kStateProperty, base::Value(shill::kStateOnline)); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); EXPECT_TRUE(gfx::test::AreImagesEqual( @@ -596,7 +613,7 @@ // Tests that the default network image shows a cellular network icon if // cellular network is connected while wifi is connecting. -TEST_F(NetworkIconTest, DefaultImageConnectingToWifiWileCellularConnected) { +TEST_F(NetworkIconTest, DefaultImageConnectingToWifiWhileCellularConnected) { // Connect cellular network, and set the wifi as connecting. SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty, base::Value(45)); @@ -611,8 +628,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -623,7 +640,7 @@ gfx::Image(default_image), ImageForNetwork(reference_network.get()))); } -// Test that a cellular icon is displayed when activating cellular +// Test that a connecting cellular icon is displayed when activating a cellular // network (if other networks are not connected). TEST_F(NetworkIconTest, DefaultNetworkImageActivatingCellularNetwork) { SetServiceProperty(cellular_path(), shill::kSignalStrengthProperty, @@ -634,8 +651,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -663,8 +680,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -692,8 +709,8 @@ base::Value(45)); // With Ethernet and WiFi connected, the default icon should be empty. - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_TRUE(default_image.isNull()); EXPECT_FALSE(animating); @@ -703,8 +720,8 @@ ASSERT_FALSE(vpn_path.empty()); // When a VPN is connected, the default icon should be Ethernet with a badge. - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -724,8 +741,8 @@ // Disconnect Ethernet. The default icon should become WiFi with a badge. SetServiceProperty(ethernet_path, shill::kStateProperty, base::Value(shill::kStateIdle)); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_FALSE(animating); @@ -740,8 +757,8 @@ // Set the VPN to connecting; the default icon should be animating. SetServiceProperty(vpn_path, shill::kStateProperty, base::Value(shill::kStateAssociation)); - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); } @@ -760,8 +777,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating); @@ -787,8 +804,8 @@ gfx::ImageSkia default_image; base::string16 label; bool animating = false; - ash::network_icon::GetDefaultNetworkImageAndLabel(icon_type_, &default_image, - &label, &animating); + GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label, + &animating); ASSERT_FALSE(default_image.isNull()); EXPECT_TRUE(animating);
diff --git a/ash/system/network/network_tray_icon_strategy.cc b/ash/system/network/network_tray_icon_strategy.cc index b653342fc..c6f41503 100644 --- a/ash/system/network/network_tray_icon_strategy.cc +++ b/ash/system/network/network_tray_icon_strategy.cc
@@ -6,6 +6,8 @@ #include "ash/session/session_controller.h" #include "ash/shell.h" +#include "ash/system/model/system_tray_model.h" +#include "ash/system/network/active_network_icon.h" #include "ash/system/network/network_icon.h" #include "base/logging.h" #include "chromeos/network/network_state.h" @@ -24,30 +26,6 @@ namespace { -const NetworkState* GetConnectingOrConnectedNetwork( - NetworkTypePattern pattern) { - NetworkStateHandler* state_handler = - NetworkHandler::Get()->network_state_handler(); - const NetworkState* connecting_network = - state_handler->ConnectingNetworkByType(pattern); - const NetworkState* connected_network = - state_handler->ConnectedNetworkByType(pattern); - // If we are connecting to a network, and there is either no connected - // network, or the connection was user requested, or shill triggered a - // reconnection, use the connecting network. - if (connecting_network && - (!connected_network || connecting_network->IsReconnecting() || - connecting_network->connect_requested())) { - return connecting_network; - } - return connected_network; -} - -bool NetworkTypeEnabled(NetworkTypePattern pattern) { - return NetworkHandler::Get()->network_state_handler()->IsTechnologyEnabled( - pattern); -} - // OOBE has a white background that makes regular tray icons not visible. network_icon::IconType GetIconType() { if (Shell::Get()->session_controller()->GetSessionState() == @@ -60,55 +38,24 @@ } // namespace gfx::ImageSkia DefaultNetworkTrayIconStrategy::GetNetworkIcon(bool* animating) { - if (!NetworkTypeEnabled(NetworkTypePattern::WiFi())) - return gfx::ImageSkia(); - - auto icon_type = GetIconType(); - const NetworkState* network = - GetConnectingOrConnectedNetwork(NetworkTypePattern::WiFi()); - if (network) { - bool show_vpn_badge = - network->IsConnectedState() && - NetworkHandler::Get()->network_state_handler()->ConnectedNetworkByType( - NetworkTypePattern::VPN()); - return network_icon::GetImageForNonVirtualNetwork( - network, icon_type, show_vpn_badge, animating); - } - *animating = false; - return network_icon::GetDisconnectedImageForNetworkType(shill::kTypeWifi); + return Shell::Get() + ->system_tray_model() + ->active_network_icon() + ->GetDualImagePrimary(GetIconType(), animating); } gfx::ImageSkia MobileNetworkTrayIconStrategy::GetNetworkIcon(bool* animating) { - if (!NetworkTypeEnabled(NetworkTypePattern::Mobile())) - return gfx::ImageSkia(); - - auto icon_type = GetIconType(); - // Check if we are initializing a mobile network. - if (network_icon::GetCellularUninitializedMsg()) { - *animating = true; - return network_icon::GetConnectingImageForNetworkType(shill::kTypeCellular, - icon_type); - } - - const NetworkState* network = - NetworkHandler::Get()->network_state_handler()->FirstNetworkByType( - NetworkTypePattern::Mobile()); - - if (network && network->IsConnectingOrConnected()) { - return network_icon::GetImageForNonVirtualNetwork( - network, icon_type, false /* show_vpn_badge */, animating); - } - - *animating = false; - return network_icon::GetDisconnectedImageForNetworkType(shill::kTypeCellular); + return Shell::Get() + ->system_tray_model() + ->active_network_icon() + ->GetDualImageCellular(GetIconType(), animating); } gfx::ImageSkia SingleNetworkTrayIconStrategy::GetNetworkIcon(bool* animating) { - auto icon_type = GetIconType(); - gfx::ImageSkia image; - network_icon::GetDefaultNetworkImageAndLabel(icon_type, &image, - /*label=*/nullptr, animating); - return image; + return Shell::Get() + ->system_tray_model() + ->active_network_icon() + ->GetSingleImage(GetIconType(), animating); } } // namespace tray
diff --git a/ash/system/night_light/night_light_controller.cc b/ash/system/night_light/night_light_controller.cc index 697badf..c428eb9 100644 --- a/ash/system/night_light/night_light_controller.cc +++ b/ash/system/night_light/night_light_controller.cc
@@ -70,34 +70,39 @@ base::Time GetSunsetTime() const override { return GetSunRiseSet(false); } base::Time GetSunriseTime() const override { return GetSunRiseSet(true); } void SetGeoposition(mojom::SimpleGeopositionPtr position) override { - position_ = std::move(position); + geoposition_ = std::move(position); } + bool HasGeoposition() const override { return !!geoposition_; } private: + // Note that the below computation is intentionally performed every time + // GetSunsetTime() or GetSunriseTime() is called rather than once whenever we + // receive a geoposition (which happens at least once a day). This increases + // the chances of getting accurate values, especially around DST changes. base::Time GetSunRiseSet(bool sunrise) const { - if (!ValidatePosition()) { + if (!HasGeoposition()) { LOG(ERROR) << "Invalid geoposition. Using default time for " << (sunrise ? "sunrise." : "sunset."); return sunrise ? TimeOfDay(kDefaultEndTimeOffsetMinutes).ToTimeToday() : TimeOfDay(kDefaultStartTimeOffsetMinutes).ToTimeToday(); } - icu::CalendarAstronomer astro(position_->longitude, position_->latitude); + icu::CalendarAstronomer astro(geoposition_->longitude, + geoposition_->latitude); // For sunset and sunrise times calculations to be correct, the time of the // icu::CalendarAstronomer object should be set to a time near local noon. // This avoids having the computation flopping over into an adjacent day. // See the documentation of icu::CalendarAstronomer::getSunRiseSet(). // Note that the icu calendar works with milliseconds since epoch, and // base::Time::FromDoubleT() / ToDoubleT() work with seconds since epoch. - const double noon_today_sec = TimeOfDay(12 * 60).ToTimeToday().ToDoubleT(); - astro.setTime(noon_today_sec * 1000.0); + const double midday_today_sec = + TimeOfDay(12 * 60).ToTimeToday().ToDoubleT(); + astro.setTime(midday_today_sec * 1000.0); const double sun_rise_set_ms = astro.getSunRiseSet(sunrise); return base::Time::FromDoubleT(sun_rise_set_ms / 1000.0); } - bool ValidatePosition() const { return !!position_; } - - mojom::SimpleGeopositionPtr position_; + mojom::SimpleGeopositionPtr geoposition_; DISALLOW_COPY_AND_ASSIGN(NightLightControllerDelegateImpl); }; @@ -329,6 +334,10 @@ registry->RegisterIntegerPref(prefs::kNightLightCustomEndTime, kDefaultEndTimeOffsetMinutes, PrefRegistry::PUBLIC); + + // Non-public prefs, only meant to be used by ash. + registry->RegisterDoublePref(prefs::kNightLightCachedLatitude, 0.0); + registry->RegisterDoublePref(prefs::kNightLightCachedLongitude, 0.0); } // static @@ -472,6 +481,9 @@ void NightLightController::SetCurrentGeoposition( mojom::SimpleGeopositionPtr position) { VLOG(1) << "Received new geoposition."; + + is_current_geoposition_from_cache_ = false; + StoreCachedGeoposition(position); delegate_->SetGeoposition(std::move(position)); // If the schedule type is sunset to sunrise, then a potential change in the @@ -502,6 +514,52 @@ delegate_ = std::move(delegate); } +void NightLightController::LoadCachedGeopositionIfNeeded() { + DCHECK(active_user_pref_service_); + + // Even if there is a geoposition, but it's coming from a previously cached + // value, switching users should load the currently saved values for the + // new user. This is to keep users' prefs completely separate. We only ignore + // the cached values once we have a valid non-cached geoposition from any + // user in the same session. + if (delegate_->HasGeoposition() && !is_current_geoposition_from_cache_) + return; + + if (!active_user_pref_service_->HasPrefPath( + prefs::kNightLightCachedLatitude) || + !active_user_pref_service_->HasPrefPath( + prefs::kNightLightCachedLongitude)) { + VLOG(1) << "No valid current geoposition and no valid cached geoposition" + " are available. Will use default times for sunset / sunrise."; + return; + } + + VLOG(1) << "Temporarily using a previously cached geoposition."; + delegate_->SetGeoposition(mojom::SimpleGeoposition::New( + active_user_pref_service_->GetDouble(prefs::kNightLightCachedLatitude), + active_user_pref_service_->GetDouble(prefs::kNightLightCachedLongitude))); + is_current_geoposition_from_cache_ = true; +} + +void NightLightController::StoreCachedGeoposition( + const mojom::SimpleGeopositionPtr& position) { + DCHECK(position); + + const SessionController* session_controller = + Shell::Get()->session_controller(); + for (const auto& user_session : session_controller->GetUserSessions()) { + PrefService* pref_service = session_controller->GetUserPrefServiceForUser( + user_session->user_info->account_id); + if (!pref_service) + continue; + + pref_service->SetDouble(prefs::kNightLightCachedLatitude, + position->latitude); + pref_service->SetDouble(prefs::kNightLightCachedLongitude, + position->longitude); + } +} + void NightLightController::RefreshLayersTemperature() { const float new_temperature = GetEnabled() ? GetColorTemperature() : 0.0f; temperature_animation_->AnimateToNewValue( @@ -545,10 +603,15 @@ prefs::kNightLightCustomEndTime, base::BindRepeating(&NightLightController::OnCustomSchedulePrefsChanged, base::Unretained(this))); + + // Note: No need to observe changes in the cached latitude/longitude since + // they're only accessed here in ash. We only load them when the active user + // changes, and store them whenever we receive an updated geoposition. } void NightLightController::InitFromUserPrefs() { StartWatchingPrefsChanges(); + LoadCachedGeopositionIfNeeded(); Refresh(true /* did_schedule_change */); NotifyStatusChanged(); NotifyClientWithScheduleChange();
diff --git a/ash/system/night_light/night_light_controller.h b/ash/system/night_light/night_light_controller.h index 1452b41..dd01ee03 100644 --- a/ash/system/night_light/night_light_controller.h +++ b/ash/system/night_light/night_light_controller.h
@@ -74,6 +74,9 @@ // Provides the delegate with the geoposition so that it can be used to // calculate sunset and sunrise times. virtual void SetGeoposition(mojom::SimpleGeopositionPtr position) = 0; + + // Returns true if a geoposition value is available. + virtual bool HasGeoposition() const = 0; }; class Observer { @@ -119,6 +122,9 @@ return last_animation_duration_; } base::OneShotTimer* timer() { return &timer_; } + bool is_current_geoposition_from_cache() const { + return is_current_geoposition_from_cache_; + } void BindRequest(mojom::NightLightControllerRequest request); @@ -163,6 +169,15 @@ void SetDelegateForTesting(std::unique_ptr<Delegate> delegate); private: + // Called only when the active user changes in order to see if we need to use + // a previously cached geoposition value from the active user's prefs. + void LoadCachedGeopositionIfNeeded(); + + // Called whenever we receive a new geoposition update to cache it in all + // logged-in users' prefs so that it can be used later in the event of not + // being able to retrieve a valid geoposition. + void StoreCachedGeoposition(const mojom::SimpleGeopositionPtr& position); + void RefreshLayersTemperature(); void StartWatchingPrefsChanges(); @@ -224,6 +239,15 @@ // type is either kSunsetToSunrise or kCustom. base::OneShotTimer timer_; + // True if the current geoposition value used by the Delegate is from a + // previously cached value in the user prefs of any of the users in the + // current session. It is reset to false once we receive a newly-updated + // geoposition from the client. + // This is used to treat the current geoposition as temporary until we receive + // a valid geoposition update, and also not to let a cached geoposition value + // to leak to another user for privacy reasons. + bool is_current_geoposition_from_cache_ = false; + // The registrar used to watch NightLight prefs changes in the above // |active_user_pref_service_| from outside ash. // NOTE: Prefs are how Chrome communicates changes to the NightLight settings
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc index 4d9b6510..e3feb4f 100644 --- a/ash/system/night_light/night_light_controller_unittest.cc +++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -117,6 +117,7 @@ base::Time GetSunsetTime() const override { return fake_sunset_; } base::Time GetSunriseTime() const override { return fake_sunrise_; } void SetGeoposition(mojom::SimpleGeopositionPtr position) override { + has_geoposition_ = true; if (position.Equals(mojom::SimpleGeoposition::New( kFakePosition1_Latitude, kFakePosition1_Longitude))) { // Set sunset and sunrise times associated with fake position 1. @@ -129,11 +130,13 @@ SetFakeSunrise(TimeOfDay(kFakePosition2_SunriseOffset)); } } + bool HasGeoposition() const override { return has_geoposition_; } private: base::Time fake_now_; base::Time fake_sunset_; base::Time fake_sunrise_; + bool has_geoposition_ = false; DISALLOW_COPY_AND_ASSIGN(TestDelegate); }; @@ -638,6 +641,101 @@ controller->timer()->GetCurrentDelay()); } +// Tests the behavior when there is no valid geoposition for example due to lack +// of connectivity. +TEST_F(NightLightTest, AbsentValidGeoposition) { + NightLightController* controller = GetController(); + ASSERT_FALSE(delegate()->HasGeoposition()); + + // Initially, no values are stored in either of the two users' prefs. + ASSERT_FALSE( + user1_pref_service()->HasPrefPath(prefs::kNightLightCachedLatitude)); + ASSERT_FALSE( + user1_pref_service()->HasPrefPath(prefs::kNightLightCachedLongitude)); + ASSERT_FALSE( + user2_pref_service()->HasPrefPath(prefs::kNightLightCachedLatitude)); + ASSERT_FALSE( + user2_pref_service()->HasPrefPath(prefs::kNightLightCachedLongitude)); + + // Store fake geoposition 2 in user 2's prefs. + user2_pref_service()->SetDouble(prefs::kNightLightCachedLatitude, + kFakePosition2_Latitude); + user2_pref_service()->SetDouble(prefs::kNightLightCachedLongitude, + kFakePosition2_Longitude); + + // Switch to user 2 and expect that the delegate now has a geoposition, but + // the controller knows that it's from a cached value. + SwitchActiveUser(kUser2Email); + EXPECT_TRUE(delegate()->HasGeoposition()); + EXPECT_TRUE(controller->is_current_geoposition_from_cache()); + const TimeOfDay kSunset2{kFakePosition2_SunsetOffset}; + const TimeOfDay kSunrise2{kFakePosition2_SunriseOffset}; + EXPECT_EQ(delegate()->GetSunsetTime(), kSunset2.ToTimeToday()); + EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise2.ToTimeToday()); + + // Store fake geoposition 1 in user 1's prefs. + user1_pref_service()->SetDouble(prefs::kNightLightCachedLatitude, + kFakePosition1_Latitude); + user1_pref_service()->SetDouble(prefs::kNightLightCachedLongitude, + kFakePosition1_Longitude); + + // Switching to user 1 should ignore the current geoposition since it's + // a cached value from user 2's prefs rather than a newly-updated value. + // User 1's cached values should be loaded. + SwitchActiveUser(kUser1Email); + EXPECT_TRUE(delegate()->HasGeoposition()); + EXPECT_TRUE(controller->is_current_geoposition_from_cache()); + const TimeOfDay kSunset1{kFakePosition1_SunsetOffset}; + const TimeOfDay kSunrise1{kFakePosition1_SunriseOffset}; + EXPECT_EQ(delegate()->GetSunsetTime(), kSunset1.ToTimeToday()); + EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise1.ToTimeToday()); + + // Now simulate receiving a geoposition update of fake geoposition 2. + controller->SetCurrentGeoposition(mojom::SimpleGeoposition::New( + kFakePosition2_Latitude, kFakePosition2_Longitude)); + EXPECT_TRUE(delegate()->HasGeoposition()); + EXPECT_FALSE(controller->is_current_geoposition_from_cache()); + EXPECT_EQ(delegate()->GetSunsetTime(), kSunset2.ToTimeToday()); + EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise2.ToTimeToday()); + + // Update user 2's prefs with fake geoposition 1. + user2_pref_service()->SetDouble(prefs::kNightLightCachedLatitude, + kFakePosition1_Latitude); + user2_pref_service()->SetDouble(prefs::kNightLightCachedLongitude, + kFakePosition1_Longitude); + + // Now switching to user 2 should completely ignore their cached geopsoition, + // since from now on we have a valid newly-retrieved value. + SwitchActiveUser(kUser2Email); + EXPECT_TRUE(delegate()->HasGeoposition()); + EXPECT_FALSE(controller->is_current_geoposition_from_cache()); + EXPECT_EQ(delegate()->GetSunsetTime(), kSunset2.ToTimeToday()); + EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise2.ToTimeToday()); + + // Clear all cached geoposition prefs for all users, just to make sure getting + // a new geoposition with persist it for all users not just the active one. + user1_pref_service()->ClearPref(prefs::kNightLightCachedLatitude); + user1_pref_service()->ClearPref(prefs::kNightLightCachedLongitude); + user2_pref_service()->ClearPref(prefs::kNightLightCachedLatitude); + user2_pref_service()->ClearPref(prefs::kNightLightCachedLongitude); + + // Now simulate receiving a geoposition update of fake geoposition 1. + controller->SetCurrentGeoposition(mojom::SimpleGeoposition::New( + kFakePosition1_Latitude, kFakePosition1_Longitude)); + EXPECT_TRUE(delegate()->HasGeoposition()); + EXPECT_FALSE(controller->is_current_geoposition_from_cache()); + EXPECT_EQ(delegate()->GetSunsetTime(), kSunset1.ToTimeToday()); + EXPECT_EQ(delegate()->GetSunriseTime(), kSunrise1.ToTimeToday()); + EXPECT_EQ(kFakePosition1_Latitude, + user1_pref_service()->GetDouble(prefs::kNightLightCachedLatitude)); + EXPECT_EQ(kFakePosition1_Longitude, + user1_pref_service()->GetDouble(prefs::kNightLightCachedLongitude)); + EXPECT_EQ(kFakePosition1_Latitude, + user2_pref_service()->GetDouble(prefs::kNightLightCachedLatitude)); + EXPECT_EQ(kFakePosition1_Longitude, + user2_pref_service()->GetDouble(prefs::kNightLightCachedLongitude)); +} + // Tests that on device resume from sleep, the NightLight status is updated // correctly if the time has changed meanwhile. TEST_F(NightLightTest, TestCustomScheduleOnResume) {
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc index e4735d7..e5056f34 100644 --- a/ash/system/overview/overview_button_tray.cc +++ b/ash/system/overview/overview_button_tray.cc
@@ -22,6 +22,7 @@ #include "base/metrics/user_metrics_action.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/paint_vector_icon.h" +#include "ui/views/animation/ink_drop.h" #include "ui/views/border.h" #include "ui/views/controls/image_view.h" #include "ui/wm/core/window_util.h" @@ -63,6 +64,10 @@ UpdateIconVisibility(); } +void OverviewButtonTray::SnapRippleToActivated() { + GetInkDrop()->SnapToActivated(); +} + void OverviewButtonTray::OnGestureEvent(ui::GestureEvent* event) { Button::OnGestureEvent(event); if (event->type() == ui::ET_GESTURE_LONG_PRESS) {
diff --git a/ash/system/overview/overview_button_tray.h b/ash/system/overview/overview_button_tray.h index 47097fd..0d16b4b 100644 --- a/ash/system/overview/overview_button_tray.h +++ b/ash/system/overview/overview_button_tray.h
@@ -45,6 +45,9 @@ // state of TabletMode virtual void UpdateAfterLoginStatusChange(LoginStatus status); + // Sets the ink drop ripple to ACTIVATED immediately with no animations. + void SnapRippleToActivated(); + // views::Button: void OnGestureEvent(ui::GestureEvent* event) override;
diff --git a/ash/system/palette/palette_welcome_bubble.cc b/ash/system/palette/palette_welcome_bubble.cc index 474e6ac..2659adc 100644 --- a/ash/system/palette/palette_welcome_bubble.cc +++ b/ash/system/palette/palette_welcome_bubble.cc
@@ -37,7 +37,7 @@ WelcomeBubbleView(views::View* anchor, views::BubbleBorder::Arrow arrow) : views::BubbleDialogDelegateView(anchor, arrow) { set_close_on_deactivate(true); - set_can_activate(false); + SetCanActivate(false); set_accept_events(true); set_parent_window( anchor_widget()->GetNativeWindow()->GetRootWindow()->GetChildById(
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc index cd24bb4..3bd89d9b 100644 --- a/ash/system/tray/tray_bubble_view.cc +++ b/ash/system/tray/tray_bubble_view.cc
@@ -171,7 +171,7 @@ (key_code == ui::VKEY_ESCAPE && flags == ui::EF_NONE)) { // Make TrayBubbleView activatable as the following Widget::OnKeyEvent might // try to activate it. - tray_bubble_view_->set_can_activate(true); + tray_bubble_view_->SetCanActivate(true); tray_bubble_view_->GetWidget()->OnKeyEvent(event); @@ -218,7 +218,7 @@ if (init_params.corner_radius) bubble_border_->SetCornerRadius(init_params.corner_radius.value()); set_parent_window(params_.parent_window); - set_can_activate(false); + SetCanActivate(false); set_notify_enter_exit_on_child(true); set_close_on_deactivate(init_params.close_on_deactivate); set_margins(gfx::Insets()); @@ -480,7 +480,7 @@ // No need to explicitly activate the widget. View::RequestFocus will activate // it if necessary. - set_can_activate(true); + SetCanActivate(true); view->RequestFocus(); }
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc index 99a14d1f..aab15ac 100644 --- a/ash/system/unified/unified_system_tray_bubble.cc +++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -158,7 +158,7 @@ if (bubble_widget_->IsClosed()) return; - bubble_widget_->widget_delegate()->set_can_activate(true); + bubble_widget_->widget_delegate()->SetCanActivate(true); bubble_widget_->Activate(); }
diff --git a/ash/wallpaper/OWNERS b/ash/wallpaper/OWNERS index f8b0297..c4a642c 100644 --- a/ash/wallpaper/OWNERS +++ b/ash/wallpaper/OWNERS
@@ -1,4 +1,5 @@ achuith@chromium.org +maybelle@chromium.org wzang@chromium.org xdai@chromium.org
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc index 66e68ae..4115bf7 100644 --- a/ash/wm/immersive_fullscreen_controller_unittest.cc +++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -1021,14 +1021,14 @@ views::BubbleDialogDelegateView* bubble_delegate4( new TestBubbleDialogDelegate(child_view)); - bubble_delegate4->set_can_activate(false); + bubble_delegate4->SetCanActivate(false); views::Widget* bubble_widget4( views::BubbleDialogDelegateView::CreateBubble(bubble_delegate4)); bubble_widget4->Show(); views::BubbleDialogDelegateView* bubble_delegate5( new TestBubbleDialogDelegate(child_view)); - bubble_delegate5->set_can_activate(false); + bubble_delegate5->SetCanActivate(false); views::Widget* bubble_widget5( views::BubbleDialogDelegateView::CreateBubble(bubble_delegate5)); bubble_widget5->Show();
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc index 4e361502..3213665 100644 --- a/ash/wm/overview/overview_controller.cc +++ b/ash/wm/overview/overview_controller.cc
@@ -541,6 +541,12 @@ return windows; } +void OverviewController::DelayedUpdateMaskAndShadow() { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&OverviewController::UpdateMaskAndShadow, + weak_ptr_factory_.GetWeakPtr())); +} + // TODO(flackr): Make OverviewController observe the activation of // windows, so we can remove OverviewDelegate. // TODO(sammiequon): Rename to something like EndOverview() and refactor to use @@ -634,6 +640,11 @@ OnStartingAnimationComplete(/*canceled=*/false); } +void OverviewController::UpdateMaskAndShadow() { + if (overview_session_) + overview_session_->UpdateMaskAndShadow(); +} + // static void OverviewController::SetDoNotChangeWallpaperBlurForTests() { g_disable_wallpaper_blur_for_tests = true;
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h index 659cffb..5e88d1d 100644 --- a/ash/wm/overview/overview_controller.h +++ b/ash/wm/overview/overview_controller.h
@@ -68,6 +68,9 @@ // overview mode is active for testing. std::vector<aura::Window*> GetWindowsListInOverviewGridsForTesting(); + // Post a task to update the shadow and mask of overview windows. + void DelayedUpdateMaskAndShadow(); + // OverviewDelegate: void OnSelectionEnded() override; void AddDelayedAnimationObserver( @@ -115,6 +118,8 @@ void OnEndingAnimationComplete(bool canceled); void ResetPauser(); + void UpdateMaskAndShadow(); + // Collection of DelayedAnimationObserver objects that own widgets that may be // still animating after overview mode ends. If shell needs to shut down while // those animations are in progress, the animations are shut down and the
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc index 8357e06c..9946dfb 100644 --- a/ash/wm/overview/overview_grid.cc +++ b/ash/wm/overview/overview_grid.cc
@@ -17,6 +17,7 @@ #include "ash/public/cpp/window_properties.h" #include "ash/public/cpp/window_state_type.h" #include "ash/root_window_controller.h" +#include "ash/rotator/screen_rotation_animator.h" #include "ash/screen_util.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_constants.h" @@ -395,6 +396,8 @@ } void OverviewGrid::Shutdown() { + ScreenRotationAnimator::GetForRootWindow(root_window_)->RemoveObserver(this); + for (const auto& window : window_list_) window->Shutdown(); @@ -426,6 +429,11 @@ for (const auto& window : window_list_) window->PrepareForOverview(); prepared_for_overview_ = true; + if (Shell::Get() + ->tablet_mode_controller() + ->IsTabletModeWindowManagerEnabled()) { + ScreenRotationAnimator::GetForRootWindow(root_window_)->AddObserver(this); + } } void OverviewGrid::PositionWindows( @@ -897,6 +905,21 @@ } } +void OverviewGrid::OnScreenCopiedBeforeRotation() { + for (auto& window : window_list()) { + window->set_disable_mask(true); + window->UpdateMaskAndShadow(); + } +} + +void OverviewGrid::OnScreenRotationAnimationFinished( + ScreenRotationAnimator* animator, + bool canceled) { + for (auto& window : window_list()) + window->set_disable_mask(false); + Shell::Get()->overview_controller()->DelayedUpdateMaskAndShadow(); +} + void OverviewGrid::OnStartingAnimationComplete() { if (!shield_widget_) { InitShieldWidget(/*animate=*/true);
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h index b2cbe74..047ae51 100644 --- a/ash/wm/overview/overview_grid.h +++ b/ash/wm/overview/overview_grid.h
@@ -11,6 +11,7 @@ #include <set> #include <vector> +#include "ash/rotator/screen_rotation_animator_observer.h" #include "ash/wm/overview/overview_session.h" #include "ash/wm/window_state_observer.h" #include "base/macros.h" @@ -50,7 +51,8 @@ // The selector is switched to the next window grid (if available) or wrapped if // it reaches the end of its movement sequence. class ASH_EXPORT OverviewGrid : public aura::WindowObserver, - public wm::WindowStateObserver { + public wm::WindowStateObserver, + public ScreenRotationAnimatorObserver { public: OverviewGrid(aura::Window* root_window, const std::vector<aura::Window*>& window_list, @@ -148,6 +150,11 @@ void OnPostWindowStateTypeChange(wm::WindowState* window_state, mojom::WindowStateType old_type) override; + // ScreenRotationAnimatorObserver: + void OnScreenCopiedBeforeRotation() override; + void OnScreenRotationAnimationFinished(ScreenRotationAnimator* animator, + bool canceled) override; + // Called when overview starting animation completes. void OnStartingAnimationComplete();
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc index 0bc90b9..82a3d5b 100644 --- a/ash/wm/overview/overview_item.cc +++ b/ash/wm/overview/overview_item.cc
@@ -625,7 +625,8 @@ // 6) this overview item is in animation. bool should_show = true; OverviewController* overview_controller = Shell::Get()->overview_controller(); - if (!overview_controller || !overview_controller->IsSelecting() || + if (disable_mask_ || !overview_controller || + !overview_controller->IsSelecting() || overview_grid_->window_list().size() > 10 || overview_controller->IsInStartAnimation() || is_being_dragged_ || overview_grid_->IsDropTargetWindow(GetWindow()) ||
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h index 40f2140..fb0330d 100644 --- a/ash/wm/overview/overview_item.h +++ b/ash/wm/overview/overview_item.h
@@ -242,6 +242,8 @@ bool animating_to_close() const { return animating_to_close_; } void set_animating_to_close(bool val) { animating_to_close_ = val; } + void set_disable_mask(bool disable) { disable_mask_ = disable; } + float GetCloseButtonVisibilityForTesting() const; float GetTitlebarOpacityForTesting() const; gfx::Rect GetShadowBoundsForTesting(); @@ -340,6 +342,9 @@ // True if this overview item is currently being dragged around. bool is_being_dragged_ = false; + // True to always disable mask regardless of the state. + bool disable_mask_ = false; + // The shadow around the overview window. Shadows the original window, not // |item_widget_|. Done here instead of on the original window because of the // rounded edges mask applied on entering overview window.
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index a162ca8..7a88fbd1 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -574,11 +574,11 @@ void OverviewSession::OnStartingAnimationComplete(bool canceled) { if (!canceled) { - UpdateMaskAndShadow(); if (overview_focus_widget_) overview_focus_widget_->Show(); for (auto& grid : grid_list_) grid->OnStartingAnimationComplete(); + Shell::Get()->overview_controller()->DelayedUpdateMaskAndShadow(); } }
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc index fe87b0ea..e2d87a2 100644 --- a/ash/wm/overview/overview_session_unittest.cc +++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2238,6 +2238,11 @@ EXPECT_FALSE(HasMaskForItem(item2)); window1->layer()->GetAnimator()->StopAnimating(); window2->layer()->GetAnimator()->StopAnimating(); + + // Mask is set asynchronously. + EXPECT_FALSE(HasMaskForItem(item1)); + EXPECT_FALSE(HasMaskForItem(item2)); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(HasMaskForItem(item1)); EXPECT_TRUE(HasMaskForItem(item2)); @@ -2262,6 +2267,9 @@ ->layer() ->GetAnimator() ->StopAnimating(); + EXPECT_FALSE(HasMaskForItem(item1)); + EXPECT_FALSE(HasMaskForItem(item2)); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(HasMaskForItem(item1)); EXPECT_TRUE(HasMaskForItem(item2));
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc index 8273fc1..6e37ccc 100644 --- a/ash/wm/tablet_mode/tablet_mode_controller.cc +++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -294,6 +294,9 @@ } void TabletModeController::OnDisplayConfigurationChanged() { + if (!AllowUiModeChange()) + return; + if (!HasActiveInternalDisplay()) { AttemptLeaveTabletMode(); } else if (tablet_mode_switch_is_on_ && !IsTabletModeWindowManagerEnabled()) {
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc index e0fa102..1231a2c 100644 --- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -1080,6 +1080,29 @@ EXPECT_TRUE(AreEventsBlocked()); } +TEST_F(TabletModeControllerForceTabletModeTest, DockInForcedTabletMode) { + UpdateDisplay("800x600, 800x600"); + const int64_t internal_display_id = + display::test::DisplayManagerTestApi(display_manager()) + .SetFirstDisplayAsInternalDisplay(); + + // Deactivate internal display to simulate Docked Mode. + std::vector<display::ManagedDisplayInfo> all_displays; + all_displays.push_back(display_manager()->GetDisplayInfo( + display_manager()->GetDisplayAt(0).id())); + std::vector<display::ManagedDisplayInfo> secondary_only; + display::ManagedDisplayInfo secondary_display = + display_manager()->GetDisplayInfo( + display_manager()->GetDisplayAt(1).id()); + all_displays.push_back(secondary_display); + secondary_only.push_back(secondary_display); + display_manager()->OnNativeDisplaysChanged(secondary_only); + ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id)); + + // Still expect tablet mode. + EXPECT_TRUE(IsTabletModeStarted()); +} + class TabletModeControllerForceClamshellModeTest : public TabletModeControllerTest { public:
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc index 74fc9c3..4a3c8ff4 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -7,6 +7,8 @@ #include "ash/root_window_controller.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" +#include "ash/system/overview/overview_button_tray.h" +#include "ash/system/status_area_widget.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/overview/overview_grid.h" #include "ash/wm/overview/overview_item.h" @@ -108,6 +110,12 @@ // might open overview in the dragged window side of the screen. split_view_controller_->OnWindowDragStarted(dragged_window_); if (ShouldOpenOverviewWhenDragStarts() && !controller->IsSelecting()) { + OverviewButtonTray* overview_button_tray = + RootWindowController::ForWindow(dragged_window_) + ->GetStatusAreaWidget() + ->overview_button_tray(); + DCHECK(overview_button_tray); + overview_button_tray->SnapRippleToActivated(); controller->ToggleOverview( OverviewSession::EnterExitOverviewType::kWindowDragged); }
diff --git a/ash/wm/top_level_window_factory.cc b/ash/wm/top_level_window_factory.cc index 06f5d8c..e1941ac 100644 --- a/ash/wm/top_level_window_factory.cc +++ b/ash/wm/top_level_window_factory.cc
@@ -196,7 +196,7 @@ NonClientFrameController::Get(window); window->SetProperty(ws::kCanFocus, can_focus); if (non_client_frame_controller) - non_client_frame_controller->set_can_activate(can_focus); + non_client_frame_controller->SetCanActivate(can_focus); // No need to persist this value. properties->erase(focusable_iter); }
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc index a4698a12..cae029b 100644 --- a/ash/wm/window_state.cc +++ b/ash/wm/window_state.cc
@@ -492,7 +492,7 @@ if (IsPip()) { views::Widget::GetWidgetForNativeWindow(window()) ->widget_delegate() - ->set_can_activate(false); + ->SetCanActivate(false); } } @@ -687,7 +687,7 @@ // widget may not exit in some unit tests. // TODO(oshima): Fix unit tests and add DCHECK. if (widget) { - widget->widget_delegate()->set_can_activate(false); + widget->widget_delegate()->SetCanActivate(false); if (widget->IsActive()) widget->Deactivate(); Shell::Get()->focus_cycler()->AddWidget(widget); @@ -696,7 +696,7 @@ window(), WINDOW_VISIBILITY_ANIMATION_TYPE_FADE_IN_SLIDE_OUT); } else if (was_pip) { if (widget) { - widget->widget_delegate()->set_can_activate(true); + widget->widget_delegate()->SetCanActivate(true); Shell::Get()->focus_cycler()->RemoveWidget(widget); } ::wm::SetWindowVisibilityAnimationType(
diff --git a/base/fuchsia/filtered_service_directory.cc b/base/fuchsia/filtered_service_directory.cc index 732bc76..0fc3e22 100644 --- a/base/fuchsia/filtered_service_directory.cc +++ b/base/fuchsia/filtered_service_directory.cc
@@ -26,7 +26,7 @@ } void FilteredServiceDirectory::AddService(const char* service_name) { - outgoing_directory_->AddService( + outgoing_directory_->AddServiceUnsafe( service_name, base::BindRepeating(&FilteredServiceDirectory::HandleRequest, base::Unretained(this), service_name));
diff --git a/base/fuchsia/filtered_service_directory_unittest.cc b/base/fuchsia/filtered_service_directory_unittest.cc index 53e4e3f..6d5b27c 100644 --- a/base/fuchsia/filtered_service_directory_unittest.cc +++ b/base/fuchsia/filtered_service_directory_unittest.cc
@@ -19,7 +19,7 @@ filtered_service_directory_ = std::make_unique<FilteredServiceDirectory>( public_service_directory_client_.get()); filtered_client_ = std::make_unique<ServiceDirectoryClient>( - filtered_service_directory_->ConnectClient().TakeChannel()); + filtered_service_directory_->ConnectClient()); } protected:
diff --git a/base/fuchsia/service_directory_test_base.h b/base/fuchsia/service_directory_test_base.h index cb55036..bef8286 100644 --- a/base/fuchsia/service_directory_test_base.h +++ b/base/fuchsia/service_directory_test_base.h
@@ -8,8 +8,8 @@ #include <lib/zx/channel.h> #include <memory> -#include "base/fuchsia/component_context.h" #include "base/fuchsia/scoped_service_binding.h" +#include "base/fuchsia/service_directory_client.h" #include "base/fuchsia/test_interface_impl.h" #include "base/fuchsia/testfidl/cpp/fidl.h" #include "base/message_loop/message_loop.h"
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 91d076b..e7d4044 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -c0913f45105efa9685a2367400babb72ff6c13cd \ No newline at end of file +0b6ae643b2e5476f6db9f114a79d372fada8b17d \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 5f39dd2..88c251a 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -f8d3014a84247254f3310e093798d4f902afe1f0 \ No newline at end of file +bb7c5bd418e7cc9f638d2b6d835742bbce30487b \ No newline at end of file
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index 25bafa2..3b0833daf 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -820,7 +820,12 @@ } // If the layer uses a CSS filter. - if (!Filters(layer).IsEmpty() || !BackdropFilters(layer).IsEmpty()) { + if (!Filters(layer).IsEmpty()) { + return true; + } + + // If the layer uses a CSS backdrop-filter. + if (!BackdropFilters(layer).IsEmpty()) { return true; }
diff --git a/chrome/VERSION b/chrome/VERSION index bbe843e..373ea95 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=74 MINOR=0 -BUILD=3696 +BUILD=3697 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java index d7739ff..9f6b9a8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
@@ -13,9 +13,14 @@ import org.xmlpull.v1.XmlSerializer; import org.chromium.base.BuildInfo; +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.identity.SettingsSecureBasedIdentificationGenerator; import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory; +import org.chromium.chrome.browser.init.ProcessInitializationHandler; +import org.chromium.components.signin.AccountManagerFacade; +import org.chromium.components.signin.ChromeSigninController; import org.chromium.ui.base.DeviceFormFactor; import java.io.IOException; @@ -98,6 +103,12 @@ serializer.attribute(null, "lang", getLanguage()); serializer.attribute(null, "installage", String.valueOf(installAge)); serializer.attribute(null, "ap", getAdditionalParameters()); + // <code>_numaccounts</code> is actually number of profiles, which is always one for + // Chrome Android. + serializer.attribute(null, "_numaccounts", "1"); + serializer.attribute(null, "_numgoogleaccountsondevice", + String.valueOf(getNumGoogleAccountsOnDevice())); + serializer.attribute(null, "_numsignedin", String.valueOf(getNumSignedIn())); serializer.attribute( null, "_dl_mgr_disabled", String.valueOf(getDownloadManagerState())); @@ -177,6 +188,43 @@ } /** + * Returns the number of accounts on the device, bucketed into: + * 0 accounts, 1 account, or 2+ accounts. + * + * @return Number of accounts on the device, bucketed as above. + */ + @VisibleForTesting + public int getNumGoogleAccountsOnDevice() { + // RequestGenerator may be invoked from JobService or AlarmManager (through OmahaService), + // so have to make sure AccountManagerFacade instance is initialized. + ThreadUtils.runOnUiThreadBlocking( + () -> ProcessInitializationHandler.getInstance().initializePreNative()); + int numAccounts = 0; + try { + numAccounts = AccountManagerFacade.get().getGoogleAccounts().size(); + } catch (Exception e) { + Log.e(TAG, "Can't get number of accounts.", e); + } + switch (numAccounts) { + case 0: + return 0; + case 1: + return 1; + default: + return 2; + } + } + + /** + * Determine number of accounts signed in. + */ + @VisibleForTesting + public int getNumSignedIn() { + // We only have a single account. + return ChromeSigninController.get().isSignedIn() ? 1 : 0; + } + + /** * Returns DownloadManager system service enabled state as * -1 - manager state unknown * 0 - manager enabled
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java index 73336182..b6d445ff1 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -22,6 +22,7 @@ import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.omaha.MockRequestGenerator; import org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType; +import org.chromium.chrome.test.omaha.MockRequestGenerator.SignedInStatus; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -87,8 +88,8 @@ @Override protected RequestGenerator createRequestGenerator(Context context) { - mMockGenerator = new MockRequestGenerator( - context, mIsOnTablet ? DeviceType.TABLET : DeviceType.HANDSET); + mMockGenerator = new MockRequestGenerator(context, + mIsOnTablet ? DeviceType.TABLET : DeviceType.HANDSET, SignedInStatus.FALSE); return mMockGenerator; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java index 08aa4cd..1f8b308 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
@@ -22,6 +22,7 @@ import org.chromium.chrome.test.omaha.AttributeFinder; import org.chromium.chrome.test.omaha.MockRequestGenerator; import org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType; +import org.chromium.chrome.test.omaha.MockRequestGenerator.SignedInStatus; import org.chromium.components.signin.AccountManagerFacade; import org.chromium.components.signin.test.util.AccountHolder; import org.chromium.components.signin.test.util.FakeAccountManagerDelegate; @@ -76,7 +77,7 @@ UniqueIdentificationGeneratorFactory.clearGeneratorMapForTest(); // Creating a RequestGenerator should register the identification generator. - new MockRequestGenerator(context, DeviceType.HANDSET); + new MockRequestGenerator(context, DeviceType.HANDSET, SignedInStatus.FALSE); // Verify the identification generator exists and is of the correct type. UniqueIdentificationGenerator instance = UniqueIdentificationGeneratorFactory.getInstance( @@ -88,49 +89,88 @@ @SmallTest @Feature({"Omaha"}) public void testHandsetXMLCreationWithInstall() { - createAndCheckXML(DeviceType.HANDSET, true); + createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, true); } @Test @SmallTest @Feature({"Omaha"}) public void testHandsetXMLCreationWithoutInstall() { - createAndCheckXML(DeviceType.HANDSET, false); + createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, false); } @Test @SmallTest @Feature({"Omaha"}) public void testTabletXMLCreationWithInstall() { - createAndCheckXML(DeviceType.TABLET, true); + createAndCheckXML(DeviceType.TABLET, SignedInStatus.FALSE, true); } @Test @SmallTest @Feature({"Omaha"}) public void testTabletXMLCreationWithoutInstall() { - createAndCheckXML(DeviceType.TABLET, false); + createAndCheckXML(DeviceType.TABLET, SignedInStatus.FALSE, false); } @Test @SmallTest @Feature({"Omaha"}) public void testIsSignedIn() { - createAndCheckXML(DeviceType.HANDSET, false); + createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, false); } @Test @SmallTest @Feature({"Omaha"}) public void testIsNotSignedIn() { - createAndCheckXML(DeviceType.HANDSET, false); + createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, false); + } + + @Test + @SmallTest + @Feature({"Omaha"}) + public void testNoGoogleAccountsRetrieved() { + RequestGenerator generator = + createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, false); + Assert.assertEquals(0, generator.getNumGoogleAccountsOnDevice()); + } + + @Test + @SmallTest + @Feature({"Omaha"}) + public void testOneGoogleAccountRetrieved() { + RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, + false, new Account("clanktester@this.com", "com.google")); + Assert.assertEquals(1, generator.getNumGoogleAccountsOnDevice()); + } + + @Test + @SmallTest + @Feature({"Omaha"}) + public void testTwoGoogleAccountsRetrieved() { + RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, + false, new Account("clanktester@gmail.com", "com.google"), + new Account("googleguy@elsewhere.com", "com.google")); + Assert.assertEquals(2, generator.getNumGoogleAccountsOnDevice()); + } + + @Test + @SmallTest + @Feature({"Omaha"}) + public void testThreeGoogleAccountsExist() { + RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, + false, new Account("clanktester@gmail.com", "com.google"), + new Account("googleguy@elsewhere.com", "com.google"), + new Account("ImInATest@gmail.com", "com.google")); + Assert.assertEquals(2, generator.getNumGoogleAccountsOnDevice()); } /** * Checks that the XML is being created properly. */ - private RequestGenerator createAndCheckXML( - DeviceType deviceType, boolean sendInstallEvent, Account... accounts) { + private RequestGenerator createAndCheckXML(DeviceType deviceType, SignedInStatus signInStatus, + boolean sendInstallEvent, Account... accounts) { Context targetContext = InstrumentationRegistry.getTargetContext(); AdvancedMockContext context = new AdvancedMockContext(targetContext); @@ -146,7 +186,9 @@ String version = "1.2.3.4"; long installAge = 42; - MockRequestGenerator generator = new MockRequestGenerator(context, deviceType); + MockRequestGenerator generator = + new MockRequestGenerator(context, deviceType, signInStatus); + String xml = null; try { RequestData data = new RequestData(sendInstallEvent, 0, requestId, INSTALL_SOURCE); @@ -184,6 +226,12 @@ checkForAttributeAndValue(xml, "request", "userid", "{" + generator.getDeviceID() + "}"); + checkForAttributeAndValue(xml, "app", "_numaccounts", "1"); + checkForAttributeAndValue(xml, "app", "_numgoogleaccountsondevice", + String.valueOf(generator.getNumGoogleAccountsOnDevice())); + checkForAttributeAndValue( + xml, "app", "_numsignedin", String.valueOf(generator.getNumSignedIn())); + return generator; }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 113d3835..86301b0 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -765,8 +765,6 @@ "memory_details_win.cc", "metrics/antivirus_metrics_provider_win.cc", "metrics/antivirus_metrics_provider_win.h", - "metrics/bluetooth_available_utility.cc", - "metrics/bluetooth_available_utility.h", "metrics/browser_window_histogram_helper.cc", "metrics/browser_window_histogram_helper.h", "metrics/chrome_browser_main_extra_parts_metrics.cc",
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc index 7c8a5d3..3b4769a6 100644 --- a/chrome/browser/apps/app_service/app_icon_factory.cc +++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -148,7 +148,7 @@ content::BrowserContext* context, const std::string& extension_id, apps::mojom::Publisher::LoadIconCallback callback) { - int size_hint_in_px = ConvertDipToPx(size_hint_in_dip); + int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip); const extensions::Extension* extension = extensions::ExtensionSystem::Get(context)
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc index 6eeab21..655ae95 100644 --- a/chrome/browser/apps/app_service/app_icon_source.cc +++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -26,7 +26,7 @@ void LoadDefaultImage(const content::URLDataSource::GotDataCallback& callback) { base::StringPiece contents = ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale( - IDR_APP_DEFAULT_ICON, ui::SCALE_FACTOR_100P); + IDR_APP_DEFAULT_ICON, apps_util::GetPrimaryDisplayUIScaleFactor()); base::RefCountedBytes* image_bytes = new base::RefCountedBytes(); image_bytes->data().assign(contents.data(), @@ -88,7 +88,7 @@ LoadDefaultImage(callback); return; } - int size_in_dip = ConvertPxToDip(size); + int size_in_dip = apps_util::ConvertPxToDip(size); apps::AppServiceProxy* app_service_proxy = apps::AppServiceProxy::Get(profile_);
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc index 96418ad..62d2642 100644 --- a/chrome/browser/apps/app_service/arc_apps.cc +++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -331,11 +331,10 @@ // TODO(crbug.com/826982): process the app_id argument like the private // GetAppFromAppOrGroupId function and the ArcAppIcon::mapped_app_id_ field // in arc_app_icon.cc? - // - // TODO(crbug.com/826982): don't hard-code SCALE_FACTOR_100P. return prefs_->GetIconPath( - app_id, ArcAppIconDescriptor(size_hint_in_dip, - ui::ScaleFactor::SCALE_FACTOR_100P)); + app_id, + ArcAppIconDescriptor(size_hint_in_dip, + apps_util::GetPrimaryDisplayUIScaleFactor())); } void ArcApps::LoadIconFromVM(const std::string icon_key_s_key, @@ -347,9 +346,9 @@ if (app_info) { base::OnceCallback<void(apps::ArcApps::AppConnectionHolder*)> pending = base::BindOnce(&LoadIcon0, icon_compression, - ConvertDipToPx(size_hint_in_dip), app_info->package_name, - app_info->activity, app_info->icon_resource_id, - std::move(callback)); + apps_util::ConvertDipToPx(size_hint_in_dip), + app_info->package_name, app_info->activity, + app_info->icon_resource_id, std::move(callback)); AppConnectionHolder* app_connection_holder = prefs_->app_connection_holder(); @@ -369,7 +368,7 @@ int32_t size_hint_in_dip, LoadIconCallback callback) { // Use overloaded Chrome icon for Play Store that is adapted to Chrome style. - int size_hint_in_px = ConvertDipToPx(size_hint_in_dip); + int size_hint_in_px = apps_util::ConvertDipToPx(size_hint_in_dip); int resource_id = (size_hint_in_px <= 32) ? IDR_ARC_SUPPORT_ICON_32 : IDR_ARC_SUPPORT_ICON_192; LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id, @@ -400,6 +399,8 @@ ? apps::mojom::OptionalBool::kTrue : apps::mojom::OptionalBool::kFalse; + app->is_platform_app = apps::mojom::OptionalBool::kFalse; + auto show = app_info.show_in_launcher ? apps::mojom::OptionalBool::kTrue : apps::mojom::OptionalBool::kFalse; app->show_in_launcher = show;
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc index 9ab489e0..43dc0bb 100644 --- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc +++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -42,6 +42,7 @@ app->install_time = base::Time(); app->installed_internally = apps::mojom::OptionalBool::kTrue; + app->is_platform_app = apps::mojom::OptionalBool::kFalse; app->show_in_launcher = internal_app.show_in_launcher ? apps::mojom::OptionalBool::kTrue : apps::mojom::OptionalBool::kFalse;
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc index 3710ded..4e51b4a3c 100644 --- a/chrome/browser/apps/app_service/crostini_apps.cc +++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/apps/app_service/app_icon_factory.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/dip_px_util.h" #include "chrome/browser/apps/app_service/launch_util.h" #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" @@ -76,8 +77,7 @@ } if (icon_key->icon_type == apps::mojom::IconType::kCrostini) { - // TODO(crbug.com/826982): don't hard-code SCALE_FACTOR_100P. - auto scale_factor = ui::ScaleFactor::SCALE_FACTOR_100P; + auto scale_factor = apps_util::GetPrimaryDisplayUIScaleFactor(); // Try loading the icon from an on-disk cache. If that fails, fall back // to LoadIconFromVM. @@ -180,6 +180,7 @@ app->install_time = registration.InstallTime(); app->installed_internally = apps::mojom::OptionalBool::kFalse; + app->is_platform_app = apps::mojom::OptionalBool::kFalse; // TODO(crbug.com/826982): if Crostini isn't enabled, don't show the Terminal // item until it becomes enabled.
diff --git a/chrome/browser/apps/app_service/dip_px_util.cc b/chrome/browser/apps/app_service/dip_px_util.cc index e0511902..cbc7adfa 100644 --- a/chrome/browser/apps/app_service/dip_px_util.cc +++ b/chrome/browser/apps/app_service/dip_px_util.cc
@@ -7,9 +7,15 @@ #include <cmath> #include "base/numerics/safe_conversions.h" +#include "ui/base/layout.h" #include "ui/display/display.h" #include "ui/display/screen.h" +// TODO(crbug.com/826982): plumb through enough information to use one of +// Screen::GetDisplayNearest{Window/View/Point}. That way in multi-monitor +// setups where one screen is hidpi and the other one isn't, we don't always do +// the wrong thing. + namespace { float GetPrimaryDisplayScaleFactor() { @@ -22,7 +28,7 @@ } // namespace -namespace apps { +namespace apps_util { int ConvertDipToPx(int dip) { return base::saturated_cast<int>( @@ -34,4 +40,8 @@ std::floor(static_cast<float>(px) / GetPrimaryDisplayScaleFactor())); } -} // namespace apps +ui::ScaleFactor GetPrimaryDisplayUIScaleFactor() { + return ui::GetSupportedScaleFactor(GetPrimaryDisplayScaleFactor()); +} + +} // namespace apps_util
diff --git a/chrome/browser/apps/app_service/dip_px_util.h b/chrome/browser/apps/app_service/dip_px_util.h index 4b88ebf8..81d24a4 100644 --- a/chrome/browser/apps/app_service/dip_px_util.h +++ b/chrome/browser/apps/app_service/dip_px_util.h
@@ -8,11 +8,14 @@ // Utility functions for converting between DIP (device independent pixels) and // PX (physical pixels). -namespace apps { +#include "ui/base/resource/scale_factor.h" + +namespace apps_util { int ConvertDipToPx(int dip); int ConvertPxToDip(int px); +ui::ScaleFactor GetPrimaryDisplayUIScaleFactor(); -} // namespace apps +} // namespace apps_util #endif // CHROME_BROWSER_APPS_APP_SERVICE_DIP_PX_UTIL_H_
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc index 853b149..444b681 100644 --- a/chrome/browser/apps/app_service/extension_apps.cc +++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -43,11 +43,6 @@ // TODO(crbug.com/826982): do we also need to watch prefs, the same as // ExtensionAppModelBuilder? -// TODO(crbug.com/826982): support the is_platform_app bit. We might not need -// to plumb this all the way through the Mojo methods, as AFAICT it's only used -// for populating the context menu, which is done on the app publisher side -// (i.e. in this C++ file) and not at all on the app subscriber side. - namespace { // Only supporting important permissions for now. @@ -464,6 +459,10 @@ ? apps::mojom::OptionalBool::kTrue : apps::mojom::OptionalBool::kFalse; + app->is_platform_app = extension->is_platform_app() + ? apps::mojom::OptionalBool::kTrue + : apps::mojom::OptionalBool::kFalse; + auto show = app_list::ShouldShowInLauncher(extension, profile_) ? apps::mojom::OptionalBool::kTrue : apps::mojom::OptionalBool::kFalse;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 6197331..4d69563 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -274,7 +274,6 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_context.h" -#include "content/public/browser/site_instance.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/tts_controller.h" #include "content/public/browser/tts_platform.h" @@ -519,7 +518,6 @@ #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h" #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" #include "extensions/browser/process_manager.h" -#include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_set.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -4911,14 +4909,6 @@ if (!base::FeatureList::IsEnabled(features::kWebUsb)) return; -#if BUILDFLAG(ENABLE_EXTENSIONS) - // WebUSB is not supported in Apps/Extensions. https://crbug.com/770896 - if (render_frame_host->GetSiteInstance()->GetSiteURL().SchemeIs( - extensions::kExtensionScheme)) { - return; - } -#endif - WebContents* web_contents = WebContents::FromRenderFrameHost(render_frame_host); if (!web_contents) {
diff --git a/chrome/browser/chromeos/accessibility/chromevox_panel.cc b/chrome/browser/chromeos/accessibility/chromevox_panel.cc index b2474a0..11cb323 100644 --- a/chrome/browser/chromeos/accessibility/chromevox_panel.cc +++ b/chrome/browser/chromeos/accessibility/chromevox_panel.cc
@@ -70,12 +70,12 @@ void ChromeVoxPanel::ExitFullscreen() { GetWidget()->Deactivate(); - GetWidget()->widget_delegate()->set_can_activate(false); + GetWidget()->widget_delegate()->SetCanActivate(false); SetAccessibilityPanelFullscreen(false); } void ChromeVoxPanel::Focus() { - GetWidget()->widget_delegate()->set_can_activate(true); + GetWidget()->widget_delegate()->SetCanActivate(true); GetWidget()->Activate(); GetContentsView()->RequestFocus(); }
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc index e40a88a..b042e9b 100644 --- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc +++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -418,7 +418,7 @@ // Prepare widget to hold it. views::Widget* widget = CreateTestWidget(); - widget->widget_delegate()->set_can_activate(false); + widget->widget_delegate()->SetCanActivate(false); widget->Deactivate(); widget->SetContentsView(notification_view.get()); widget->Show();
diff --git a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc index 53f0719..8922e2f 100644 --- a/chrome/browser/chromeos/file_manager/video_player_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/video_player_browsertest.cc
@@ -70,11 +70,6 @@ StartTest(); } -IN_PROC_BROWSER_TEST_F(VideoPlayerBrowserTest, ClickControlButtons) { - set_test_case_name("clickControlButtons"); - StartTest(); -} - // Flaky. Suspect due to a race when loading Chromecast integration. // See https://crbug.com/926035. IN_PROC_BROWSER_TEST_F(VideoPlayerBrowserTest, DISABLED_NativeMediaKey) {
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.cc b/chrome/browser/chromeos/smb_client/smb_file_system.cc index 93fc68ed..023dbbf6 100644 --- a/chrome/browser/chromeos/smb_client/smb_file_system.cc +++ b/chrome/browser/chromeos/smb_client/smb_file_system.cc
@@ -124,10 +124,12 @@ SmbFileSystem::SmbFileSystem( const file_system_provider::ProvidedFileSystemInfo& file_system_info, UnmountCallback unmount_callback, - RequestCredentialsCallback request_creds_callback) + RequestCredentialsCallback request_creds_callback, + RequestUpdatedSharePathCallback request_path_callback) : file_system_info_(file_system_info), unmount_callback_(std::move(unmount_callback)), request_creds_callback_(std::move(request_creds_callback)), + request_path_callback_(std::move(request_path_callback)), task_queue_(kTaskQueueCapacity) {} SmbFileSystem::~SmbFileSystem() {} @@ -575,6 +577,11 @@ request_creds_callback_.Run(GetMountPath(), GetMountId(), std::move(reply)); } +void SmbFileSystem::RequestUpdatedSharePath( + SmbService::StartReadDirIfSuccessfulCallback reply) { + request_path_callback_.Run(GetMountPath(), GetMountId(), std::move(reply)); +} + void SmbFileSystem::HandleRequestReadDirectoryCallback( storage::AsyncFileUtil::ReadDirectoryCallback callback, const base::ElapsedTimer& metrics_timer,
diff --git a/chrome/browser/chromeos/smb_client/smb_file_system.h b/chrome/browser/chromeos/smb_client/smb_file_system.h index 08915ae..dc7e9be 100644 --- a/chrome/browser/chromeos/smb_client/smb_file_system.h +++ b/chrome/browser/chromeos/smb_client/smb_file_system.h
@@ -61,7 +61,8 @@ SmbFileSystem( const file_system_provider::ProvidedFileSystemInfo& file_system_info, UnmountCallback unmount_callback, - RequestCredentialsCallback request_creds_callback); + RequestCredentialsCallback request_creds_callback, + RequestUpdatedSharePathCallback request_path_callback); ~SmbFileSystem() override; // ProvidedFileSystemInterface overrides. @@ -238,6 +239,11 @@ // updated, |reply| is executed. void RequestUpdatedCredentials(base::OnceClosure reply); + // Requests updated share path for the mount. Once the share path have been, + // updated, |reply| is executed. + void RequestUpdatedSharePath( + SmbService::StartReadDirIfSuccessfulCallback reply); + void HandleRequestUnmountCallback( storage::AsyncFileUtil::StatusCallback callback, smbprovider::ErrorType error); @@ -349,6 +355,7 @@ UnmountCallback unmount_callback_; RequestCredentialsCallback request_creds_callback_; + RequestUpdatedSharePathCallback request_path_callback_; std::unique_ptr<TempFileManager> temp_file_manager_; mutable SmbTaskQueue task_queue_;
diff --git a/chrome/browser/chromeos/smb_client/smb_provider.cc b/chrome/browser/chromeos/smb_client/smb_provider.cc index 1a4e26e..4d2cabd 100644 --- a/chrome/browser/chromeos/smb_client/smb_provider.cc +++ b/chrome/browser/chromeos/smb_client/smb_provider.cc
@@ -47,7 +47,8 @@ const ProvidedFileSystemInfo& file_system_info) { DCHECK(profile); return std::make_unique<SmbFileSystem>(file_system_info, unmount_callback_, - request_creds_callback_); + request_creds_callback_, + request_path_callback_); } const Capabilities& SmbProvider::GetCapabilities() const {
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc index cfb7d296..24f3864 100644 --- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc +++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
@@ -387,7 +387,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -406,7 +406,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -421,7 +421,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); handle.set_was_response_cached(true); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -435,7 +435,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); handle.set_was_response_cached(true); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -452,7 +452,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); handle.set_was_response_cached(true); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -468,7 +468,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -485,7 +485,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get()); @@ -502,7 +502,7 @@ scoped_refptr<net::HttpResponseHeaders> headers = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( raw_headers.c_str(), raw_headers.size())); - handle.set_response_headers(headers.get()); + handle.set_response_headers(headers); auto data = drp_chrome_settings_->CreateDataFromNavigationHandle( &handle, headers.get());
diff --git a/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc index ae9133b..7bddaac 100644 --- a/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
@@ -176,8 +176,9 @@ std::vector<std::string>({"image", "stylesheet"}); rule.condition->excluded_resource_types = std::vector<std::string>({"image"}); AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_RESOURCE_TYPE_DUPLICATED, 0u) - .GetErrorDescription(kJSONRulesFilename)); + LoadAndExpectError( + ParseInfo(ParseResult::ERROR_RESOURCE_TYPE_DUPLICATED, *rule.id) + .GetErrorDescription(kJSONRulesFilename)); } TEST_P(RuleIndexingTest, EmptyRedirectRulePriority) { @@ -186,7 +187,7 @@ rule.action->redirect_url = std::string("https://google.com"); AddRule(rule); LoadAndExpectError( - ParseInfo(ParseResult::ERROR_EMPTY_REDIRECT_RULE_PRIORITY, 0u) + ParseInfo(ParseResult::ERROR_EMPTY_REDIRECT_RULE_PRIORITY, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -200,7 +201,7 @@ rule.priority = kMinValidPriority; AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_REDIRECT_URL, 1u) + LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_REDIRECT_URL, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -208,7 +209,7 @@ TestRule rule = CreateGenericRule(); rule.id = kMinValidID - 1; AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_INVALID_RULE_ID, 0u) + LoadAndExpectError(ParseInfo(ParseResult::ERROR_INVALID_RULE_ID, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -219,7 +220,7 @@ rule.priority = kMinValidPriority - 1; AddRule(rule); LoadAndExpectError( - ParseInfo(ParseResult::ERROR_INVALID_REDIRECT_RULE_PRIORITY, 0u) + ParseInfo(ParseResult::ERROR_INVALID_REDIRECT_RULE_PRIORITY, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -231,7 +232,7 @@ "other"}); AddRule(rule); LoadAndExpectError( - ParseInfo(ParseResult::ERROR_NO_APPLICABLE_RESOURCE_TYPES, 0u) + ParseInfo(ParseResult::ERROR_NO_APPLICABLE_RESOURCE_TYPES, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -239,7 +240,7 @@ TestRule rule = CreateGenericRule(); rule.condition->domains = std::vector<std::string>(); AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_DOMAINS_LIST, 0u) + LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_DOMAINS_LIST, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -247,15 +248,16 @@ TestRule rule = CreateGenericRule(); rule.condition->resource_types = std::vector<std::string>(); AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_RESOURCE_TYPES_LIST, 0u) - .GetErrorDescription(kJSONRulesFilename)); + LoadAndExpectError( + ParseInfo(ParseResult::ERROR_EMPTY_RESOURCE_TYPES_LIST, *rule.id) + .GetErrorDescription(kJSONRulesFilename)); } TEST_P(RuleIndexingTest, EmptyURLFilter) { TestRule rule = CreateGenericRule(); rule.condition->url_filter = std::string(); AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_URL_FILTER, 0u) + LoadAndExpectError(ParseInfo(ParseResult::ERROR_EMPTY_URL_FILTER, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -265,8 +267,9 @@ rule.action->redirect_url = std::string("google"); rule.priority = kMinValidPriority; AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_INVALID_REDIRECT_URL, 0u) - .GetErrorDescription(kJSONRulesFilename)); + LoadAndExpectError( + ParseInfo(ParseResult::ERROR_INVALID_REDIRECT_URL, *rule.id) + .GetErrorDescription(kJSONRulesFilename)); } TEST_P(RuleIndexingTest, ListNotPassed) { @@ -279,7 +282,7 @@ TestRule rule = CreateGenericRule(); AddRule(rule); AddRule(rule); - LoadAndExpectError(ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, 1u) + LoadAndExpectError(ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, *rule.id) .GetErrorDescription(kJSONRulesFilename)); } @@ -321,7 +324,7 @@ // |kMaxUnparsedRulesWarnings| rules, which couldn't be parsed. for (size_t i = 0; i < kMaxUnparsedRulesWarnings; i++) { warning.message = ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, std::to_string(i), + kRuleNotParsedWarning, base::StringPrintf("id %zu", i + 1), "'RuleActionType': expected \"block\" or \"redirect\" or \"allow\", " "got \"invalid_action_type\""); EXPECT_EQ(expected_warnings[i], warning); @@ -374,14 +377,14 @@ expected_warnings.emplace_back( ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, "1", + kRuleNotParsedWarning, "id 2", "'RuleActionType': expected \"block\" or \"redirect\" or \"allow\"," " got \"invalid action\""), manifest_keys::kDeclarativeNetRequestKey, manifest_keys::kDeclarativeRuleResourcesKey); expected_warnings.emplace_back( ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, "3", + kRuleNotParsedWarning, "id 4", "'DomainType': expected \"firstParty\" or \"thirdParty\", got " "\"invalid_domain_type\""), manifest_keys::kDeclarativeNetRequestKey, @@ -412,7 +415,7 @@ "action" : {"type" : "block" } }, { - "id" : "4", + "id" : "6", "condition" : {"urlFilter" : "google"}, "action" : {"type" : "block" } } @@ -431,17 +434,17 @@ expected_warnings.emplace_back( ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, "0", + kRuleNotParsedWarning, "id 1", "'condition': expected dictionary, got list"), manifest_keys::kDeclarativeNetRequestKey, manifest_keys::kDeclarativeRuleResourcesKey); expected_warnings.emplace_back( - ErrorUtils::FormatErrorMessage(kRuleNotParsedWarning, "2", + ErrorUtils::FormatErrorMessage(kRuleNotParsedWarning, "id 3", "found unexpected key 'invalidKey'"), manifest_keys::kDeclarativeNetRequestKey, manifest_keys::kDeclarativeRuleResourcesKey); expected_warnings.emplace_back( - ErrorUtils::FormatErrorMessage(kRuleNotParsedWarning, "3", + ErrorUtils::FormatErrorMessage(kRuleNotParsedWarning, "index 4", "'id': expected id, got string"), manifest_keys::kDeclarativeNetRequestKey, manifest_keys::kDeclarativeRuleResourcesKey);
diff --git a/chrome/browser/mac/bluetooth_utility.h b/chrome/browser/mac/bluetooth_utility.h index 8f4f559..df54d35e 100644 --- a/chrome/browser/mac/bluetooth_utility.h +++ b/chrome/browser/mac/bluetooth_utility.h
@@ -17,7 +17,6 @@ // On OSX 10.6, if the Link Manager Protocol version supports Low Energy, // there is no further indication of whether Low Energy is supported. BLUETOOTH_AVAILABLE_LE_UNKNOWN = 4, - BLUETOOTH_NOT_SUPPORTED = 5, BLUETOOTH_AVAILABILITY_COUNT, };
diff --git a/chrome/browser/metrics/bluetooth_available_utility.cc b/chrome/browser/metrics/bluetooth_available_utility.cc deleted file mode 100644 index beecea5..0000000 --- a/chrome/browser/metrics/bluetooth_available_utility.cc +++ /dev/null
@@ -1,77 +0,0 @@ -// Copyright 2019 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/metrics/bluetooth_available_utility.h" - -#include "base/bind.h" -#include "base/metrics/histogram_macros.h" -#include "base/task/post_task.h" -#include "build/build_config.h" -#include "chrome/browser/mac/bluetooth_utility.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "device/bluetooth/bluetooth_adapter.h" -#include "device/bluetooth/bluetooth_adapter_factory.h" - -#if defined(OS_LINUX) -#include "device/bluetooth/dbus/bluez_dbus_manager.h" -#endif // defined(OS_LINUX) - -namespace bluetooth_utility { - -void ReportAvailability(BluetoothAvailability availability) { - UMA_HISTOGRAM_ENUMERATION("Bluetooth.Availability", availability, - BLUETOOTH_AVAILABILITY_COUNT); -} - -void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter) { - if (!adapter->IsPresent()) { - ReportAvailability(BLUETOOTH_NOT_AVAILABLE); - return; - } - - if (!device::BluetoothAdapterFactory::Get().IsLowEnergySupported()) { - ReportAvailability(BLUETOOTH_AVAILABLE_WITHOUT_LE); - return; - } - - ReportAvailability(BLUETOOTH_AVAILABLE_WITH_LE); -} - -void ReportBluetoothAvailability() { -#if defined(OS_MACOSX) - // TODO(kenrb): This is separate from other platforms because we get a - // little bit of extra information from the Mac-specific code. It might not - // be worth having the extra code path, and we should consider whether to - // combine them (https://crbug.com/907279). - bluetooth_utility::BluetoothAvailability availability = - bluetooth_utility::GetBluetoothAvailability(); - UMA_HISTOGRAM_ENUMERATION("Bluetooth.Availability", availability, - bluetooth_utility::BLUETOOTH_AVAILABILITY_COUNT); - return; -#endif // defined(OS_MACOSX) - - // GetAdapter must be called on the UI thread, because it creates a - // WeakPtr, which is checked from that thread on future calls. - if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&ReportBluetoothAvailability)); - return; - } - -#if defined(OS_LINUX) - // This is for tests that have not initialized bluez or dbus thread manager. - // Outside of tests these are initialized earlier during browser startup. - if (!bluez::BluezDBusManager::IsInitialized()) - return; -#endif // defined(OS_LINUX) - - if (!device::BluetoothAdapterFactory::Get().IsBluetoothSupported()) - ReportAvailability(BLUETOOTH_NOT_SUPPORTED); - - device::BluetoothAdapterFactory::Get().GetAdapter( - base::BindOnce(&OnGetAdapter)); -} - -} // namespace bluetooth_utility
diff --git a/chrome/browser/metrics/bluetooth_available_utility.h b/chrome/browser/metrics/bluetooth_available_utility.h deleted file mode 100644 index dfa79b4..0000000 --- a/chrome/browser/metrics/bluetooth_available_utility.h +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2019 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_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_ -#define CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_ - -namespace bluetooth_utility { - -// Reports the bluetooth availability of the system's hardware. -// This currently only works on ChromeOS and Windows. For Bluetooth -// availability on OS X, see chrome/browser/mac/bluetooth_utility.h. -void ReportBluetoothAvailability(); - -} // namespace bluetooth_utility - -#endif // CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc index 72257ad..85b818a 100644 --- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc +++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -21,7 +21,7 @@ #include "chrome/browser/about_flags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_browser_main.h" -#include "chrome/browser/metrics/bluetooth_available_utility.h" +#include "chrome/browser/mac/bluetooth_utility.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/vr/service/xr_runtime_manager.h" #include "components/flags_ui/pref_service_flags_storage.h" @@ -201,7 +201,13 @@ base::TimeTicks::IsHighResolution()); #endif // defined(OS_WIN) - bluetooth_utility::ReportBluetoothAvailability(); +#if defined(OS_MACOSX) + bluetooth_utility::BluetoothAvailability availability = + bluetooth_utility::GetBluetoothAvailability(); + UMA_HISTOGRAM_ENUMERATION("OSX.BluetoothAvailability", + availability, + bluetooth_utility::BLUETOOTH_AVAILABILITY_COUNT); +#endif // defined(OS_MACOSX) // Record whether Chrome is the default browser or not. shell_integration::DefaultWebClientState default_state =
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc index 05ce090..23ecd3b 100644 --- a/chrome/browser/pdf/pdf_extension_test.cc +++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -487,20 +487,6 @@ content::BrowserContext::GetDownloadManager(browser_context); download_awaiter_ = std::make_unique<DownloadAwaiter>(); download_manager->AddObserver(download_awaiter_.get()); - - // TODO(tommycli): PDFIFrameNavigationThrottle currently doesn't wait for - // the plugin list to be loaded, and this causes some unpredictable (but not - // catastrophic) behavior on startup. Remove this after we fix that. - base::RunLoop run_loop; - content::PluginService::GetInstance()->GetPlugins(base::BindOnce( - &PDFPluginDisabledTest::PluginsLoadedCallback, run_loop.QuitClosure())); - run_loop.Run(); - } - - static void PluginsLoadedCallback( - base::OnceClosure callback, - const std::vector<content::WebPluginInfo>& plugins) { - std::move(callback).Run(); } void TearDownOnMainThread() override {
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc index f555d21..572e7226 100644 --- a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc +++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
@@ -9,7 +9,6 @@ #include "base/feature_list.h" #include "base/memory/weak_ptr.h" #include "base/task/post_task.h" -#include "chrome/common/chrome_content_client.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pdf_util.h" #include "content/public/browser/browser_context.h" @@ -23,7 +22,6 @@ #include "content/public/browser/web_contents_user_data.h" #include "net/base/escape.h" #include "net/http/http_response_headers.h" -#include "ppapi/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_PLUGINS) #include "chrome/browser/plugins/chrome_plugin_service_filter.h" @@ -58,6 +56,26 @@ WEB_CONTENTS_USER_DATA_KEY_IMPL(PdfWebContentsLifetimeHelper) +#if BUILDFLAG(ENABLE_PLUGINS) +// Returns true if the PDF plugin for |navigation_handle| is enabled. Optionally +// also sets |is_stale| to true if the plugin list needs a reload. +bool IsPDFPluginEnabled(content::NavigationHandle* navigation_handle, + bool* is_stale) { + content::WebContents* web_contents = navigation_handle->GetWebContents(); + int process_id = web_contents->GetMainFrame()->GetProcess()->GetID(); + int routing_id = web_contents->GetMainFrame()->GetRoutingID(); + content::ResourceContext* resource_context = + web_contents->GetBrowserContext()->GetResourceContext(); + + content::WebPluginInfo plugin_info; + return content::PluginService::GetInstance()->GetPluginInfo( + process_id, routing_id, resource_context, navigation_handle->GetURL(), + web_contents->GetMainFrame()->GetLastCommittedOrigin(), kPDFMimeType, + false /* allow_wildcard */, is_stale, &plugin_info, + nullptr /* actual_mime_type */); +} +#endif + } // namespace PDFIFrameNavigationThrottle::PDFIFrameNavigationThrottle( @@ -77,28 +95,6 @@ if (handle->IsInMainFrame()) return nullptr; -#if BUILDFLAG(ENABLE_PLUGINS) - content::WebPluginInfo pdf_plugin_info; - static const base::FilePath pdf_plugin_path( - ChromeContentClient::kPDFPluginPath); - content::PluginService::GetInstance()->GetPluginInfoByPath(pdf_plugin_path, - &pdf_plugin_info); - - ChromePluginServiceFilter* filter = ChromePluginServiceFilter::GetInstance(); - int process_id = - handle->GetWebContents()->GetMainFrame()->GetProcess()->GetID(); - int routing_id = handle->GetWebContents()->GetMainFrame()->GetRoutingID(); - content::ResourceContext* resource_context = - handle->GetWebContents()->GetBrowserContext()->GetResourceContext(); - if (filter->IsPluginAvailable(process_id, routing_id, resource_context, - handle->GetURL(), url::Origin(), - &pdf_plugin_info)) { - return nullptr; - } -#endif - - // If ENABLE_PLUGINS is false, the PDF plugin is not available, so we should - // always intercept PDF iframe navigations. return std::make_unique<PDFIFrameNavigationThrottle>(handle); } @@ -126,6 +122,41 @@ if (!base::FeatureList::IsEnabled(features::kClickToOpenPDFPlaceholder)) return content::NavigationThrottle::PROCEED; +#if BUILDFLAG(ENABLE_PLUGINS) + bool is_stale = false; + bool pdf_plugin_enabled = IsPDFPluginEnabled(navigation_handle(), &is_stale); + + if (is_stale) { + // On browser start, the plugin list may not be ready yet. + content::PluginService::GetInstance()->GetPlugins( + base::BindOnce(&PDFIFrameNavigationThrottle::OnPluginsLoaded, + weak_factory_.GetWeakPtr())); + return content::NavigationThrottle::DEFER; + } + + // If the plugin was found, proceed on the navigation. Otherwise fall through + // to the placeholder case. + if (pdf_plugin_enabled) + return content::NavigationThrottle::PROCEED; +#endif + + LoadPlaceholderHTML(); + return content::NavigationThrottle::CANCEL_AND_IGNORE; +} + +#if BUILDFLAG(ENABLE_PLUGINS) +void PDFIFrameNavigationThrottle::OnPluginsLoaded( + const std::vector<content::WebPluginInfo>& plugins) { + if (IsPDFPluginEnabled(navigation_handle(), nullptr /* is_stale */)) { + Resume(); + } else { + LoadPlaceholderHTML(); + CancelDeferredNavigation(content::NavigationThrottle::CANCEL_AND_IGNORE); + } +} +#endif + +void PDFIFrameNavigationThrottle::LoadPlaceholderHTML() { // Prepare the params to navigate to the placeholder. std::string html = GetPDFPlaceholderHTML(navigation_handle()->GetURL()); GURL data_url("data:text/html," + net::EscapePath(html)); @@ -148,6 +179,4 @@ FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&PdfWebContentsLifetimeHelper::NavigateIFrameToPlaceholder, helper->GetWeakPtr(), params)); - - return content::NavigationThrottle::CANCEL_AND_IGNORE; }
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle.h b/chrome/browser/plugins/pdf_iframe_navigation_throttle.h index 34e5398..39bf392 100644 --- a/chrome/browser/plugins/pdf_iframe_navigation_throttle.h +++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle.h
@@ -6,12 +6,16 @@ #define CHROME_BROWSER_PLUGINS_PDF_IFRAME_NAVIGATION_THROTTLE_H_ #include <memory> +#include <vector> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "content/public/browser/navigation_throttle.h" +#include "ppapi/buildflags/buildflags.h" namespace content { class NavigationHandle; +struct WebPluginInfo; } // namespace content class PDFIFrameNavigationThrottle : public content::NavigationThrottle { @@ -25,6 +29,17 @@ // content::NavigationThrottle: ThrottleCheckResult WillProcessResponse() override; const char* GetNameForLogging() override; + + private: +#if BUILDFLAG(ENABLE_PLUGINS) + // Callback to check on the PDF plugin status after loading the plugin list. + void OnPluginsLoaded(const std::vector<content::WebPluginInfo>& plugins); +#endif + + // Loads the placeholder HTML into the IFRAME. + void LoadPlaceholderHTML(); + + base::WeakPtrFactory<PDFIFrameNavigationThrottle> weak_factory_{this}; }; #endif // CHROME_BROWSER_PLUGINS_PDF_IFRAME_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc index 0d8f7367..67e5415 100644 --- a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc +++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
@@ -6,8 +6,12 @@ #include "base/bind.h" #include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/plugins/chrome_plugin_service_filter.h" +#include "chrome/common/chrome_content_client.h" #include "chrome/common/chrome_features.h" +#include "chrome/common/pdf_util.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/mock_navigation_handle.h" @@ -21,16 +25,8 @@ namespace { -const char kHeader[] = "HTTP/1.1 200 OK\r\n"; const char kExampleURL[] = "http://example.com"; -#if BUILDFLAG(ENABLE_PLUGINS) -void PluginsLoadedCallback(base::OnceClosure callback, - const std::vector<content::WebPluginInfo>& plugins) { - std::move(callback).Run(); -} -#endif - } // namespace class PDFIFrameNavigationThrottleTest : public ChromeRenderViewHostTestHarness { @@ -45,10 +41,15 @@ #endif } - std::string GetHeaderWithMimeType(const std::string& mime_type) { - return "HTTP/1.1 200 OK\r\n" - "content-type: " + - mime_type + "\r\n"; + scoped_refptr<net::HttpResponseHeaders> GetHeaderWithMimeType( + const std::string& mime_type) { + std::string raw_response_headers = + "HTTP/1.1 200 OK\r\n" + "content-type: " + + mime_type + "\r\n"; + return base::MakeRefCounted<net::HttpResponseHeaders>( + net::HttpUtil::AssembleRawHeaders(raw_response_headers.c_str(), + raw_response_headers.size())); } content::RenderFrameHost* subframe() { return subframe_; } @@ -58,13 +59,21 @@ ChromeRenderViewHostTestHarness::SetUp(); #if BUILDFLAG(ENABLE_PLUGINS) - content::PluginService::GetInstance()->Init(); + content::PluginService* plugin_service = + content::PluginService::GetInstance(); + plugin_service->Init(); + plugin_service->SetFilter(ChromePluginServiceFilter::GetInstance()); - // Load plugins. - base::RunLoop run_loop; - content::PluginService::GetInstance()->GetPlugins( - base::BindOnce(&PluginsLoadedCallback, run_loop.QuitClosure())); - run_loop.Run(); + // Register a fake PDF Viewer plugin into our plugin service. + content::WebPluginInfo info; + info.name = + base::ASCIIToUTF16(ChromeContentClient::kPDFExtensionPluginName); + info.mime_types.push_back(content::WebPluginMimeType( + kPDFMimeType, "pdf", "Fake PDF description")); + plugin_service->RegisterInternalPlugin(info, true); + + // Set the plugin list as dirty, like when the browser first starts. + plugin_service->RefreshPlugins(); #endif content::RenderFrameHostTester::For(main_rfh()) @@ -84,62 +93,56 @@ SetAlwaysOpenPdfExternallyForTests(true); // Never create throttle for main frames. - std::string raw_response_headers = - net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)); - scoped_refptr<net::HttpResponseHeaders> headers = - new net::HttpResponseHeaders(raw_response_headers); content::MockNavigationHandle handle(GURL(kExampleURL), main_rfh()); - handle.set_response_headers(headers.get()); - std::unique_ptr<content::NavigationThrottle> throttle = - PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); - ASSERT_EQ(nullptr, throttle); + handle.set_response_headers(GetHeaderWithMimeType("")); + ASSERT_EQ(nullptr, + PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle)); // Create a throttle for subframes. handle.set_render_frame_host(subframe()); - throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); - ASSERT_NE(nullptr, throttle); + ASSERT_NE(nullptr, + PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle)); } TEST_F(PDFIFrameNavigationThrottleTest, InterceptPDFOnly) { // Setup SetAlwaysOpenPdfExternallyForTests(true); - std::string raw_response_headers = GetHeaderWithMimeType("application/pdf"); - raw_response_headers = net::HttpUtil::AssembleRawHeaders( - raw_response_headers.c_str(), raw_response_headers.size()); - scoped_refptr<net::HttpResponseHeaders> headers = - new net::HttpResponseHeaders(raw_response_headers); + // Load plugins to keep this test synchronous. +#if BUILDFLAG(ENABLE_PLUGINS) + base::RunLoop run_loop; + content::PluginService::GetInstance()->GetPlugins(base::BindRepeating( + [](base::RunLoop* run_loop, + const std::vector<content::WebPluginInfo>& plugins) { + run_loop->Quit(); + }, + base::Unretained(&run_loop))); + run_loop.Run(); +#endif + content::MockNavigationHandle handle(GURL(kExampleURL), subframe()); - handle.set_response_headers(headers.get()); + handle.set_response_headers(GetHeaderWithMimeType("application/pdf")); // Verify that we CANCEL for PDF mime type. std::unique_ptr<content::NavigationThrottle> throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); - ASSERT_NE(nullptr, throttle); ASSERT_EQ(content::NavigationThrottle::CANCEL_AND_IGNORE, throttle->WillProcessResponse().action()); // Verify that we PROCEED for other mime types. // Blank mime type - raw_response_headers = - net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)); - headers = new net::HttpResponseHeaders(raw_response_headers); - handle.set_response_headers(headers.get()); + handle.set_response_headers(GetHeaderWithMimeType("")); ASSERT_EQ(content::NavigationThrottle::PROCEED, throttle->WillProcessResponse().action()); // HTML - raw_response_headers = GetHeaderWithMimeType("text/html"); - headers = new net::HttpResponseHeaders(raw_response_headers); - handle.set_response_headers(headers.get()); + handle.set_response_headers(GetHeaderWithMimeType("text/html")); ASSERT_EQ(content::NavigationThrottle::PROCEED, throttle->WillProcessResponse().action()); // PNG - raw_response_headers = GetHeaderWithMimeType("image/png"); - headers = new net::HttpResponseHeaders(raw_response_headers); - handle.set_response_headers(headers.get()); + handle.set_response_headers(GetHeaderWithMimeType("image/png")); ASSERT_EQ(content::NavigationThrottle::PROCEED, throttle->WillProcessResponse().action()); } @@ -167,26 +170,54 @@ } #if BUILDFLAG(ENABLE_PLUGINS) -TEST_F(PDFIFrameNavigationThrottleTest, CancelOnlyIfPDFViewerIsDisabled) { - // Setup - std::string raw_response_headers = GetHeaderWithMimeType("application/pdf"); - raw_response_headers = net::HttpUtil::AssembleRawHeaders( - raw_response_headers.c_str(), raw_response_headers.size()); - scoped_refptr<net::HttpResponseHeaders> headers = - new net::HttpResponseHeaders(raw_response_headers); +TEST_F(PDFIFrameNavigationThrottleTest, ProceedIfPDFViewerIsEnabled) { content::MockNavigationHandle handle(GURL(kExampleURL), subframe()); - handle.set_response_headers(headers.get()); + handle.set_response_headers(GetHeaderWithMimeType("application/pdf")); - // Test PDF Viewer enabled. SetAlwaysOpenPdfExternallyForTests(false); + + // First time should asynchronously Resume the navigation. std::unique_ptr<content::NavigationThrottle> throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); - ASSERT_EQ(nullptr, throttle); + ASSERT_NE(nullptr, throttle); + ASSERT_EQ(content::NavigationThrottle::DEFER, + throttle->WillProcessResponse().action()); + base::RunLoop run_loop; + throttle->set_resume_callback_for_testing(run_loop.QuitClosure()); + run_loop.Run(); - // Test PDF Viewer disabled. - SetAlwaysOpenPdfExternallyForTests(true); + // Subsequent times should synchronously PROCEED the navigation. throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); + ASSERT_NE(nullptr, throttle); + ASSERT_EQ(content::NavigationThrottle::PROCEED, + throttle->WillProcessResponse().action()); +} +TEST_F(PDFIFrameNavigationThrottleTest, CancelIfPDFViewerIsDisabled) { + content::MockNavigationHandle handle(GURL(kExampleURL), subframe()); + handle.set_response_headers(GetHeaderWithMimeType("application/pdf")); + + SetAlwaysOpenPdfExternallyForTests(true); + + // First time should asynchronously Cancel the navigation. + std::unique_ptr<content::NavigationThrottle> throttle = + PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); + ASSERT_NE(nullptr, throttle); + ASSERT_EQ(content::NavigationThrottle::DEFER, + throttle->WillProcessResponse().action()); + base::RunLoop run_loop; + throttle->set_cancel_deferred_navigation_callback_for_testing( + base::BindRepeating( + [](base::RunLoop* run_loop, + content::NavigationThrottle::ThrottleCheckResult result) { + ASSERT_EQ(content::NavigationThrottle::CANCEL_AND_IGNORE, result); + run_loop->Quit(); + }, + base::Unretained(&run_loop))); + run_loop.Run(); + + // Subsequent times should synchronously CANCEL the navigation. + throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle); ASSERT_NE(nullptr, throttle); ASSERT_EQ(content::NavigationThrottle::CANCEL_AND_IGNORE, throttle->WillProcessResponse().action());
diff --git a/chrome/browser/printing/cloud_print/privet_http_unittest.cc b/chrome/browser/printing/cloud_print/privet_http_unittest.cc index 4d5dcb0..0d99892 100644 --- a/chrome/browser/printing/cloud_print/privet_http_unittest.cc +++ b/chrome/browser/printing/cloud_print/privet_http_unittest.cc
@@ -752,6 +752,8 @@ EXPECT_TRUE(SuccessfulResponse(kSubmitDocURL, kSampleLocalPrintResponse)); EXPECT_EQ("foobar", GetUploadData(kSubmitDocURL)); + EXPECT_EQ(printing::DuplexMode::SIMPLEX, + pwg_converter_->bitmap_settings().duplex_mode); EXPECT_EQ(printing::TRANSFORM_NORMAL, pwg_converter_->bitmap_settings().odd_page_transform); EXPECT_FALSE(pwg_converter_->bitmap_settings().rotate_all_pages); @@ -788,6 +790,8 @@ SuccessfulResponse(kSubmitDocWithJobIDURL, kSampleLocalPrintResponse)); EXPECT_EQ("foobar", GetUploadData(kSubmitDocWithJobIDURL)); + EXPECT_EQ(printing::DuplexMode::SHORT_EDGE, + pwg_converter_->bitmap_settings().duplex_mode); EXPECT_EQ(printing::TRANSFORM_ROTATE_180, pwg_converter_->bitmap_settings().odd_page_transform); EXPECT_FALSE(pwg_converter_->bitmap_settings().rotate_all_pages);
diff --git a/chrome/browser/printing/pwg_raster_converter.cc b/chrome/browser/printing/pwg_raster_converter.cc index db841c9..3e86443 100644 --- a/chrome/browser/printing/pwg_raster_converter.cc +++ b/chrome/browser/printing/pwg_raster_converter.cc
@@ -245,17 +245,20 @@ raster_capability.value().document_sheet_back; PwgRasterSettings result; - result.odd_page_transform = TRANSFORM_NORMAL; switch (duplex_value) { case cloud_devices::printer::NO_DUPLEX: + result.duplex_mode = DuplexMode::SIMPLEX; + result.odd_page_transform = TRANSFORM_NORMAL; break; case cloud_devices::printer::LONG_EDGE: + result.duplex_mode = DuplexMode::LONG_EDGE; if (document_sheet_back == cloud_devices::printer::ROTATED) result.odd_page_transform = TRANSFORM_ROTATE_180; else if (document_sheet_back == cloud_devices::printer::FLIPPED) result.odd_page_transform = TRANSFORM_FLIP_VERTICAL; break; case cloud_devices::printer::SHORT_EDGE: + result.duplex_mode = DuplexMode::SHORT_EDGE; if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) result.odd_page_transform = TRANSFORM_ROTATE_180; else if (document_sheet_back == cloud_devices::printer::FLIPPED)
diff --git a/chrome/browser/printing/pwg_raster_converter_browsertest.cc b/chrome/browser/printing/pwg_raster_converter_browsertest.cc index afb6f4c..c1e192c 100644 --- a/chrome/browser/printing/pwg_raster_converter_browsertest.cc +++ b/chrome/browser/printing/pwg_raster_converter_browsertest.cc
@@ -28,10 +28,14 @@ constexpr char kPdfToPwgRasterColorTestFile[] = "pdf_to_pwg_raster_test_32.pwg"; constexpr char kPdfToPwgRasterMonoTestFile[] = "pdf_to_pwg_raster_mono_test_32.pwg"; +constexpr char kPdfToPwgRasterLongEdgeTestFile[] = + "pdf_to_pwg_raster_long_edge_test_32.pwg"; #else constexpr char kPdfToPwgRasterColorTestFile[] = "pdf_to_pwg_raster_test.pwg"; constexpr char kPdfToPwgRasterMonoTestFile[] = "pdf_to_pwg_raster_mono_test.pwg"; +constexpr char kPdfToPwgRasterLongEdgeTestFile[] = + "pdf_to_pwg_raster_long_edge_test.pwg"; #endif void ResultCallbackImpl(bool* called, @@ -123,6 +127,7 @@ /*use_color=*/true, PdfRenderSettings::Mode::NORMAL); PwgRasterSettings pwg_settings; + pwg_settings.duplex_mode = DuplexMode::SIMPLEX; pwg_settings.odd_page_transform = PwgRasterTransformType::TRANSFORM_NORMAL; pwg_settings.rotate_all_pages = false; pwg_settings.reverse_page_order = false; @@ -150,6 +155,7 @@ /*use_color=*/false, PdfRenderSettings::Mode::NORMAL); PwgRasterSettings pwg_settings; + pwg_settings.duplex_mode = DuplexMode::SIMPLEX; pwg_settings.odd_page_transform = PwgRasterTransformType::TRANSFORM_NORMAL; pwg_settings.rotate_all_pages = false; pwg_settings.reverse_page_order = false; @@ -164,4 +170,32 @@ ComparePwgOutput(expected_pwg_file, std::move(pwg_region)); } +IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestSuccessLongDuplex) { + base::ScopedAllowBlockingForTesting allow_blocking; + + base::FilePath test_data_dir; + scoped_refptr<base::RefCountedString> pdf_data; + GetPdfData("pdf_to_pwg_raster_test.pdf", &test_data_dir, &pdf_data); + + PdfRenderSettings pdf_settings(gfx::Rect(0, 0, 500, 500), gfx::Point(0, 0), + /*dpi=*/gfx::Size(1000, 1000), + /*autorotate=*/false, + /*use_color=*/false, + PdfRenderSettings::Mode::NORMAL); + PwgRasterSettings pwg_settings; + pwg_settings.duplex_mode = DuplexMode::LONG_EDGE; + pwg_settings.odd_page_transform = PwgRasterTransformType::TRANSFORM_NORMAL; + pwg_settings.rotate_all_pages = false; + pwg_settings.reverse_page_order = false; + pwg_settings.use_color = false; + + base::ReadOnlySharedMemoryRegion pwg_region; + Convert(pdf_data.get(), pdf_settings, pwg_settings, + /*expect_success=*/true, &pwg_region); + + base::FilePath expected_pwg_file = + test_data_dir.AppendASCII(kPdfToPwgRasterLongEdgeTestFile); + ComparePwgOutput(expected_pwg_file, std::move(pwg_region)); +} + } // namespace printing
diff --git a/chrome/browser/resources/app_management/actions.js b/chrome/browser/resources/app_management/actions.js index 0427f84d..a195768e 100644 --- a/chrome/browser/resources/app_management/actions.js +++ b/chrome/browser/resources/app_management/actions.js
@@ -43,7 +43,7 @@ * @param {string=} id */ function changePage(pageType, id) { - if (pageType == PageType.DETAIL && !id) { + if (pageType === PageType.DETAIL && !id) { console.warn( 'Tried to load app detail page without providing an app id.'); }
diff --git a/chrome/browser/resources/app_management/main_view.js b/chrome/browser/resources/app_management/main_view.js index c1b75baf..5d1059d1 100644 --- a/chrome/browser/resources/app_management/main_view.js +++ b/chrome/browser/resources/app_management/main_view.js
@@ -141,7 +141,7 @@ .map(function(p) { // Make the titles of app collapsible but make the number in the // "X other app(s)" part non-collapsible. - p.collapsible = !!p.arg && p.arg != '$' + placeholder; + p.collapsible = !!p.arg && p.arg !== '$' + placeholder; return p; }); return pieces; @@ -158,7 +158,7 @@ const textContainer = this.$['notifications-sublabel']; textContainer.textContent = ''; for (const p of pieces) { - if (!p.value || p.value.length == 0) { + if (!p.value || p.value.length === 0) { return; }
diff --git a/chrome/browser/resources/app_management/reducers.js b/chrome/browser/resources/app_management/reducers.js index 2263e85..337bdca2 100644 --- a/chrome/browser/resources/app_management/reducers.js +++ b/chrome/browser/resources/app_management/reducers.js
@@ -76,12 +76,12 @@ * @return {Page} */ CurrentPageState.changePage = function(apps, action) { - if (action.pageType == PageType.DETAIL && apps[action.id]) { + if (action.pageType === PageType.DETAIL && apps[action.id]) { return { pageType: PageType.DETAIL, selectedAppId: action.id, }; - } else if (action.pageType == PageType.NOTIFICATIONS) { + } else if (action.pageType === PageType.NOTIFICATIONS) { return { pageType: PageType.NOTIFICATIONS, selectedAppId: null, @@ -100,8 +100,8 @@ * @return {Page} */ CurrentPageState.removeApp = function(currentPage, action) { - if (currentPage.pageType == PageType.DETAIL && - currentPage.selectedAppId == action.id) { + if (currentPage.pageType === PageType.DETAIL && + currentPage.selectedAppId === action.id) { return { pageType: PageType.MAIN, selectedAppId: null,
diff --git a/chrome/browser/resources/app_management/router.html b/chrome/browser/resources/app_management/router.html index 0e404f2..bbe2548 100644 --- a/chrome/browser/resources/app_management/router.html +++ b/chrome/browser/resources/app_management/router.html
@@ -8,7 +8,7 @@ <dom-module id="app-management-router"> <template> - <iron-location query="{{urlQuery_}}" path="{{path_}}" dwell-time="-1"> + <iron-location id="iron-location" query="{{urlQuery_}}" path="{{path_}}"> </iron-location> <iron-query-params params-string="{{query_}}" params-object="{{queryParams_}}"></iron-query-params>
diff --git a/chrome/browser/resources/app_management/router.js b/chrome/browser/resources/app_management/router.js index abfc9dc4..0b5dae0 100644 --- a/chrome/browser/resources/app_management/router.js +++ b/chrome/browser/resources/app_management/router.js
@@ -77,7 +77,12 @@ /** @private */ publishUrl_: function() { + // Disable pushing urls into the history stack, so that we only push one + // state. + this.$['iron-location'].dwellTime = Infinity; this.publishQueryParams_(); + // Re-enable pushing urls into the history stack. + this.$['iron-location'].dwellTime = 0; this.publishPath_(); },
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/OWNERS b/chrome/browser/resources/chromeos/wallpaper_manager/OWNERS index 13c45fc..35e595f6 100644 --- a/chrome/browser/resources/chromeos/wallpaper_manager/OWNERS +++ b/chrome/browser/resources/chromeos/wallpaper_manager/OWNERS
@@ -1,3 +1,4 @@ +maybelle@chromium.org wzang@chromium.org xdai@chromium.org
diff --git a/chrome/browser/resources/md_extensions/detail_view.js b/chrome/browser/resources/md_extensions/detail_view.js index c2ef73f..1ff977b 100644 --- a/chrome/browser/resources/md_extensions/detail_view.js +++ b/chrome/browser/resources/md_extensions/detail_view.js
@@ -48,6 +48,14 @@ }, /** + * Focuses the extensions options button. This should be used after the + * dialog closes. + */ + focusOptionsButton: function() { + this.$$('#extensions-options').focus(); + }, + + /** * Focuses the back button when page is loaded. * @private */
diff --git a/chrome/browser/resources/md_extensions/manager.js b/chrome/browser/resources/md_extensions/manager.js index 2125fc2..4eba159 100644 --- a/chrome/browser/resources/md_extensions/manager.js +++ b/chrome/browser/resources/md_extensions/manager.js
@@ -458,7 +458,6 @@ const optionsDialog = this.$$('#options-dialog'); if (optionsDialog && optionsDialog.open) { - optionsDialog.close(); this.showOptionsDialog_ = false; } @@ -535,6 +534,7 @@ /** @private */ onOptionsDialogClose_: function() { this.showOptionsDialog_ = false; + this.$$('extensions-detail-view').focusOptionsButton(); }, /** @private */
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface.js b/chrome/browser/resources/print_preview/cloud_print_interface.js index 319b8e7..7fb4b8d 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface.js +++ b/chrome/browser/resources/print_preview/cloud_print_interface.js
@@ -23,6 +23,16 @@ /** * @typedef {{ + * status: number, + * errorCode: number, + * message: string, + * origin: !print_preview.DestinationOrigin, + * }} + */ +cloudprint.CloudPrintInterfaceErrorEventDetail; + +/** + * @typedef {{ * user: string, * origin: !print_preview.DestinationOrigin, * printers: (!Array<!print_preview.Destination>|undefined),
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_js.js b/chrome/browser/resources/print_preview/cloud_print_interface_js.js index 4bed3cf..8ede13af 100644 --- a/chrome/browser/resources/print_preview/cloud_print_interface_js.js +++ b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
@@ -288,10 +288,7 @@ * request. * @param {!cloudprint.CloudPrintRequest} request Request that has been * completed. - * @return {!{ status: number, - * errorCode: number, - * message: string, - * origin: !print_preview.DestinationOrigin }} Information + * @return {!cloudprint.CloudPrintInterfaceErrorEventDetail} Information * about the error. * @private */
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js index 3922b57..b71dfaf 100644 --- a/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -1255,13 +1255,12 @@ /** * Called when the /search call completes, either successfully or not. * In case of success, stores fetched destinations. - * @param {!CustomEvent} event Contains the request result. + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceSearchDoneDetail>} + * event Contains the request result. * @private */ onCloudPrintSearchDone_(event) { - const payload = - /** @type {!cloudprint.CloudPrintInterfaceSearchDoneDetail} */ ( - event.detail); + const payload = event.detail; if (payload.printers && payload.printers.length > 0) { this.insertDestinations_(payload.printers); if (this.selectFirstDestination_) { @@ -1344,8 +1343,9 @@ /** * Called when printer sharing invitation was processed successfully. - * @param {!CustomEvent} event Contains detailed information about the - * invite and newly accepted destination (if known). + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceProcessInviteDetail>} + * event Contains detailed information about the invite and newly + * accepted destination (if known). * @private */ onCloudPrintProcessInviteDone_(event) {
diff --git a/chrome/browser/resources/print_preview/data/invitation_store.js b/chrome/browser/resources/print_preview/data/invitation_store.js index 014ceeae..eeb06ea 100644 --- a/chrome/browser/resources/print_preview/data/invitation_store.js +++ b/chrome/browser/resources/print_preview/data/invitation_store.js
@@ -149,16 +149,14 @@ /** * Called when printer sharing invitations are fetched. - * @param {!CustomEvent} event Contains the list of invitations. + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceInvitesDoneDetail>} + * event Contains the list of invitations. * @private */ onCloudPrintInvitesDone_(event) { - const invitesDoneDetail = - /** @type {!cloudprint.CloudPrintInterfaceInvitesDoneDetail} */ ( - event.detail); - this.loadStatus_[invitesDoneDetail.user] = + this.loadStatus_[event.detail.user] = print_preview.InvitationStoreLoadStatus.DONE; - this.invitations_[invitesDoneDetail.user] = invitesDoneDetail.invitations; + this.invitations_[event.detail.user] = event.detail.invitations; this.dispatchEvent( new CustomEvent(InvitationStore.EventType.INVITATION_SEARCH_DONE)); @@ -166,25 +164,23 @@ /** * Called when printer sharing invitations fetch has failed. - * @param {!CustomEvent} event + * @param {!CustomEvent<string>} event Contains the user for whom invite + * fetch failed. * @private */ onCloudPrintInvitesFailed_(event) { - this.loadStatus_[/** @type {string} */ (event.detail)] = + this.loadStatus_[event.detail] = print_preview.InvitationStoreLoadStatus.FAILED; } /** * Called when printer sharing invitation was processed successfully. - * @param {!CustomEvent} event Contains detailed information about the - * invite. + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceProcessInviteDetail>} + * event Contains detailed information about the invite. * @private */ onCloudPrintProcessInviteDone_(event) { - this.invitationProcessed_( - /** @type {!cloudprint.CloudPrintInterfaceProcessInviteDetail} */ ( - event.detail) - .invitation); + this.invitationProcessed_(event.detail.invitation); this.dispatchEvent( new CustomEvent(InvitationStore.EventType.INVITATION_PROCESSED)); }
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js index 030cdd3..eeda43d 100644 --- a/chrome/browser/resources/print_preview/new/app.js +++ b/chrome/browser/resources/print_preview/new/app.js
@@ -613,7 +613,8 @@ /** * Updates the cloud print status to NOT_SIGNED_IN if there is an * authentication error. - * @param {!CustomEvent<{status: number}>} event Contains the error status + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceErrorEventDetail>} + * event Contains the error status * @private */ checkCloudPrintStatus_: function(event) { @@ -631,7 +632,8 @@ /** * Called when there was an error communicating with Google Cloud print. * Displays an error message in the print header. - * @param {!CustomEvent} event Contains the error message. + * @param {!CustomEvent<!cloudprint.CloudPrintInterfaceErrorEventDetail>} + * event Contains the error message. * @private */ onCloudPrintError_: function(event) {
diff --git a/chrome/browser/resources/print_preview/new/margin_control.js b/chrome/browser/resources/print_preview/new/margin_control.js index 3ba711a..3d900e94 100644 --- a/chrome/browser/resources/print_preview/new/margin_control.js +++ b/chrome/browser/resources/print_preview/new/margin_control.js
@@ -140,7 +140,7 @@ }, /** - * @param {!CustomEvent} e Contains the new value of the input. + * @param {!CustomEvent<string>} e Contains the new value of the input. * @private */ onInputChange_: function(e) {
diff --git a/chrome/browser/resources/print_preview/new/margin_control_container.js b/chrome/browser/resources/print_preview/new/margin_control_container.js index ac4a7123..7cb7a10b 100644 --- a/chrome/browser/resources/print_preview/new/margin_control_container.js +++ b/chrome/browser/resources/print_preview/new/margin_control_container.js
@@ -336,8 +336,7 @@ }, /** - * @param {!CustomEvent} e Contains information about what control fired the - * event. + * @param {!Event} e Contains information about what control fired the event. * @private */ onTextFocus_: function(e) { @@ -454,8 +453,9 @@ }, /** - * @param {!CustomEvent} e Event fired when a control's text field is blurred. - * Contains information about whether the control is in an invalid state. + * @param {!CustomEvent<boolean>} e Event fired when a control's text field + * is blurred. Contains information about whether the control is in an + * invalid state. * @private */ onTextBlur_: function(e) {
diff --git a/chrome/browser/resources/print_preview/new/print_preview_search_box.js b/chrome/browser/resources/print_preview/new/print_preview_search_box.js index 6e986a33..2d2885c 100644 --- a/chrome/browser/resources/print_preview/new/print_preview_search_box.js +++ b/chrome/browser/resources/print_preview/new/print_preview_search_box.js
@@ -39,12 +39,14 @@ }, /** - * @param {!CustomEvent} e Event containing the new search. + * @param {!CustomEvent<string>} e Event containing the new search. * @private */ onSearchChanged_: function(e) { - let safeQuery = e.detail.trim().replace(SANITIZE_REGEX, '\\$&'); - safeQuery = safeQuery.length > 0 ? new RegExp(`(${safeQuery})`, 'i') : null; + const safeQueryString = e.detail.trim().replace(SANITIZE_REGEX, '\\$&'); + const safeQuery = safeQueryString.length > 0 ? + new RegExp(`(${safeQueryString})`, 'i') : + null; if (this.timeout_) { clearTimeout(this.timeout_); }
diff --git a/chrome/browser/resources/welcome/welcome.css b/chrome/browser/resources/welcome/welcome.css index d859a2e..2d08e69 100644 --- a/chrome/browser/resources/welcome/welcome.css +++ b/chrome/browser/resources/welcome/welcome.css
@@ -15,6 +15,10 @@ padding: 8px; } +[dark] body { + color: var(--cr-primary-text-color); +} + .watermark { -webkit-mask-image: url(chrome://resources/images/google_logo.svg); -webkit-mask-repeat: no-repeat; @@ -27,6 +31,10 @@ width: 74px; } +[dark] .watermark { + background: var(--cr-secondary-text-color); +} + @media(max-height: 608px) { .watermark { display: none;
diff --git a/chrome/browser/resources/welcome/welcome.html b/chrome/browser/resources/welcome/welcome.html index a1e54f0..4aa0ce86 100644 --- a/chrome/browser/resources/welcome/welcome.html +++ b/chrome/browser/resources/welcome/welcome.html
@@ -1,5 +1,5 @@ <!doctype html> -<html dir="$i18n{textdirection}" lang="$i18n{language}"> +<html dir="$i18n{textdirection}" lang="$i18n{language}" $i18n{dark}> <head> <meta charset="utf-8"> <title>$i18n{headerText}</title> @@ -7,14 +7,21 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> + <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> - <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> + <link rel="stylesheet" href="chrome://resources/css/md_colors.css"> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> <link rel="stylesheet" href="chrome://welcome/welcome.css"> + <style> + html[dark] { + background-color: var(--md-background-color); + } + </style> + <dom-module id="welcome-app"> <template> <style include="paper-button-style"> @@ -128,13 +135,17 @@ .subheading { animation: fadeInAndSlideUp 600ms 1.9s cubic-bezier(.4, .2, 0, 1) both; - color: #5f6368; + color: rgb(95, 99, 104); font-size: 1em; font-weight: 500; margin-top: .25em; text-align: center; } + :host-context([dark]) .subheading { + color: var(--cr-secondary-text-color); + } + .logo { animation: fadeIn 600ms both, bounce 1s 600ms linear both; height: 96px; @@ -167,7 +178,6 @@ .signin { animation: fadeInAndSlideUp 600ms 2s cubic-bezier(.4, .2, 0, 1) both; margin-top: 3em; - text-align: left; } .signin-description { @@ -227,5 +237,6 @@ <body> <welcome-app></welcome-app> <div class="watermark"></div> + <link rel="import" href="chrome://resources/html/dark_mode.html"> </body> </html>
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc index a6893ef5..14efc2ca 100644 --- a/chrome/browser/themes/browser_theme_pack.cc +++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -634,6 +634,10 @@ pack->CropImages(&pack->images_); + // Set toolbar related elements' colors (e.g. status bubble, info bar, + // download shelf, detached bookmark bar) to toolbar color. + pack->SetToolbarRelatedColors(); + // Create toolbar image, and generate toolbar color from image where relevant. // This must be done after reading colors from JSON (so they can be used for // compositing the image). @@ -1272,6 +1276,19 @@ } } +void BrowserThemePack::SetToolbarRelatedColors() { + // Propagate the user-specified Toolbar Color to similar elements (for + // backwards-compatibility with themes written before this toolbar processing + // was introduced). + SkColor toolbar_color; + if (GetColor(TP::COLOR_TOOLBAR, &toolbar_color)) { + SetColor(TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND, toolbar_color); + SetColor(TP::COLOR_INFOBAR, toolbar_color); + SetColor(TP::COLOR_DOWNLOAD_SHELF, toolbar_color); + SetColor(TP::COLOR_STATUS_BUBBLE, toolbar_color); + } +} + void BrowserThemePack::CreateToolbarImageAndColors(ImageCache* images) { ImageCache temp_output; @@ -1285,15 +1302,7 @@ constexpr int kToolbarColorId = TP::COLOR_TOOLBAR; SkColor toolbar_color; - // Propagate the user-specified Toolbar Color to similar elements (for - // backwards-compatibility with themes written before this toolbar processing - // was introduced). - if (GetColor(kToolbarColorId, &toolbar_color)) { - SetColor(TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND, toolbar_color); - SetColor(TP::COLOR_INFOBAR, toolbar_color); - SetColor(TP::COLOR_DOWNLOAD_SHELF, toolbar_color); - SetColor(TP::COLOR_STATUS_BUBBLE, toolbar_color); - } else { + if (!GetColor(kToolbarColorId, &toolbar_color)) { toolbar_color = TP::GetDefaultColor(kToolbarColorId, false); }
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h index d6d97fb9..3131d6c 100644 --- a/chrome/browser/themes/browser_theme_pack.h +++ b/chrome/browser/themes/browser_theme_pack.h
@@ -167,6 +167,10 @@ // can be of any size. Source and destination is |images|. void CropImages(ImageCache* images) const; + // Set toolbar related elements' colors (e.g. status bubble, info bar, + // download shelf, detached bookmark bar) to toolbar color. + void SetToolbarRelatedColors(); + // Creates a composited toolbar image. Source and destination is |images|. // Also sets toolbar color corresponding to this image. void CreateToolbarImageAndColors(ImageCache* images);
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc index 7ef4a00..d0ac3620 100644 --- a/chrome/browser/themes/browser_theme_pack_unittest.cc +++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -1014,6 +1014,30 @@ EXPECT_EQ(infobar_color, status_bubble_color); } +// Ensure that a specified 'toolbar' color is propagated to other 'bar' and +// 'shelf' colors (before a new color is computed from the toolbar image). +TEST_F(BrowserThemePackTest, TestToolbarColorPropagationNoImage) { + scoped_refptr<BrowserThemePack> pack; + BuildTestExtensionTheme("theme_test_toolbar_color_no_image", &pack); + + SkColor infobar_color; + SkColor bookmark_bar_color; + SkColor download_shelf_color; + SkColor status_bubble_color; + + EXPECT_TRUE(pack->GetColor(TP::COLOR_INFOBAR, &infobar_color)); + EXPECT_TRUE(pack->GetColor(TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND, + &bookmark_bar_color)); + EXPECT_TRUE(pack->GetColor(TP::COLOR_DOWNLOAD_SHELF, &download_shelf_color)); + EXPECT_TRUE(pack->GetColor(TP::COLOR_STATUS_BUBBLE, &status_bubble_color)); + + constexpr SkColor kExpectedColor = SkColorSetRGB(0, 255, 0); + EXPECT_EQ(infobar_color, kExpectedColor); + EXPECT_EQ(infobar_color, bookmark_bar_color); + EXPECT_EQ(infobar_color, download_shelf_color); + EXPECT_EQ(infobar_color, status_bubble_color); +} + // Ensure that, given an explicit toolbar color and a toolbar image, the output // color in COLOR_TOOLBAR reflects the color of the image (not the explicit // color).
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc index 6390570..a9d9e32 100644 --- a/chrome/browser/ui/app_list/app_service_app_item.cc +++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -52,7 +52,8 @@ const app_list::AppListSyncableService::SyncItem* sync_item, const apps::AppUpdate& app_update) : ChromeAppListItem(profile, app_update.AppId()), - app_type_(app_update.AppType()) { + app_type_(app_update.AppType()), + is_platform_app_(false) { OnAppUpdate(app_update, true); if (sync_item && sync_item->item_ordinal.IsValid()) { UpdateFromSync(sync_item); @@ -86,6 +87,11 @@ weak_ptr_factory_.GetWeakPtr())); } } + + if (in_constructor || app_update.IsPlatformAppChanged()) { + is_platform_app_ = + app_update.IsPlatformApp() == apps::mojom::OptionalBool::kTrue; + } } void AppServiceAppItem::Activate(int event_flags) { @@ -97,12 +103,8 @@ } void AppServiceAppItem::GetContextMenuModel(GetMenuModelCallback callback) { - // TODO(crbug.com/826982): don't hard-code false. The App Service should - // probably provide this. - const bool is_platform_app = false; - context_menu_ = MakeAppContextMenu(app_type_, this, profile(), id(), - GetController(), is_platform_app); + GetController(), is_platform_app_); context_menu_->GetMenuModel(std::move(callback)); }
diff --git a/chrome/browser/ui/app_list/app_service_app_item.h b/chrome/browser/ui/app_list/app_service_app_item.h index 15e8bfa..47be350 100644 --- a/chrome/browser/ui/app_list/app_service_app_item.h +++ b/chrome/browser/ui/app_list/app_service_app_item.h
@@ -51,6 +51,7 @@ void OnLoadIcon(apps::mojom::IconValuePtr icon_value); apps::mojom::AppType app_type_; + bool is_platform_app_; std::unique_ptr<app_list::AppContextMenu> context_menu_;
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc index 967072d8..4239f0f 100644 --- a/chrome/browser/ui/app_list/search/app_service_app_result.cc +++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -21,6 +21,7 @@ bool is_recommendation) : AppResult(profile, app_id, controller, is_recommendation), app_type_(apps::mojom::AppType::kUnknown), + is_platform_app_(false), show_in_launcher_(false), weak_ptr_factory_(this) { apps::AppServiceProxy* proxy = apps::AppServiceProxy::Get(profile); @@ -28,6 +29,8 @@ if (proxy) { proxy->Cache().ForOneApp(app_id, [this](const apps::AppUpdate& update) { app_type_ = update.AppType(); + is_platform_app_ = + update.IsPlatformApp() == apps::mojom::OptionalBool::kTrue; show_in_launcher_ = update.ShowInLauncher() == apps::mojom::OptionalBool::kTrue; }); @@ -80,10 +83,6 @@ } void AppServiceAppResult::GetContextMenuModel(GetMenuModelCallback callback) { - // TODO(crbug.com/826982): don't hard-code false. The App Service should - // probably provide this. - const bool is_platform_app = false; - // TODO(crbug.com/826982): drop the (app_type_ == etc), and check // show_in_launcher_ for all app types? if ((app_type_ == apps::mojom::AppType::kBuiltIn) && !show_in_launcher_) { @@ -92,7 +91,7 @@ } context_menu_ = AppServiceAppItem::MakeAppContextMenu( - app_type_, this, profile(), app_id(), controller(), is_platform_app); + app_type_, this, profile(), app_id(), controller(), is_platform_app_); context_menu_->GetMenuModel(std::move(callback)); }
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.h b/chrome/browser/ui/app_list/search/app_service_app_result.h index 2a7562d..2949e3c 100644 --- a/chrome/browser/ui/app_list/search/app_service_app_result.h +++ b/chrome/browser/ui/app_list/search/app_service_app_result.h
@@ -35,6 +35,7 @@ void OnLoadIcon(bool chip, apps::mojom::IconValuePtr icon_value); apps::mojom::AppType app_type_; + bool is_platform_app_; bool show_in_launcher_; std::unique_ptr<AppContextMenu> context_menu_;
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc index f9e831a..f2cd178 100644 --- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc +++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc
@@ -25,16 +25,14 @@ ArcAppReinstallAppResult::ArcAppReinstallAppResult( const arc::mojom::AppReinstallCandidatePtr& mojom_data, - const gfx::ImageSkia& skia_icon, - bool is_recommendation) { + + const gfx::ImageSkia& skia_icon) { ash::mojom::SearchResultMetadataPtr metadata = {base::in_place}; set_id(kPlayStoreAppUrlPrefix + mojom_data->package_name); - SetResultType(ash::SearchResultType::kPlayStoreApp); + SetResultType(ash::SearchResultType::kPlayStoreReinstallApp); SetTitle(base::UTF8ToUTF16(mojom_data->title)); SetDetails(base::UTF8ToUTF16(metadata->id)); - SetDisplayType(is_recommendation - ? ash::SearchResultDisplayType::kRecommendation - : ash::SearchResultDisplayType::kTile); + SetDisplayType(ash::SearchResultDisplayType::kRecommendation); set_relevance(kAppReinstallRelevance); SetIcon(skia_icon);
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h index 9309662..40c58867 100644 --- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h +++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h
@@ -19,8 +19,7 @@ public: ArcAppReinstallAppResult( const arc::mojom::AppReinstallCandidatePtr& mojom_data, - const gfx::ImageSkia& skia_icon, - bool is_recommendation); + const gfx::ImageSkia& skia_icon); ~ArcAppReinstallAppResult() override; // ArcAppResult:
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc index bd5ea9e..1184967 100644 --- a/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc +++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc
@@ -182,7 +182,7 @@ } else if (icon_it != icon_urls_.end()) { // Icon is loaded, add it to the results. new_results.emplace_back(std::make_unique<ArcAppReinstallAppResult>( - loaded_value_[i], icon_it->second, /*is_recommendation=*/true)); + loaded_value_[i], icon_it->second)); } }
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc index c737177..d4bf3b2 100644 --- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc +++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
@@ -13,6 +13,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/user_action_tester.h" #include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" @@ -94,9 +95,11 @@ "link: " "{0}.\",\"template_parameter\":[{\"display_text\":\"Link\",\"url\":\"https:" "//www.example.com/\"}]}]},\"context_token\":\"dummy_context_token\"}"; -const char kResponseGetUploadDetailsFailure[] = +const char kResponsePaymentsFailure[] = "{\"error\":{\"code\":\"FAILED_PRECONDITION\",\"user_error_message\":\"An " "unexpected error has occurred. Please try again later.\"}}"; +const char kURLUploadCardRequest[] = + "https://payments.google.com/payments/apis/chromepaymentsservice/savecard"; const double kFakeGeolocationLatitude = 1.23; const double kFakeGeolocationLongitude = 4.56; @@ -509,7 +512,7 @@ void SetUploadDetailsRpcPaymentsDeclines() { test_url_loader_factory()->AddResponse(kURLGetUploadDetailsRequest, - kResponseGetUploadDetailsFailure); + kResponsePaymentsFailure); } void SetUploadDetailsRpcServerError() { @@ -518,6 +521,11 @@ net::HTTP_INTERNAL_SERVER_ERROR); } + void SetUploadCardRpcPaymentsFails() { + test_url_loader_factory()->AddResponse(kURLUploadCardRequest, + kResponsePaymentsFailure); + } + void ClickOnView(views::View* view) { DCHECK(view); ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), @@ -590,19 +598,19 @@ return specified_view; } - void ClickOnCancelButton() { + void ClickOnCancelButton(bool strike_expected = false) { SaveCardBubbleViews* save_card_bubble_views = GetSaveCardBubbleViews(); DCHECK(save_card_bubble_views); - if (base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystem) || - base::FeatureList::IsEnabled( - features::kAutofillSaveCreditCardUsesStrikeSystemV2)) { + if (strike_expected && + (base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystem) || + base::FeatureList::IsEnabled( + features::kAutofillSaveCreditCardUsesStrikeSystemV2))) { ResetEventWaiterForSequence( {DialogEvent::STRIKE_CHANGE_COMPLETE, DialogEvent::BUBBLE_CLOSED}); } else { ResetEventWaiterForSequence({DialogEvent::BUBBLE_CLOSED}); } - ClickOnDialogViewWithIdAndWait(DialogViewId::CANCEL_BUTTON); DCHECK(!GetSaveCardBubbleViews()); } @@ -720,8 +728,12 @@ IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, Local_ClickingNoThanksClosesBubble) { // Enable the updated UI. - scoped_feature_list_.InitAndEnableFeature( - features::kAutofillSaveCardImprovedUserConsent); + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent}, + // Disabled + {features::kAutofillSaveCreditCardUsesStrikeSystem, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}); // Submitting the form and having Payments decline offering to save should // show the local save bubble. @@ -1254,7 +1266,8 @@ {features::kAutofillSaveCardImprovedUserConsent, features::kAutofillUpstream}, // Disabled - {}); + {features::kAutofillSaveCreditCardUsesStrikeSystem, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}); // Start sync. harness_->SetupSync(); @@ -2275,4 +2288,424 @@ year_input()->GetTextForRow(year_input()->selected_index())); } +// TODO(crbug.com/884817): Investigate combining local vs. upload tests using a +// boolean to branch local vs. upload logic. + +// Tests StrikeDatabase interaction with the local save bubble. Ensures that no +// strikes are added if the feature flag is disabled. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Local_StrikeNotAddedIfExperimentFlagOff) { + // Disable the the SaveCreditCardUsesStrikeSystem experiments. + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent}, + // Disabled + {features::kAutofillSaveCreditCardUsesStrikeSystem, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}); + + // Submitting the form and having Payments decline offering to save should + // show the local save bubble. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence({DialogEvent::OFFERED_LOCAL_SAVE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_LOCAL)->visible()); + + base::HistogramTester histogram_tester; + ClickOnCancelButton(); + + // Ensure that no strike was added because the feature is disabled. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", 0); +} + +// Tests StrikeDatabase interaction with the upload save bubble. Ensures that no +// strikes are added if the feature flag is disabled. +IN_PROC_BROWSER_TEST_F( + SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Upload_StrikeNotAddedIfExperimentFlagOff) { + // Disable the the SaveCreditCardUsesStrikeSystem experiments. + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillUpstream, + features::kAutofillSaveCardImprovedUserConsent}, + // Disabled + {features::kAutofillSaveCreditCardUsesStrikeSystem, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}); + + // Start sync. + harness_->SetupSync(); + + // Set up the Payments RPC. + SetUploadDetailsRpcPaymentsAccepts(); + + // Submitting the form should show the upload save bubble and legal footer. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence( + {DialogEvent::REQUESTED_UPLOAD_SAVE, + DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible()); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible()); + + base::HistogramTester histogram_tester; + + ClickOnCancelButton(); + + // Ensure that no strike was added because the feature is disabled. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", 0); +} + +// Tests StrikeDatabase interaction with the local save bubble. Ensures that a +// strike is added if the bubble is ignored. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Local_AddStrikeIfBubbleIgnored) { + scoped_feature_list_.InitAndEnableFeature( + features::kAutofillSaveCreditCardUsesStrikeSystemV2); + + TestAutofillClock test_clock; + test_clock.SetNow(base::Time::Now()); + + // Set up the Payments RPC. + SetUploadDetailsRpcPaymentsDeclines(); + + // Submitting the form and having Payments decline offering to save should + // show the local save bubble. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence({DialogEvent::OFFERED_LOCAL_SAVE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_LOCAL)->visible()); + + // Clicking the [X] close button should dismiss the bubble. + ClickOnCloseButton(); + + // Add an event observer to the controller to detect strike changes. + AddEventObserverToController(); + + base::HistogramTester histogram_tester; + + // Wait long enough to avoid bubble stickiness, then navigate away from the + // page. + test_clock.Advance(kCardBubbleSurviveNavigationTime); + ResetEventWaiterForSequence({DialogEvent::STRIKE_CHANGE_COMPLETE}); + NavigateTo(kCreditCardUploadForm); + WaitForObservedEvent(); + + // Ensure that a strike was added due to the bubble being ignored. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/1, /*count=*/1); +} + +// Tests StrikeDatabase interaction with the upload save bubble. Ensures that a +// strike is added if the bubble is ignored. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Upload_AddStrikeIfBubbleIgnored) { + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillUpstream, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}, + // Disabled + {}); + + TestAutofillClock test_clock; + test_clock.SetNow(base::Time::Now()); + + // Start sync. + harness_->SetupSync(); + + // Set up the Payments RPC. + SetUploadDetailsRpcPaymentsAccepts(); + + // Submitting the form should show the upload save bubble and legal footer. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence( + {DialogEvent::REQUESTED_UPLOAD_SAVE, + DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible()); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible()); + + // Clicking the [X] close button should dismiss the bubble. + ClickOnCloseButton(); + + // Add an event observer to the controller to detect strike changes. + AddEventObserverToController(); + + base::HistogramTester histogram_tester; + + // Wait long enough to avoid bubble stickiness, then navigate away from the + // page. + test_clock.Advance(kCardBubbleSurviveNavigationTime); + ResetEventWaiterForSequence({DialogEvent::STRIKE_CHANGE_COMPLETE}); + NavigateTo(kCreditCardUploadForm); + WaitForObservedEvent(); + + // Ensure that a strike was added due to the bubble being ignored. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/1, /*count=*/1); +} + +// Tests the local save bubble. Ensures that clicking the [No thanks] button +// successfully causes a strike to be added. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Local_AddStrikeIfBubbleDeclined) { + // Enable the updated UI. + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}, + // Disabled + {}); + + // Submitting the form and having Payments decline offering to save should + // show the local save bubble. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence({DialogEvent::OFFERED_LOCAL_SAVE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_LOCAL)->visible()); + + // Clicking [No thanks] should cancel and close it. + base::HistogramTester histogram_tester; + ClickOnCancelButton(/*strike_expected=*/true); + + // Ensure that a strike was added. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/(1), /*count=*/1); +} + +// Tests the upload save bubble. Ensures that clicking the [No thanks] button +// successfully causes a strike to be added. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Upload_AddStrikeIfBubbleDeclined) { + // Enable the updated UI. + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent, + features::kAutofillUpstream, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}, + // Disabled + {}); + + // Start sync. + harness_->SetupSync(); + + // Set up the Payments RPC. + SetUploadDetailsRpcPaymentsAccepts(); + + // Submitting the form should show the upload save bubble and legal footer. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence( + {DialogEvent::REQUESTED_UPLOAD_SAVE, + DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible()); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible()); + + // Clicking [No thanks] should cancel and close it. + base::HistogramTester histogram_tester; + ClickOnCancelButton(/*strike_expected=*/true); + + // Ensure that a strike was added. + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/(1), /*count=*/1); +} + +// Tests overall StrikeDatabase interaction with the local save bubble. Runs an +// example of declining the prompt three times and ensuring that the +// offer-to-save bubble does not appear on the fourth try. Then, ensures that no +// strikes are added if the card already has max strikes. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Local_FullFlowTest) { + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}, + // Disabled + {}); + + bool controller_observer_set = false; + + // Show and ignore the bubble kMaxStrikesToPreventPoppingUpOfferToSavePrompt + // times in order to accrue maximum strikes. + for (int i = 0; i < kMaxStrikesToPreventPoppingUpOfferToSavePrompt; i++) { + // Submitting the form and having Payments decline offering to save should + // show the local save bubble. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence({DialogEvent::OFFERED_LOCAL_SAVE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_LOCAL)->visible()); + + if (!controller_observer_set) { + // Add an event observer to the controller. + AddEventObserverToController(); + ReduceAnimationTime(); + controller_observer_set = true; + } + + base::HistogramTester histogram_tester; + ResetEventWaiterForSequence({DialogEvent::STRIKE_CHANGE_COMPLETE}); + ClickOnCancelButton(/*strike_expected=*/true); + WaitForObservedEvent(); + + // Ensure that a strike was added due to the bubble being declined. + // The sample logged is the Nth strike added, or (i+1). + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/(i + 1), /*count=*/1); + } + + base::HistogramTester histogram_tester; + + // Submit the form a fourth time. Since the card now has maximum strikes (3), + // the icon should be shown but the bubble should not. + ResetEventWaiterForSequence({DialogEvent::OFFERED_LOCAL_SAVE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE(GetSaveCardIconView()->visible()); + EXPECT_FALSE(GetSaveCardBubbleViews()); + + // Click the icon to show the bubble. + ResetEventWaiterForSequence({DialogEvent::BUBBLE_SHOWN}); + ClickOnView(GetSaveCardIconView()); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_LOCAL)->visible()); + + ClickOnCancelButton(); + + // Ensure that no strike was added because the card already had max strikes. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", 0); + + // Verify that the correct histogram entry was logged. + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::LOCAL, 1); + + // UMA should have recorded bubble rejection. + histogram_tester.ExpectUniqueSample( + "Autofill.SaveCreditCardPrompt.Local.FirstShow", + AutofillMetrics::SAVE_CARD_ICON_SHOWN_WITHOUT_PROMPT, 1); +} + +// Tests overall StrikeDatabase interaction with the upload save bubble. Runs an +// example of declining the prompt three times and ensuring that the +// offer-to-save bubble does not appear on the fourth try. Then, ensures that no +// strikes are added if the card already has max strikes. +IN_PROC_BROWSER_TEST_F(SaveCardBubbleViewsFullFormBrowserTest, + StrikeDatabase_Upload_FullFlowTest) { + scoped_feature_list_.InitWithFeatures( + // Enabled + {features::kAutofillSaveCardImprovedUserConsent, + features::kAutofillUpstream, + features::kAutofillSaveCreditCardUsesStrikeSystemV2}, + // Disabled + {}); + + bool controller_observer_set = false; + + // Start sync. + harness_->SetupSync(); + + // Set up the Payments RPC. + SetUploadDetailsRpcPaymentsAccepts(); + + // Show and ignore the bubble kMaxStrikesToPreventPoppingUpOfferToSavePrompt + // times in order to accrue maximum strikes. + for (int i = 0; i < kMaxStrikesToPreventPoppingUpOfferToSavePrompt; i++) { + // Submitting the form should show the upload save bubble and legal footer. + // (Must wait for response from Payments before accessing the controller.) + ResetEventWaiterForSequence( + {DialogEvent::REQUESTED_UPLOAD_SAVE, + DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD) + ->visible()); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible()); + + if (!controller_observer_set) { + // Add an event observer to the controller. + AddEventObserverToController(); + ReduceAnimationTime(); + controller_observer_set = true; + } + + base::HistogramTester histogram_tester; + + ClickOnCancelButton(/*strike_expected=*/true); + WaitForObservedEvent(); + + // Ensure that a strike was added due to the bubble being declined. + // The sample logged is the Nth strike added, or (i+1). + histogram_tester.ExpectUniqueSample( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", + /*sample=*/(i + 1), /*count=*/1); + } + + base::HistogramTester histogram_tester; + + // Submit the form a fourth time. Since the card now has maximum strikes (3), + // the icon should be shown but the bubble should not. + ResetEventWaiterForSequence( + {DialogEvent::REQUESTED_UPLOAD_SAVE, + DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}); + NavigateTo(kCreditCardUploadForm); + FillAndSubmitForm(); + WaitForObservedEvent(); + EXPECT_TRUE(GetSaveCardIconView()->visible()); + EXPECT_FALSE(GetSaveCardBubbleViews()); + + // Click the icon to show the bubble. + ResetEventWaiterForSequence({DialogEvent::BUBBLE_SHOWN}); + ClickOnView(GetSaveCardIconView()); + WaitForObservedEvent(); + EXPECT_TRUE( + FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible()); + EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible()); + + ClickOnCancelButton(); + + // Ensure that no strike was added because the card already had max strikes. + histogram_tester.ExpectTotalCount( + "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave", 0); + + // Verify that the correct histogram entry was logged. + histogram_tester.ExpectBucketCount( + "Autofill.StrikeDatabase.CreditCardSaveNotOfferedDueToMaxStrikes", + AutofillMetrics::SaveTypeMetric::SERVER, 1); + + // UMA should have recorded bubble rejection. + histogram_tester.ExpectUniqueSample( + "Autofill.SaveCreditCardPrompt.Upload.FirstShow", + AutofillMetrics::SAVE_CARD_ICON_SHOWN_WITHOUT_PROMPT, 1); +} + } // namespace autofill
diff --git a/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc b/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc index e300b26..f86ec57 100644 --- a/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc +++ b/chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.cc
@@ -92,7 +92,7 @@ AddChildView(label); if (activation_action == ActivationAction::DO_NOT_ACTIVATE) { - set_can_activate(activation_action == ActivationAction::ACTIVATE); + SetCanActivate(activation_action == ActivationAction::ACTIVATE); set_shadow(views::BubbleBorder::BIG_SHADOW); }
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc index cc34073..80819733 100644 --- a/chrome/browser/ui/views/frame/browser_frame.cc +++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -164,6 +164,20 @@ browser_frame_view_->OnBrowserViewInitViewsComplete(); } +bool BrowserFrame::ShouldUseTheme() const { + // Main browser windows are always themed. + if (browser_view_->IsBrowserTypeNormal()) + return true; + + // The system GTK theme should always be respected if the user has opted to + // use it. + if (IsUsingGtkTheme(browser_view_->browser()->profile())) + return true; + + // Other window types (popups, hosted apps) on non-GTK use the default theme. + return false; +} + /////////////////////////////////////////////////////////////////////////////// // BrowserFrame, views::Widget overrides: @@ -264,20 +278,6 @@ menu_runner_.reset(); } -bool BrowserFrame::ShouldUseTheme() const { - // Main browser windows are always themed. - if (browser_view_->IsBrowserTypeNormal()) - return true; - - // The system GTK theme should always be respected if the user has opted to - // use it. - if (IsUsingGtkTheme(browser_view_->browser()->profile())) - return true; - - // Other window types (popups, hosted apps) on non-GTK use the default theme. - return false; -} - void BrowserFrame::OnTouchUiChanged() { client_view()->InvalidateLayout(); non_client_view()->InvalidateLayout();
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h index 9a2644b..0f36993 100644 --- a/chrome/browser/ui/views/frame/browser_frame.h +++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -100,6 +100,9 @@ // Called when BrowserView creates all it's child views. void OnBrowserViewInitViewsComplete(); + // Returns whether this window should be themed with the user's theme or not. + bool ShouldUseTheme() const; + // views::Widget: views::internal::RootView* CreateRootView() override; views::NonClientFrameView* CreateNonClientFrameView() override; @@ -131,9 +134,6 @@ // Callback for MenuRunner. void OnMenuClosed(); - // Returns whether this window should be themed with the user's theme or not. - bool ShouldUseTheme() const; - NativeBrowserFrame* native_browser_frame_; // A weak reference to the root view associated with the window. We save a
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc index 943a1f0..e6ffbfe 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -149,16 +149,7 @@ : ThemeProperties::COLOR_FRAME_INACTIVE; } - // For hosted app windows, if "painting as themed" (which is only true when on - // Linux and using the system theme), prefer the system theme color over the - // hosted app theme color. The title bar will be painted in the system theme - // color (regardless of what we do here), so by returning the system title bar - // background color here, we ensure that: - // a) The side and bottom borders are painted in the same color as the title - // bar background, and - // b) The title text is painted in a color that contrasts with the title bar - // background. - if (ShouldPaintAsThemed()) + if (frame_->ShouldUseTheme()) return GetThemeProviderForProfile()->GetColor(color_id); extensions::HostedAppBrowserController* hosted_app_controller = @@ -255,10 +246,6 @@ browser_view_->tabstrip()->SingleTabMode(); } -bool BrowserNonClientFrameView::ShouldPaintAsThemed() const { - return browser_view_->IsBrowserTypeNormal(); -} - SkColor BrowserNonClientFrameView::GetCaptionColor( ActiveState active_state) const { return color_utils::GetColorWithMaxContrast(GetFrameColor(active_state)); @@ -276,8 +263,8 @@ const int frame_image_id = ShouldPaintAsActive(active_state) ? IDR_THEME_FRAME : IDR_THEME_FRAME_INACTIVE; - return ShouldPaintAsThemed() && (tp->HasCustomImage(frame_image_id) || - tp->HasCustomImage(IDR_THEME_FRAME)) + return frame_->ShouldUseTheme() && (tp->HasCustomImage(frame_image_id) || + tp->HasCustomImage(IDR_THEME_FRAME)) ? *tp->GetImageSkiaNamed(frame_image_id) : gfx::ImageSkia(); } @@ -425,7 +412,7 @@ // During shutdown, there may no longer be a widget, and thus no theme // provider. const auto* theme_provider = GetThemeProvider(); - return ShouldPaintAsThemed() && theme_provider + return frame_->ShouldUseTheme() && theme_provider ? theme_provider->GetColor(color_id) : ThemeProperties::GetDefaultColor(color_id, browser_view_->IsIncognito());
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h index 5a21806d..c0d4fe7 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -140,13 +140,6 @@ } protected: - // Whether the frame should be painted with theming. - // By default, tabbed browser windows are themed but popup and app windows are - // not. - // TODO(https://crbug.com/927381): Dedupe this with - // BrowserFrame::ShouldIgnoreTheme(). - virtual bool ShouldPaintAsThemed() const; - // Returns the color to use for text, caption buttons, and other title bar // elements. virtual SkColor GetCaptionColor(ActiveState active_state = kUseCurrent) const;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc index 89b3422..1fa6165 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -10,8 +10,6 @@ #include "build/build_config.h" #include "build/buildflag.h" #include "chrome/browser/themes/theme_properties.h" -#include "chrome/browser/themes/theme_service.h" -#include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h" #include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/views/frame/browser_frame.h" @@ -124,10 +122,8 @@ layout_->set_delegate(this); SetLayoutManager(std::unique_ptr<views::LayoutManager>(layout_)); - // This must be initialised before the call to GetFrameColor(). - platform_observer_.reset(OpaqueBrowserFrameViewPlatformSpecific::Create( - this, layout_, - ThemeServiceFactory::GetForProfile(browser_view->browser()->profile()))); + platform_observer_.reset( + OpaqueBrowserFrameViewPlatformSpecific::Create(this, layout_)); } OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {} @@ -544,13 +540,6 @@ PaintClientEdge(canvas); } -// BrowserNonClientFrameView: -bool OpaqueBrowserFrameView::ShouldPaintAsThemed() const { - // Theme app and popup windows if |platform_observer_| wants it. - return browser_view()->IsBrowserTypeNormal() || - platform_observer_->IsUsingSystemTheme(); -} - /////////////////////////////////////////////////////////////////////////////// // OpaqueBrowserFrameView, private:
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h index cc4a25ae..999b1b9 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -120,9 +120,6 @@ // views::View: void OnPaint(gfx::Canvas* canvas) override; - // BrowserNonClientFrameView: - bool ShouldPaintAsThemed() const override; - OpaqueBrowserFrameViewLayout* layout() { return layout_; } private:
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.cc index 05b10c0..f34965e 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.h" -#include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h" #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" #include "ui/views/linux_ui/linux_ui.h" @@ -14,11 +13,8 @@ OpaqueBrowserFrameViewLinux::OpaqueBrowserFrameViewLinux( OpaqueBrowserFrameView* view, - OpaqueBrowserFrameViewLayout* layout, - ThemeService* theme_service) - : view_(view), - layout_(layout), - theme_service_(theme_service) { + OpaqueBrowserFrameViewLayout* layout) + : view_(view), layout_(layout) { views::LinuxUI* ui = views::LinuxUI::instance(); if (ui) ui->AddWindowButtonOrderObserver(this); @@ -30,10 +26,6 @@ ui->RemoveWindowButtonOrderObserver(this); } -bool OpaqueBrowserFrameViewLinux::IsUsingSystemTheme() { - return theme_service_->UsingSystemTheme(); -} - /////////////////////////////////////////////////////////////////////////////// // OpaqueBrowserFrameViewLinux, // views::WindowButtonOrderObserver implementation: @@ -63,7 +55,6 @@ OpaqueBrowserFrameViewPlatformSpecific* OpaqueBrowserFrameViewPlatformSpecific::Create( OpaqueBrowserFrameView* view, - OpaqueBrowserFrameViewLayout* layout, - ThemeService* theme_service) { - return new OpaqueBrowserFrameViewLinux(view, layout, theme_service); + OpaqueBrowserFrameViewLayout* layout) { + return new OpaqueBrowserFrameViewLinux(view, layout); }
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.h index 172e0bd..b0759dbd 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_linux.h
@@ -17,13 +17,9 @@ public views::WindowButtonOrderObserver { public: OpaqueBrowserFrameViewLinux(OpaqueBrowserFrameView* view, - OpaqueBrowserFrameViewLayout* layout, - ThemeService* theme_service); + OpaqueBrowserFrameViewLayout* layout); ~OpaqueBrowserFrameViewLinux() override; - // Overridden from OpaqueBrowserFrameViewPlatformSpecific: - bool IsUsingSystemTheme() override; - // Overridden from views::WindowButtonOrderObserver: void OnWindowButtonOrderingChange( const std::vector<views::FrameButton>& leading_buttons, @@ -32,7 +28,6 @@ private: OpaqueBrowserFrameView* view_; OpaqueBrowserFrameViewLayout* layout_; - ThemeService* theme_service_; DISALLOW_COPY_AND_ASSIGN(OpaqueBrowserFrameViewLinux); };
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.cc index c3e5f26..029e3ca 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.cc +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.cc
@@ -6,18 +6,13 @@ #include "build/build_config.h" -bool OpaqueBrowserFrameViewPlatformSpecific::IsUsingSystemTheme() { - return false; -} - #if !defined(OS_LINUX) // static OpaqueBrowserFrameViewPlatformSpecific* OpaqueBrowserFrameViewPlatformSpecific::Create( OpaqueBrowserFrameView* view, - OpaqueBrowserFrameViewLayout* layout, - ThemeService* theme_service) { + OpaqueBrowserFrameViewLayout* layout) { return new OpaqueBrowserFrameViewPlatformSpecific(); }
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h index 6916b52..b457233 100644 --- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h +++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h
@@ -7,26 +7,16 @@ class OpaqueBrowserFrameView; class OpaqueBrowserFrameViewLayout; -class ThemeService; // Handles platform specific configuration concepts. class OpaqueBrowserFrameViewPlatformSpecific { public: virtual ~OpaqueBrowserFrameViewPlatformSpecific() {} - // Returns whether we're using native system like rendering for theme - // elements. - // - // Why not just ask ThemeService::UsingSystemTheme()? Because on Windows, the - // default theme is UsingSystemTheme(). Therefore, the default implementation - // always returns false and we specifically override this on Linux. - virtual bool IsUsingSystemTheme(); - // Builds an observer for |view| and |layout|. static OpaqueBrowserFrameViewPlatformSpecific* Create( OpaqueBrowserFrameView* view, - OpaqueBrowserFrameViewLayout* layout, - ThemeService* theme_service); + OpaqueBrowserFrameViewLayout* layout); }; #endif // CHROME_BROWSER_UI_VIEWS_FRAME_OPAQUE_BROWSER_FRAME_VIEW_PLATFORM_SPECIFIC_H_
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc index 5e5f8199..d87ce750 100644 --- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc +++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -21,7 +21,7 @@ // Set so that when hovering over a tab in a inactive window that window will // not become active. Setting this to false creates the need to explicitly // hide the hovercard on press, touch, and keyboard events. - set_can_activate(false); + SetCanActivate(false); title_label_ = new views::Label(base::string16(), CONTEXT_TAB_HOVER_CARD_TITLE,
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc index 8c3dece..bed8524 100644 --- a/chrome/browser/ui/webui/welcome/welcome_ui.cc +++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -10,6 +10,7 @@ #include "base/metrics/histogram_macros.h" #include "build/build_config.h" #include "chrome/browser/signin/account_consistency_mode_manager.h" +#include "chrome/browser/ui/webui/dark_mode_handler.h" #include "chrome/browser/ui/webui/localized_string.h" #include "chrome/browser/ui/webui/welcome/nux/bookmark_handler.h" #include "chrome/browser/ui/webui/welcome/nux/constants.h" @@ -119,6 +120,8 @@ content::WebUIDataSource* html_source = content::WebUIDataSource::Create(url.host()); + DarkModeHandler::Initialize(web_ui, html_source); + bool is_dice = AccountConsistencyModeManager::IsDiceEnabledForProfile(profile);
diff --git a/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc b/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc index a18d582..7f05e32 100644 --- a/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc +++ b/chrome/renderer/extensions/extension_hooks_delegate_unittest.cc
@@ -6,6 +6,7 @@ #include "base/strings/stringprintf.h" #include "content/public/common/child_process_host.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_messages.h" #include "extensions/common/value_builder.h" @@ -184,7 +185,8 @@ DictionaryBuilder().Set("tabId", tab_id).Build().get()); ExtensionMsg_ExternalConnectionInfo external_connection_info; external_connection_info.target_id = extension()->id(); - external_connection_info.source_id = extension()->id(); + external_connection_info.source_endpoint = + MessagingEndpoint::ForExtension(extension()->id()); external_connection_info.source_url = source_url; external_connection_info.guest_process_id = content::ChildProcessHost::kInvalidUniqueID;
diff --git a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js index 9357856..5862078 100644 --- a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js +++ b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js
@@ -64,6 +64,7 @@ /** * Get a cached bound InterfacePtr for this InputChannel impl. * Create one the ptr if it's not bound yet. + * * @return {!chromeos.ime.mojom.InputChannelPtr}. */ getChannelPtr() { @@ -73,6 +74,7 @@ /** * Set a handler for processing text message. The handler must return a * nonnull string, otherwise it will lead to disconnection. + * * @param {function(string):string} handler. */ onTextMessage(handler) { @@ -83,6 +85,7 @@ /** * Set a handler for processing protobuf message. The handler must return a * nonnull Uint8Array, otherwise it will lead to disconnection. + * * @param {function(!Uint8Array):!Uint8Array} handler. */ onProtobufMessage(handler) { @@ -122,6 +125,7 @@ /** * Set the error handler when the channel Mojo pipe is disconnected. + * * @param {function():void} handler. */ setConnectionErrorHandler(handler) { @@ -178,6 +182,7 @@ /** * Set the error handler when the IME Mojo service is disconnected. + * * @param {function():void} callback. */ setConnectionErrorHandler(callback) { @@ -198,7 +203,33 @@ } /** + * Set a handler for the bound client delegate to process messages in text + * format. This should be called after an IME engine is activated. + * + * @param {!function(string):string} callback Callback on text message. + */ + setDelegateTextHandler(callback) { + if (this.clientChannel_ && this.clientChannel_.ptr.isBound()) { + this.clientChannel_.onTextMessage(callback); + } + } + + /** + * Set a handler for the bound client delegate to process messages in protobuf + * format. This should be called after an IME engine is activated. + * + * @param {!function(!Uint8Array):!Uint8Array} callback Callback on protobuf + * message. + */ + setDelegateProtobufHandler(callback) { + if (this.clientChannel_ && this.clientChannel_.ptr.isBound()) { + this.clientChannel_.onProtobufMessage(callback); + } + } + + /** * Activates an input method based on its specification. + * * @param {string} imeSpec The specification of an IME (e.g. the engine ID). * @param {!Uint8Array} extra The extra data (e.g. initial tasks to run). * @param {function(boolean):void} onConnection The callback function to @@ -244,6 +275,7 @@ this.activeEngine_.ptr.reset(); } this.activeEngine_ = null; + // TODO(crbug.com/837156): Release client channel? } }
diff --git a/chrome/services/app_service/public/cpp/app_update.cc b/chrome/services/app_service/public/cpp/app_update.cc index 1ad15c6a..7ee4e64 100644 --- a/chrome/services/app_service/public/cpp/app_update.cc +++ b/chrome/services/app_service/public/cpp/app_update.cc
@@ -63,6 +63,9 @@ if (delta->installed_internally != apps::mojom::OptionalBool::kUnknown) { state->installed_internally = delta->installed_internally; } + if (delta->is_platform_app != apps::mojom::OptionalBool::kUnknown) { + state->is_platform_app = delta->is_platform_app; + } if (delta->show_in_launcher != apps::mojom::OptionalBool::kUnknown) { state->show_in_launcher = delta->show_in_launcher; } @@ -222,6 +225,23 @@ (delta_->installed_internally != state_->installed_internally)); } +apps::mojom::OptionalBool AppUpdate::IsPlatformApp() const { + if (delta_ && + (delta_->is_platform_app != apps::mojom::OptionalBool::kUnknown)) { + return delta_->is_platform_app; + } + if (state_) { + return state_->is_platform_app; + } + return apps::mojom::OptionalBool::kUnknown; +} + +bool AppUpdate::IsPlatformAppChanged() const { + return delta_ && + (delta_->is_platform_app != apps::mojom::OptionalBool::kUnknown) && + (!state_ || (delta_->is_platform_app != state_->is_platform_app)); +} + apps::mojom::OptionalBool AppUpdate::ShowInLauncher() const { if (delta_ && (delta_->show_in_launcher != apps::mojom::OptionalBool::kUnknown)) {
diff --git a/chrome/services/app_service/public/cpp/app_update.h b/chrome/services/app_service/public/cpp/app_update.h index 4b94698d..aedcea5 100644 --- a/chrome/services/app_service/public/cpp/app_update.h +++ b/chrome/services/app_service/public/cpp/app_update.h
@@ -82,6 +82,9 @@ apps::mojom::OptionalBool InstalledInternally() const; bool InstalledInternallyChanged() const; + apps::mojom::OptionalBool IsPlatformApp() const; + bool IsPlatformAppChanged() const; + apps::mojom::OptionalBool ShowInLauncher() const; bool ShowInLauncherChanged() const;
diff --git a/chrome/services/app_service/public/cpp/app_update_unittest.cc b/chrome/services/app_service/public/cpp/app_update_unittest.cc index ca4a56c..1ba673e 100644 --- a/chrome/services/app_service/public/cpp/app_update_unittest.cc +++ b/chrome/services/app_service/public/cpp/app_update_unittest.cc
@@ -38,6 +38,9 @@ apps::mojom::OptionalBool expect_installed_internally_; bool expect_installed_internally_changed_; + apps::mojom::OptionalBool expect_is_platform_app_; + bool expect_is_platform_app_changed_; + apps::mojom::OptionalBool expect_show_in_launcher_; bool expect_show_in_launcher_changed_; @@ -65,6 +68,7 @@ expect_install_time_changed_ = false; expect_permissions_changed_ = false; expect_installed_internally_changed_ = false; + expect_is_platform_app_changed_ = false; expect_show_in_launcher_changed_ = false; expect_show_in_search_changed_ = false; } @@ -95,6 +99,9 @@ EXPECT_EQ(expect_installed_internally_changed_, u.InstalledInternallyChanged()); + EXPECT_EQ(expect_is_platform_app_, u.IsPlatformApp()); + EXPECT_EQ(expect_is_platform_app_changed_, u.IsPlatformAppChanged()); + EXPECT_EQ(expect_show_in_launcher_, u.ShowInLauncher()); EXPECT_EQ(expect_show_in_launcher_changed_, u.ShowInLauncherChanged()); @@ -117,6 +124,7 @@ expect_install_time_ = base::Time(); expect_permissions_.clear(); expect_installed_internally_ = apps::mojom::OptionalBool::kUnknown; + expect_is_platform_app_ = apps::mojom::OptionalBool::kUnknown; expect_show_in_launcher_ = apps::mojom::OptionalBool::kUnknown; expect_show_in_search_ = apps::mojom::OptionalBool::kUnknown; ExpectNoChange(); @@ -278,6 +286,28 @@ CheckExpects(u); } + // IsPlatformApp tests. + + if (state) { + state->is_platform_app = apps::mojom::OptionalBool::kFalse; + expect_is_platform_app_ = apps::mojom::OptionalBool::kFalse; + expect_is_platform_app_changed_ = false; + CheckExpects(u); + } + + if (delta) { + delta->is_platform_app = apps::mojom::OptionalBool::kTrue; + expect_is_platform_app_ = apps::mojom::OptionalBool::kTrue; + expect_is_platform_app_changed_ = true; + CheckExpects(u); + } + + if (state) { + apps::AppUpdate::Merge(state, delta); + ExpectNoChange(); + CheckExpects(u); + } + // ShowInSearch tests. if (state) {
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom index bf3b492..8ba3cce 100644 --- a/chrome/services/app_service/public/mojom/types.mojom +++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -31,6 +31,10 @@ // Whether the app was installed by sync, policy or as a default app. OptionalBool installed_internally; + // Whether the app is an extensions::Extensions where is_platform_app() + // returns true. + OptionalBool is_platform_app; + // TODO(nigeltao): be more principled, instead of ad hoc show_in_xxx and // show_in_yyy fields? OptionalBool show_in_launcher;
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.cc b/chrome/services/printing/pdf_to_pwg_raster_converter.cc index 9291473..59b7200 100644 --- a/chrome/services/printing/pdf_to_pwg_raster_converter.cc +++ b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
@@ -68,6 +68,22 @@ ? pwg_encoder::PwgHeaderInfo::SRGB : pwg_encoder::PwgHeaderInfo::SGRAY; + switch (bitmap_settings.duplex_mode) { + case DuplexMode::UNKNOWN_DUPLEX_MODE: + NOTREACHED(); + break; + case DuplexMode::SIMPLEX: + // Already defaults to false/false. + break; + case DuplexMode::LONG_EDGE: + header_info.duplex = true; + break; + case DuplexMode::SHORT_EDGE: + header_info.duplex = true; + header_info.tumble = true; + break; + } + // Transform odd pages. if (page_number % 2) { switch (bitmap_settings.odd_page_transform) {
diff --git a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom index d5e1511b2..285099a 100644 --- a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom +++ b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom
@@ -15,6 +15,14 @@ TRANSFORM_FLIP_VERTICAL }; + enum DuplexMode { + SIMPLEX, + LONG_EDGE, + SHORT_EDGE, + }; + + DuplexMode duplex_mode; + // How to transform odd-numbered pages. TransformType odd_page_transform;
diff --git a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.cc b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.cc index df9ece4..c66dcc3 100644 --- a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.cc +++ b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.cc
@@ -14,7 +14,8 @@ out->rotate_all_pages = data.rotate_all_pages(); out->reverse_page_order = data.reverse_page_order(); out->use_color = data.use_color(); - return data.ReadOddPageTransform(&out->odd_page_transform); + return data.ReadOddPageTransform(&out->odd_page_transform) && + data.ReadDuplexMode(&out->duplex_mode); } } // namespace mojo
diff --git a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.h b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.h index 7c83a273..5eb87a8 100644 --- a/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.h +++ b/chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter_mojom_traits.h
@@ -60,6 +60,43 @@ }; template <> +struct EnumTraits<printing::mojom::PwgRasterSettings::DuplexMode, + printing::DuplexMode> { + static printing::mojom::PwgRasterSettings::DuplexMode ToMojom( + printing::DuplexMode duplex_mode) { + switch (duplex_mode) { + case printing::DuplexMode::UNKNOWN_DUPLEX_MODE: + break; + case printing::DuplexMode::SIMPLEX: + return printing::mojom::PwgRasterSettings::DuplexMode::SIMPLEX; + case printing::DuplexMode::LONG_EDGE: + return printing::mojom::PwgRasterSettings::DuplexMode::LONG_EDGE; + case printing::DuplexMode::SHORT_EDGE: + return printing::mojom::PwgRasterSettings::DuplexMode::SHORT_EDGE; + } + NOTREACHED() << "Unknown duplex mode " << static_cast<int>(duplex_mode); + return printing::mojom::PwgRasterSettings::DuplexMode::SIMPLEX; + } + + static bool FromMojom(printing::mojom::PwgRasterSettings::DuplexMode input, + printing::DuplexMode* output) { + switch (input) { + case printing::mojom::PwgRasterSettings::DuplexMode::SIMPLEX: + *output = printing::DuplexMode::SIMPLEX; + return true; + case printing::mojom::PwgRasterSettings::DuplexMode::LONG_EDGE: + *output = printing::DuplexMode::LONG_EDGE; + return true; + case printing::mojom::PwgRasterSettings::DuplexMode::SHORT_EDGE: + *output = printing::DuplexMode::SHORT_EDGE; + return true; + } + NOTREACHED() << "Unknown duplex mode " << static_cast<int>(input); + return false; + } +}; + +template <> class StructTraits<printing::mojom::PwgRasterSettingsDataView, printing::PwgRasterSettings> { public: @@ -76,6 +113,10 @@ const printing::PwgRasterSettings& settings) { return settings.odd_page_transform; } + static printing::DuplexMode duplex_mode( + const printing::PwgRasterSettings& settings) { + return settings.duplex_mode; + } static bool Read(printing::mojom::PwgRasterSettingsDataView data, printing::PwgRasterSettings* out_settings);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 5c01854..484506e 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -5219,6 +5219,13 @@ # target should be deleted and this line removed. See the # chrome_extensions_interactive_uitests target for more. deps += [ "//extensions:chrome_extensions_interactive_uitests" ] + + if (include_js_tests) { + sources += [ + "../browser/ui/webui/extensions/extension_settings_browsertest.cc", + "../browser/ui/webui/extensions/extension_settings_browsertest.h", + ] + } } if (!enable_native_notifications) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java index d9e4493..75dc803 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java
@@ -14,6 +14,8 @@ HANDSET, TABLET } + public enum SignedInStatus { TRUE, FALSE } + public static final String UUID_PHONE = "uuid_phone"; public static final String UUID_TABLET = "uuid_tablet"; public static final String SERVER_URL = "http://totallylegitserver.com"; @@ -27,9 +29,13 @@ private final boolean mIsOnTablet; - public MockRequestGenerator(Context context, DeviceType deviceType) { + private final boolean mIsSignedIn; + + public MockRequestGenerator( + Context context, DeviceType deviceType, SignedInStatus signInStatus) { super(context); mIsOnTablet = deviceType == DeviceType.TABLET; + mIsSignedIn = signInStatus == SignedInStatus.TRUE; } @Override @@ -73,6 +79,11 @@ } @Override + public int getNumSignedIn() { + return mIsSignedIn ? 1 : 0; + } + + @Override public String getAdditionalParameters() { return ADDITIONAL_PARAMETERS; }
diff --git a/chrome/test/chromedriver/server/server.py b/chrome/test/chromedriver/server/server.py index 164fc76..24b1da5f 100644 --- a/chrome/test/chromedriver/server/server.py +++ b/chrome/test/chromedriver/server/server.py
@@ -43,8 +43,9 @@ chromedriver_args.extend(['--devtools-replay=%s' % devtools_replay_path]) self._process = subprocess.Popen(chromedriver_args) - self._url = 'http://127.0.0.1:%d' % port + self._host = '127.0.0.1' self._port = port + self._url = 'http://%s:%d' % (self._host, port) if self._process is None: raise RuntimeError('ChromeDriver server cannot be started') @@ -68,6 +69,9 @@ def GetUrl(self): return self._url + def GetHost(self): + return self._host + def GetPort(self): return self._port
diff --git a/chrome/test/chromedriver/test/run_webdriver_tests.py b/chrome/test/chromedriver/test/run_webdriver_tests.py new file mode 100644 index 0000000..4fcfcdb --- /dev/null +++ b/chrome/test/chromedriver/test/run_webdriver_tests.py
@@ -0,0 +1,187 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""WPT WebDriver tests runner.""" + +import pytest +import os +import argparse +import sys +import json +import time + +_TEST_DIR = os.path.abspath(os.path.dirname(__file__)) +_CHROMEDRIVER_DIR = os.path.join(_TEST_DIR, os.pardir) +SRC_DIR = os.path.join(_CHROMEDRIVER_DIR, os.pardir, os.pardir, os.pardir) +_CLIENT_DIR = os.path.join(_CHROMEDRIVER_DIR, "client") +_SERVER_DIR = os.path.join(_CHROMEDRIVER_DIR, "server") + +sys.path.insert(1, _CHROMEDRIVER_DIR) +import util + +sys.path.insert(1, _SERVER_DIR) +import server + +WD_CLIENT_PATH = ('third_party/blink/tools/blinkpy/' + 'third_party/wpt/wpt/tools/webdriver') +WEBDRIVER_CLIENT_ABS_PATH = os.path.join(SRC_DIR, WD_CLIENT_PATH) + + +class WebDriverTestResult(object): + def __init__(self, test_name, test_status, messsage=None): + self.test_name = test_name + self.test_status = test_status + self.message = messsage + +class SubtestResultRecorder(object): + def __init__(self): + self.result = [] + + def pytest_runtest_logreport(self, report): + if report.passed and report.when == "call": + self.record_pass(report) + elif report.failed: + if report.when != "call": + self.record_error(report) + else: + self.record_fail(report) + elif report.skipped: + self.record_skip(report) + + def record_pass(self, report): + self.record(report.nodeid, "PASS") + + def record_fail(self, report): + message = report.longrepr.reprcrash.message + self.record(report.nodeid, "FAIL", message=message) + + def record_error(self, report): + # error in setup/teardown + if report.when != "call": + message = "error in %s" % report.when + self.record(report.nodeid, "FAIL", message) + + def record_skip(self, report): + self.record(report.nodeid, "FAIL", + "In-test skip decorators are disallowed.") + + def record(self, test, status, message=None): + self.result.append(WebDriverTestResult( + test, status, message)) + +def set_up_config(chromedriver_server): + # These envs are used to create a WebDriver session in the fixture.py file. + os.environ["WD_HOST"] = chromedriver_server.GetHost() + os.environ["WD_PORT"] = str(chromedriver_server.GetPort()) + os.environ["WD_CAPABILITIES"] = json.dumps({ + "goog:chromeOptions": { + "w3c": True, + "prefs": { + "profile": { + "default_content_setting_values": { + "popups": 1 + } + } + }, + } + }) + os.environ["WD_SERVER_CONFIG"] = json.dumps({ + "browser_host": "web-platform.test", + "ports": {"http": [8000, 8001]}}) + +def run_test(path): + subtests = SubtestResultRecorder() + pytest.main(["--show-capture", "no", path], plugins=[subtests]) + return subtests.result + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.description = __doc__ + parser.add_argument( + '--chromedriver', + required=True, + help='Path to chromedriver binary') + parser.add_argument( + '--log-path', + help='Output verbose server logs to this file') + parser.add_argument( + '--chrome', help='Path to chrome binary') + parser.add_argument( + '--isolated-script-test-output', + help='JSON output file used by swarming') + parser.add_argument( + '--isolated-script-test-perf-output', + help='JSON perf output file used by swarming, ignored') + parser.add_argument( + '--test-path', + help='Path to the WPT WebDriver tests') + + options = parser.parse_args() + + options.chromedriver = util.GetAbsolutePathOfUserPath(options.chromedriver) + if (not os.path.exists(options.chromedriver) and + util.GetPlatformName() == 'win' and + not options.chromedriver.lower().endswith('.exe')): + options.chromedriver = options.chromedriver + '.exe' + + if not os.path.exists(options.chromedriver): + parser.error('Path given by --chromedriver is invalid.\n' + + 'Please run "%s --help" for help' % __file__) + + chromedriver_server = server.Server(options.chromedriver, options.log_path) + + if not chromedriver_server.IsRunning(): + print 'ChromeDriver is not running.' + sys.exit(1) + + set_up_config(chromedriver_server) + + test_path = options.test_path + start_time = time.time() + test_results = [] + sys.path.insert(1, WEBDRIVER_CLIENT_ABS_PATH) + if os.path.isfile(test_path): + test_results = run_test(test_path) + elif os.path.isdir(test_path): + for root, dirnames, filenames in os.walk(test_path): + for filename in filenames: + if '__init__' in filename: + continue + test_file = os.path.join(root, filename) + test_results += run_test(test_file) + else: + print '%s is not a file nor directory.' % test_path + sys.exit(1) + sys.path.remove(WEBDRIVER_CLIENT_ABS_PATH) + + chromedriver_server.Kill() + + if options.isolated_script_test_output: + output = { + 'interrupted': False, + 'num_failures_by_type': { }, + 'path_delimiter': '.', + 'seconds_since_epoch': start_time, + 'tests': { }, + 'version': 3, + } + + success_count = 0 + for test_result in test_results: + output['tests'][test_result.test_name] = { + 'expected': 'PASS', + 'actual': test_result.test_status, + 'message': test_result.message + } + + if test_result.test_status == 'PASS': + success_count += 1 + + output['num_failures_by_type']['PASS'] = success_count + output['num_failures_by_type']['FAIL'] = len(test_results) - success_count + + with open(options.isolated_script_test_output, 'w') as fp: + json.dump(output, fp) + + sys.exit(0)
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js index dc0f2666..04d1eea 100644 --- a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js +++ b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
@@ -184,29 +184,4 @@ })); }); }, - - function testRedirectToUrlWithExtraHeadersListener() { - // Set a cookie so the cookie request header is set. - navigateAndWait(getSetCookieUrl('foo', 'bar'), function() { - var finalURL = getServerURL('echoheader?Cookie'); - var url = getServerURL('server-redirect?' + finalURL); - var listener = callbackPass(function(details) { - removeHeader(details.requestHeaders, 'cookie'); - return {requestHeaders: details.requestHeaders}; - }); - chrome.webRequest.onBeforeSendHeaders.addListener(listener, - {urls: [finalURL]}, ['requestHeaders', 'blocking', 'extraHeaders']); - - navigateAndWait(url, function(tab) { - chrome.test.assertEq(finalURL, tab.url); - chrome.webRequest.onBeforeSendHeaders.removeListener(listener); - chrome.tabs.executeScript(tab.id, { - code: 'document.body.innerText' - }, callbackPass(function(results) { - chrome.test.assertTrue(results[0].indexOf('bar') == -1, - 'Header not removed.'); - })); - }); - }); - }, ]);
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js index 6d593a9..d4c19779 100644 --- a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js +++ b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
@@ -27,253 +27,203 @@ }); } -chrome.test.getConfig(function(config) { - var onHeadersReceivedExtraInfoSpec = ['blocking']; - if (config.customArg === 'useExtraHeaders') - onHeadersReceivedExtraInfoSpec.push('extraHeaders'); +runTests([ + function redirectToDataUrlOnHeadersReceived() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: dataURL}; + }; + chrome.webRequest.onHeadersReceived.addListener(listener, + {urls: [url]}, ['blocking']); - runTests([ - function redirectToDataUrlOnHeadersReceived() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: dataURL}; - }; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: [url]}, onHeadersReceivedExtraInfoSpec); + assertRedirectSucceeds(url, dataURL, function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, dataURL, function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function redirectToAboutUrlOnHeadersReceived() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: aboutURL}; + }; + chrome.webRequest.onHeadersReceived.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToAboutUrlOnHeadersReceived() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: aboutURL}; - }; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: [url]}, onHeadersReceivedExtraInfoSpec); + assertRedirectSucceeds(url, aboutURL, function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, aboutURL, function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function redirectToNonWebAccessibleUrlOnHeadersReceived() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: getURLNonWebAccessible()}; + }; + chrome.webRequest.onHeadersReceived.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToNonWebAccessibleUrlOnHeadersReceived() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: getURLNonWebAccessible()}; - }; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: [url]}, onHeadersReceivedExtraInfoSpec); + assertRedirectSucceeds(url, getURLNonWebAccessible(), function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, getURLNonWebAccessible(), function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function redirectToServerRedirectOnHeadersReceived() { + var url = getServerURL('echo'); + var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible()); + var listener = function(details) { + return {redirectUrl: redirectURL}; + }; + chrome.webRequest.onHeadersReceived.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToServerRedirectOnHeadersReceived() { - var url = getServerURL('echo'); - var redirectURL = getServerURL('server-redirect?' + - getURLWebAccessible()); - var listener = function(details) { - return {redirectUrl: redirectURL}; - }; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: [url]}, onHeadersReceivedExtraInfoSpec); + assertRedirectSucceeds(url, getURLWebAccessible(), function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, getURLWebAccessible(), function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function serverRedirectThenExtensionRedirectOnHeadersReceived() { + var url_1 = getServerURL('echo'); + var url_2 = getURLWebAccessible(); + var serverRedirect = getServerURL('server-redirect?' + url_1); + var listener = function(details) { + return {redirectUrl: url_2}; + }; + chrome.webRequest.onHeadersReceived.addListener( + listener, + { urls: [url_1] }, + ["blocking"] + ); - function serverRedirectThenExtensionRedirectOnHeadersReceived() { - var url_1 = getServerURL('echo'); - var url_2 = getURLWebAccessible(); - var serverRedirect = getServerURL('server-redirect?' + url_1); - var listener = function(details) { - return {redirectUrl: url_2}; - }; - chrome.webRequest.onHeadersReceived.addListener( - listener, - { urls: [url_1] }, - ["blocking"] - ); + assertRedirectSucceeds(serverRedirect, url_2, function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - assertRedirectSucceeds(serverRedirect, url_2, function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function redirectToUnallowedServerRedirectOnHeadersReceived() { + var url = getServerURL('echo'); + var redirectURL = getServerURL('server-redirect?' + + getURLNonWebAccessible()); + var listener = function(details) { + return {redirectUrl: redirectURL}; + }; + chrome.webRequest.onHeadersReceived.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToUnallowedServerRedirectOnHeadersReceived() { - var url = getServerURL('echo'); - var redirectURL = getServerURL('server-redirect?' + - getURLNonWebAccessible()); - var listener = function(details) { - return {redirectUrl: redirectURL}; - }; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: [url]}, onHeadersReceivedExtraInfoSpec); + // The page should be redirected to redirectURL, but not to the non web + // accessible URL. + assertRedirectSucceeds(url, redirectURL, function() { + chrome.webRequest.onHeadersReceived.removeListener(listener); + }); + }, - // The page should be redirected to redirectURL, but not to the non web - // accessible URL. - assertRedirectSucceeds(url, redirectURL, function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, + function redirectToDataUrlOnBeforeRequest() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: dataURL}; + }; + chrome.webRequest.onBeforeRequest.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToDataUrlOnBeforeRequest() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: dataURL}; - }; - chrome.webRequest.onBeforeRequest.addListener(listener, - {urls: [url]}, ['blocking']); + assertRedirectSucceeds(url, dataURL, function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, dataURL, function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + function redirectToAboutUrlOnBeforeRequest() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: aboutURL}; + }; + chrome.webRequest.onBeforeRequest.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToAboutUrlOnBeforeRequest() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: aboutURL}; - }; - chrome.webRequest.onBeforeRequest.addListener(listener, - {urls: [url]}, ['blocking']); + assertRedirectSucceeds(url, aboutURL, function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, aboutURL, function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + function redirectToNonWebAccessibleUrlOnBeforeRequest() { + var url = getServerURL('echo'); + var listener = function(details) { + return {redirectUrl: getURLNonWebAccessible()}; + }; + chrome.webRequest.onBeforeRequest.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToNonWebAccessibleUrlOnBeforeRequest() { - var url = getServerURL('echo'); - var listener = function(details) { - return {redirectUrl: getURLNonWebAccessible()}; - }; - chrome.webRequest.onBeforeRequest.addListener(listener, - {urls: [url]}, ['blocking']); + assertRedirectSucceeds(url, getURLNonWebAccessible(), function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, getURLNonWebAccessible(), function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + function redirectToServerRedirectOnBeforeRequest() { + var url = getServerURL('echo'); + var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible()); + var listener = function(details) { + return {redirectUrl: redirectURL}; + }; + chrome.webRequest.onBeforeRequest.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToServerRedirectOnBeforeRequest() { - var url = getServerURL('echo'); - var redirectURL = getServerURL('server-redirect?' + - getURLWebAccessible()); - var listener = function(details) { - return {redirectUrl: redirectURL}; - }; - chrome.webRequest.onBeforeRequest.addListener(listener, - {urls: [url]}, ['blocking']); + assertRedirectSucceeds(url, getURLWebAccessible(), function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - assertRedirectSucceeds(url, getURLWebAccessible(), function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + // A server redirect immediately followed by an extension redirect. + // Regression test for: + // - https://crbug.com/882661 + // - https://crbug.com/880741 + function serverRedirectThenExtensionRedirectOnBeforeRequest() { + var url_1 = getServerURL('echo'); + var url_2 = getURLWebAccessible(); + var serverRedirect = getServerURL('server-redirect?' + url_1); + var listener = function(details) { + return {redirectUrl: url_2}; + }; + chrome.webRequest.onBeforeRequest.addListener( + listener, + { urls: [url_1] }, + ["blocking"] + ); - // A server redirect immediately followed by an extension redirect. - // Regression test for: - // - https://crbug.com/882661 - // - https://crbug.com/880741 - function serverRedirectThenExtensionRedirectOnBeforeRequest() { - var url_1 = getServerURL('echo'); - var url_2 = getURLWebAccessible(); - var serverRedirect = getServerURL('server-redirect?' + url_1); - var listener = function(details) { - return {redirectUrl: url_2}; - }; - chrome.webRequest.onBeforeRequest.addListener( - listener, - { urls: [url_1] }, - ["blocking"] - ); + assertRedirectSucceeds(serverRedirect, url_2, function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - assertRedirectSucceeds(serverRedirect, url_2, function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + function redirectToUnallowedServerRedirectOnBeforeRequest() { + var url = getServerURL('echo'); + var redirectURL = getServerURL('server-redirect?' + + getURLNonWebAccessible()); + var listener = function(details) { + return {redirectUrl: redirectURL}; + }; + chrome.webRequest.onBeforeRequest.addListener(listener, + {urls: [url]}, ['blocking']); - function redirectToUnallowedServerRedirectOnBeforeRequest() { - var url = getServerURL('echo'); - var redirectURL = getServerURL('server-redirect?' + - getURLNonWebAccessible()); - var listener = function(details) { - return {redirectUrl: redirectURL}; - }; - chrome.webRequest.onBeforeRequest.addListener(listener, - {urls: [url]}, ['blocking']); + // The page should be redirected to redirectURL, but not to the non web + // accessible URL. + assertRedirectSucceeds(url, redirectURL, function() { + chrome.webRequest.onBeforeRequest.removeListener(listener); + }); + }, - // The page should be redirected to redirectURL, but not to the non web - // accessible URL. - assertRedirectSucceeds(url, redirectURL, function() { - chrome.webRequest.onBeforeRequest.removeListener(listener); - }); - }, + function redirectToAboutUrlWithServerRedirect() { + assertRedirectFails(getServerURL('server-redirect?' + aboutURL)); + }, - function redirectToAboutUrlWithServerRedirect() { - assertRedirectFails(getServerURL('server-redirect?' + aboutURL)); - }, + function redirectToDataUrlWithServerRedirect() { + assertRedirectFails(getServerURL('server-redirect?' + dataURL)); + }, - function redirectToDataUrlWithServerRedirect() { - assertRedirectFails(getServerURL('server-redirect?' + dataURL)); - }, + function redirectToNonWebAccessibleUrlWithServerRedirect() { + assertRedirectFails( + getServerURL('server-redirect?' + getURLNonWebAccessible())); + }, - function redirectToNonWebAccessibleUrlWithServerRedirect() { - assertRedirectFails( - getServerURL('server-redirect?' + getURLNonWebAccessible())); - }, - - function redirectToWebAccessibleUrlWithServerRedirect() { - assertRedirectSucceeds( - getServerURL('server-redirect?' + getURLWebAccessible()), - getURLWebAccessible()); - }, - - function beforeRequestRedirectAfterServerRedirect() { - var finalURL = getServerURL('echo?foo'); - var intermediateURL = getServerURL('echo?bar'); - var redirectURL = getServerURL('server-redirect?' + intermediateURL); - - var onBeforeSendHeadersListener = function(details) { - chrome.test.assertFalse(details.url == intermediateURL, - 'intermediateURL should be redirected before the request starts.'); - }; - // Make sure all URLs use the extraHeaders path to expose - // http://crbug.com/918761. - chrome.webRequest.onBeforeSendHeaders.addListener( - onBeforeSendHeadersListener, - {urls: ['<all_urls>']}, ['blocking', 'extraHeaders']); - - var onBeforeRequestListener = function(details) { - return {redirectUrl: finalURL}; - }; - chrome.webRequest.onBeforeRequest.addListener(onBeforeRequestListener, - {urls: [intermediateURL]}, ['blocking']); - - assertRedirectSucceeds(redirectURL, finalURL, function() { - chrome.webRequest.onBeforeRequest.removeListener( - onBeforeRequestListener); - chrome.webRequest.onBeforeSendHeaders.removeListener( - onBeforeSendHeadersListener); - }); - }, - - function serverRedirectChain() { - var url = getServerURL('echo'); - var redirectURL = getServerURL('server-redirect?' + - getServerURL('server-redirect?' + url)); - var listener = function(details) {}; - chrome.webRequest.onHeadersReceived.addListener(listener, - {urls: ['<all_urls>']}, onHeadersReceivedExtraInfoSpec); - - assertRedirectSucceeds(redirectURL, url, function() { - chrome.webRequest.onHeadersReceived.removeListener(listener); - }); - }, - ]); -}); + function redirectToWebAccessibleUrlWithServerRedirect() { + assertRedirectSucceeds( + getServerURL('server-redirect?' + getURLWebAccessible()), + getURLWebAccessible()); + }, +]);
diff --git a/chrome/test/data/extensions/options_page_in_view/manifest.json b/chrome/test/data/extensions/options_page_in_view/manifest.json index 1331309..3f36abb 100644 --- a/chrome/test/data/extensions/options_page_in_view/manifest.json +++ b/chrome/test/data/extensions/options_page_in_view/manifest.json
@@ -1,6 +1,7 @@ { "name": "Extension With Options Page", "description": "An extension with an options page and an extra page", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm3v3jAleGUmPIlw9zsz5xfNE0uP41ZguA2nz4sM6Dy477Ve8rLMwqdsDjxDDnHWdIxNsGkXQbP+Yo8jhH/tEc1lo780b5T3Sjj+ULKsW/lunxQV35Q4x5UNs0vryQvVZZCn4euYSn66XtRGIe4+3nq55cw8HgF2Ex3r7jJ3jRzQg0LV9j1JyoibkjwAqxvdYdnHtMvVfJPTo8NoTrFFZbq1qBQTZCvZKIFJX/iwY/ib3AnH+DCnc1jj2mBuoOoNYHuRwYt/7uCmst8UNSr47deNlv4neCh5IIWZjz6AROFRUGFrIcP6s3f1FiOQoGGAD5x7adL2zAsScNqyCm2TczQIDAQAB", "options_ui": { "page": "options.html", "open_in_tab": false }, "version": "0.1.1", "manifest_version": 2
diff --git a/chrome/test/data/extensions/theme_test_toolbar_color_no_image/manifest.json b/chrome/test/data/extensions/theme_test_toolbar_color_no_image/manifest.json new file mode 100644 index 0000000..501223e --- /dev/null +++ b/chrome/test/data/extensions/theme_test_toolbar_color_no_image/manifest.json
@@ -0,0 +1,12 @@ +{ + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGMyBRIRAOiUgubfmS+4RHiNi/u/okGcU17xoKTXnziTiwmo7lq4V7unr3Vn4wWSG5bIG+yhQhQAG1TvfsXimBIUrrVKX4b9IADZIHR5akWgb1K4EV111PW/D8RDOwYgTPHPx5eA49TxB0Yw36DNC9LE0tPZOGgDR9hCFgaN1bcszlmJBcBmx39iGCeKPIjKB4bp5hXe33T5IUdoyuW4JHf4JDqC6unwQ1Y9nwzCRUbFis7YevbGQCgWP3I+Hp+bOo30mC2/Gq2UZfIczR4vIOrarxBV8IO4Ue4gzaDl6dGkGKnkZ3JdZKbLeRke1DmaKfjVSsTUp++X2nfMaPZphQIDAQAB", + "manifest_version": 2, + "name": "Toolbar Color with No Image Test", + "theme": { + "colors": { + "frame": [ 255, 0, 255 ], + "toolbar": [ 0, 255, 0 ] + } + }, + "version": "0.0.1" +}
diff --git a/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test.pwg b/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test.pwg new file mode 100644 index 0000000..ee083dc --- /dev/null +++ b/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test.pwg Binary files differ
diff --git a/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test_32.pwg b/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test_32.pwg new file mode 100644 index 0000000..9c56b86 --- /dev/null +++ b/chrome/test/data/printing/pdf_to_pwg_raster_long_edge_test_32.pwg Binary files differ
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index a32d494..e13e316c 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -12,6 +12,7 @@ "bookmarks/bookmarks_focus_test.js", "cr_elements/cr_elements_focus_test.js", "cr_focus_row_behavior_interactive_test.js", + "extensions/cr_extensions_interactive_ui_tests.js", "history/history_focus_test.js", "print_preview/print_preview_interactive_ui_tests.js", "settings/cr_settings_interactive_ui_tests.js",
diff --git a/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js new file mode 100644 index 0000000..2d56707 --- /dev/null +++ b/chrome/test/data/webui/extensions/cr_extensions_interactive_ui_tests.js
@@ -0,0 +1,63 @@ +// Copyright 2019 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 Runs the Polymer Extensions interactive UI tests. */ + +/** @const {string} Path to source root. */ +const ROOT_PATH = '../../../../../'; + +// Polymer BrowserTest fixture. +GEN_INCLUDE( + [ROOT_PATH + 'chrome/test/data/webui/polymer_interactive_ui_test.js']); +GEN('#include "chrome/browser/ui/webui/extensions/' + + 'extension_settings_browsertest.h"'); + +/** + * Test fixture for interactive Polymer Extensions elements. + * @constructor + * @extends {PolymerInteractiveUITest} + */ +const CrExtensionsInteractiveUITest = class extends PolymerInteractiveUITest { + /** @override */ + get browsePreload() { + return 'chrome://extensions/'; + } + + /** @override */ + get extraLibraries() { + return PolymerTest.getLibraries(ROOT_PATH).concat([ + '../settings/test_util.js', + ]); + } +}; + + +/** Test fixture for Sync Page. */ +CrExtensionsOptionsPageTest = class extends CrExtensionsInteractiveUITest { + /** @override */ + get browsePreload() { + return 'chrome://extensions/?id=ibbpngabdmdpednkhonkkobdeccpkiff'; + } + + /** @override */ + get extraLibraries() { + return super.extraLibraries.concat([ + 'extension_options_dialog_test.js', + ]); + } + + /** @override */ + testGenPreamble() { + GEN(' InstallExtensionWithInPageOptions();'); + } + + /** @override */ + get typedefCppFixture() { + return 'ExtensionSettingsUIBrowserTest'; + } +}; + +TEST_F('CrExtensionsOptionsPageTest', 'All', function() { + mocha.run(); +});
diff --git a/chrome/test/data/webui/extensions/extension_options_dialog_test.js b/chrome/test/data/webui/extensions/extension_options_dialog_test.js new file mode 100644 index 0000000..dc4ff32 --- /dev/null +++ b/chrome/test/data/webui/extensions/extension_options_dialog_test.js
@@ -0,0 +1,46 @@ +// Copyright 2019 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 Suite of tests for extension options dialog. + * These are run as part of interactive_ui_tests. + */ +suite('ExtensionOptionsDialogTest', function() { + /** @type {extensions.Manager} */ + let manager; + + setup(function() { + manager = document.querySelector('extensions-manager'); + }); + + function getOptionsDialog() { + const dialog = manager.$$('#options-dialog'); + assertTrue(!!dialog); + return dialog; + } + + test('show options dialog', function() { + const extensionDetailView = manager.$$('extensions-detail-view'); + assertTrue(!!extensionDetailView); + + const optionsButton = extensionDetailView.$$('#extensions-options'); + + // Click the options button. + optionsButton.click(); + + // Wait for dialog to open. + return test_util.eventToPromise('cr-dialog-open', manager) + .then(() => { + const dialog = getOptionsDialog(); + let waitForClose = test_util.eventToPromise('close', dialog); + // Close dialog and wait. + dialog.$.dialog.cancel(); + return waitForClose; + }) + .then(() => { + // Validate that this button is focused after dialog closes. + assertEquals(optionsButton.$$('button'), getDeepActiveElement()); + }); + }); +});
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index a31f82d..e8b64862 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1345,7 +1345,8 @@ ]), }; -TEST_F('CrSettingsSiteListTest', 'SiteList', function() { +// TODO(crbug.com/929455): flaky, fix. +TEST_F('CrSettingsSiteListTest', 'DISABLED_SiteList', function() { mocha.grep('SiteList').run(); });
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index dfaa0fb..d328928 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -71,7 +71,7 @@ // Enable or disable native controls in video player on Chrome OS. const base::Feature kVideoPlayerNativeControls{ - "VideoPlayerNativeControls", base::FEATURE_DISABLED_BY_DEFAULT}; + "VideoPlayerNativeControls", base::FEATURE_ENABLED_BY_DEFAULT}; // Use the messages.google.com domain as part of the "Messages" feature under // "Connected Devices" settings.
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn index 30ba4f9..10481c60 100644 --- a/components/crash/content/app/BUILD.gn +++ b/components/crash/content/app/BUILD.gn
@@ -198,6 +198,7 @@ ] deps = [ + "//components/gwp_asan/crash_handler", "//third_party/crashpad/crashpad/handler:handler", ]
diff --git a/components/crash/content/app/chrome_crashpad_handler.cc b/components/crash/content/app/chrome_crashpad_handler.cc index ac121e2..24a8388 100644 --- a/components/crash/content/app/chrome_crashpad_handler.cc +++ b/components/crash/content/app/chrome_crashpad_handler.cc
@@ -2,8 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> + +#include "components/gwp_asan/crash_handler/crash_handler.h" #include "third_party/crashpad/crashpad/handler/handler_main.h" +#include "third_party/crashpad/crashpad/handler/user_stream_data_source.h" int main(int argc, char* argv[]) { - return crashpad::HandlerMain(argc, argv, nullptr); + crashpad::UserStreamDataSources user_stream_data_sources; + user_stream_data_sources.push_back( + std::make_unique<gwp_asan::UserStreamDataSource>()); + + return crashpad::HandlerMain(argc, argv, &user_stream_data_sources); }
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc index fc34a85f..7d6582ca 100644 --- a/components/exo/shell_surface_base.cc +++ b/components/exo/shell_surface_base.cc
@@ -1108,7 +1108,7 @@ // Prevent window from being activated when hit test region is empty. bool activatable = activatable_ && HasHitTestRegion(); if (activatable != CanActivate()) { - set_can_activate(activatable); + SetCanActivate(activatable); // Activate or deactivate window if activation state changed. if (activatable) { // Automatically activate only if the window is modal.
diff --git a/components/exo/text_input.cc b/components/exo/text_input.cc index f2534c4c..02fb7dcd 100644 --- a/components/exo/text_input.cc +++ b/components/exo/text_input.cc
@@ -4,6 +4,9 @@ #include "components/exo/text_input.h" +#include <algorithm> + +#include "base/strings/utf_string_conversions.h" #include "components/exo/surface.h" #include "components/exo/wm_helper.h" #include "third_party/icu/source/common/unicode/uchar.h" @@ -25,6 +28,14 @@ } // namespace +size_t OffsetFromUTF8Offset(const base::StringPiece& text, uint32_t offset) { + return base::UTF8ToUTF16(text.substr(0, offset)).size(); +} + +size_t OffsetFromUTF16Offset(const base::StringPiece16& text, uint32_t offset) { + return base::UTF16ToUTF8(text.substr(0, offset)).size(); +} + TextInput::TextInput(std::unique_ptr<Delegate> delegate) : delegate_(std::move(delegate)) {} @@ -69,8 +80,14 @@ } void TextInput::SetSurroundingText(const base::string16& text, - uint32_t cursor_pos) { - NOTIMPLEMENTED(); + uint32_t cursor_pos, + uint32_t anchor) { + surrounding_text_ = text; + cursor_pos_ = gfx::Range(cursor_pos); + if (anchor < cursor_pos) + cursor_pos_->set_start(anchor); + else + cursor_pos_->set_end(anchor); } void TextInput::SetTypeModeFlags(ui::TextInputType type, @@ -163,37 +180,87 @@ } bool TextInput::GetTextRange(gfx::Range* range) const { - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (!cursor_pos_) + return false; + range->set_start(0); + if (composition_.text.empty()) { + range->set_end(surrounding_text_.size()); + } else { + range->set_end(surrounding_text_.size() - cursor_pos_->length() + + composition_.text.size()); + } + return true; } bool TextInput::GetCompositionTextRange(gfx::Range* range) const { - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (!cursor_pos_ || composition_.text.empty()) + return false; + + range->set_start(cursor_pos_->start()); + range->set_end(cursor_pos_->start() + composition_.text.size()); + return true; } bool TextInput::GetEditableSelectionRange(gfx::Range* range) const { - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (!cursor_pos_) + return false; + range->set_start(cursor_pos_->start()); + range->set_end(cursor_pos_->end()); + return true; } bool TextInput::SetEditableSelectionRange(const gfx::Range& range) { - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (surrounding_text_.size() < range.GetMax()) + return false; + delegate_->SetCursor( + gfx::Range(OffsetFromUTF16Offset(surrounding_text_, range.start()), + OffsetFromUTF16Offset(surrounding_text_, range.end()))); + return true; } bool TextInput::DeleteRange(const gfx::Range& range) { - // TODO(mukai): call delegate_->DeleteSurroundingText(range) once it's - // supported. - NOTIMPLEMENTED_LOG_ONCE(); - return false; + if (surrounding_text_.size() < range.GetMax()) + return false; + delegate_->DeleteSurroundingText( + gfx::Range(OffsetFromUTF16Offset(surrounding_text_, range.start()), + OffsetFromUTF16Offset(surrounding_text_, range.end()))); + return true; } bool TextInput::GetTextFromRange(const gfx::Range& range, base::string16* text) const { - // TODO(mukai): support of surrounding text. - NOTIMPLEMENTED_LOG_ONCE(); - return false; + gfx::Range text_range; + if (!GetTextRange(&text_range) || !text_range.Contains(range)) + return false; + if (composition_.text.empty() || range.GetMax() <= cursor_pos_->GetMin()) { + text->assign(surrounding_text_, range.GetMin(), range.length()); + return true; + } + size_t composition_end = cursor_pos_->GetMin() + composition_.text.size(); + if (range.GetMin() >= composition_end) { + size_t start = + range.GetMin() - composition_.text.size() + cursor_pos_->length(); + text->assign(surrounding_text_, start, range.length()); + return true; + } + + size_t start_in_composition = 0; + if (range.GetMin() <= cursor_pos_->GetMin()) { + text->assign(surrounding_text_, range.GetMin(), + cursor_pos_->GetMin() - range.GetMin()); + } else { + start_in_composition = range.GetMin() - cursor_pos_->GetMin(); + } + if (range.GetMax() <= composition_end) { + text->append(composition_.text, start_in_composition, + range.GetMax() - cursor_pos_->GetMin() - start_in_composition); + } else { + text->append(composition_.text, start_in_composition, + composition_.text.size() - start_in_composition); + text->append(surrounding_text_, cursor_pos_->GetMax(), + range.GetMax() - composition_end); + } + return true; } void TextInput::OnInputMethodChanged() { @@ -214,7 +281,17 @@ return true; } -void TextInput::ExtendSelectionAndDelete(size_t before, size_t after) {} +void TextInput::ExtendSelectionAndDelete(size_t before, size_t after) { + if (!cursor_pos_) + return; + uint32_t start = + (cursor_pos_->GetMin() < before) ? 0 : (cursor_pos_->GetMin() - before); + uint32_t end = + std::min(cursor_pos_->GetMax() + after, surrounding_text_.size()); + delegate_->DeleteSurroundingText( + gfx::Range(OffsetFromUTF16Offset(surrounding_text_, start), + OffsetFromUTF16Offset(surrounding_text_, end))); +} void TextInput::EnsureCaretNotInRect(const gfx::Rect& rect) {}
diff --git a/components/exo/text_input.h b/components/exo/text_input.h index 4a47440..b00873a 100644 --- a/components/exo/text_input.h +++ b/components/exo/text_input.h
@@ -23,6 +23,9 @@ namespace exo { class Surface; +size_t OffsetFromUTF8Offset(const base::StringPiece& text, uint32_t offset); +size_t OffsetFromUTF16Offset(const base::StringPiece16& text, uint32_t offset); + // This class bridges the ChromeOS input method and a text-input context. class TextInput : public ui::TextInputClient, public keyboard::KeyboardControllerObserver { @@ -47,10 +50,11 @@ // Commit |text| to the current text input session. virtual void Commit(const base::string16& text) = 0; - // Set the cursor position. + // Set the cursor position. The range should be in bytes offset. virtual void SetCursor(const gfx::Range& selection) = 0; - // Delete the surrounding text of the current text input. + // Delete the surrounding text of the current text input. The range should + // be in the bytes offset. virtual void DeleteSurroundingText(const gfx::Range& range) = 0; // Sends a key event. @@ -83,7 +87,9 @@ void Resync(); // Sets the surrounding text in the app. - void SetSurroundingText(const base::string16& text, uint32_t cursor_pos); + void SetSurroundingText(const base::string16& text, + uint32_t cursor_pos, + uint32_t anchor); // Sets the text input type, mode, flags, and |should_do_learning|. void SetTypeModeFlags(ui::TextInputType type, @@ -148,6 +154,8 @@ int flags_ = ui::TEXT_INPUT_FLAG_NONE; bool should_do_learning_ = true; ui::CompositionText composition_; + base::string16 surrounding_text_; + base::Optional<gfx::Range> cursor_pos_; base::i18n::TextDirection direction_ = base::i18n::UNKNOWN_DIRECTION; DISALLOW_COPY_AND_ASSIGN(TextInput);
diff --git a/components/exo/text_input_unittest.cc b/components/exo/text_input_unittest.cc index 2aa5e1a6..3dc0ed5 100644 --- a/components/exo/text_input_unittest.cc +++ b/components/exo/text_input_unittest.cc
@@ -4,6 +4,8 @@ #include "components/exo/text_input.h" +#include <string> + #include "base/strings/utf_string_conversions.h" #include "components/exo/buffer.h" #include "components/exo/shell_surface.h" @@ -117,6 +119,16 @@ return surface_->window()->GetHost()->GetInputMethod(); } + void SetCompositionText(const std::string& utf8) { + ui::CompositionText t; + t.text = base::UTF8ToUTF16(utf8); + t.selection = gfx::Range(1u); + t.ime_text_spans.push_back( + ui::ImeTextSpan(0, t.text.size(), ui::ImeTextSpan::Thickness::kThick)); + EXPECT_CALL(*delegate(), SetCompositionText(t)).Times(1); + text_input()->SetCompositionText(t); + } + private: std::unique_ptr<TextInput> text_input_; @@ -224,31 +236,17 @@ } TEST_F(TextInputTest, CompositionText) { - ui::CompositionText t; - t.text = base::ASCIIToUTF16("composition"); - t.selection = gfx::Range(1u); - t.ime_text_spans.push_back( - ui::ImeTextSpan(0, t.text.size(), ui::ImeTextSpan::Thickness::kThick)); + SetCompositionText("composition"); ui::CompositionText empty; - EXPECT_CALL(*delegate(), SetCompositionText(t)).Times(1); EXPECT_CALL(*delegate(), SetCompositionText(empty)).Times(1); - - text_input()->SetCompositionText(t); text_input()->ClearCompositionText(); } TEST_F(TextInputTest, CommitCompositionText) { - ui::CompositionText t; - t.text = base::ASCIIToUTF16("composition"); - t.selection = gfx::Range(1u); - t.ime_text_spans.push_back( - ui::ImeTextSpan(0, t.text.size(), ui::ImeTextSpan::Thickness::kThick)); + SetCompositionText("composition"); - EXPECT_CALL(*delegate(), SetCompositionText(t)).Times(1); - EXPECT_CALL(*delegate(), Commit(t.text)).Times(1); - - text_input()->SetCompositionText(t); + EXPECT_CALL(*delegate(), Commit(base::UTF8ToUTF16("composition"))).Times(1); text_input()->ConfirmCompositionText(); } @@ -275,5 +273,66 @@ text_input()->InsertChar(ev); } +TEST_F(TextInputTest, SurroundingText) { + gfx::Range range; + EXPECT_FALSE(text_input()->GetTextRange(&range)); + EXPECT_FALSE(text_input()->GetCompositionTextRange(&range)); + EXPECT_FALSE(text_input()->GetEditableSelectionRange(&range)); + base::string16 got_text; + EXPECT_FALSE(text_input()->GetTextFromRange(gfx::Range(0, 1), &got_text)); + + base::string16 text = base::UTF8ToUTF16("surrounding\xE3\x80\x80text"); + text_input()->SetSurroundingText(text, 11, 12); + + EXPECT_TRUE(text_input()->GetTextRange(&range)); + EXPECT_EQ(gfx::Range(0, text.size()).ToString(), range.ToString()); + + EXPECT_FALSE(text_input()->GetCompositionTextRange(&range)); + EXPECT_TRUE(text_input()->GetEditableSelectionRange(&range)); + EXPECT_EQ(gfx::Range(11, 12).ToString(), range.ToString()); + EXPECT_TRUE(text_input()->GetTextFromRange(gfx::Range(11, 12), &got_text)); + EXPECT_EQ(text.substr(11, 1), got_text); + + // DeleteSurroundingText receives the range in UTF8 -- so (11, 14) range is + // expected. + EXPECT_CALL(*delegate(), DeleteSurroundingText(gfx::Range(11, 14))).Times(1); + text_input()->ExtendSelectionAndDelete(0, 0); + + size_t composition_size = std::string("composition").size(); + SetCompositionText("composition"); + EXPECT_TRUE(text_input()->GetCompositionTextRange(&range)); + EXPECT_EQ(gfx::Range(11, 11 + composition_size).ToString(), range.ToString()); + EXPECT_TRUE(text_input()->GetTextRange(&range)); + EXPECT_EQ(gfx::Range(0, text.size() - 1 + composition_size).ToString(), + range.ToString()); + EXPECT_TRUE(text_input()->GetEditableSelectionRange(&range)); + EXPECT_EQ(gfx::Range(11, 12).ToString(), range.ToString()); +} + +TEST_F(TextInputTest, GetTextRange) { + base::string16 text = base::UTF8ToUTF16("surrounding text"); + text_input()->SetSurroundingText(text, 11, 12); + + SetCompositionText("composition"); + + const struct { + gfx::Range range; + std::string expected; + } kTestCases[] = { + {gfx::Range(0, 3), "sur"}, + {gfx::Range(10, 13), "gco"}, + {gfx::Range(10, 23), "gcompositiont"}, + {gfx::Range(12, 15), "omp"}, + {gfx::Range(12, 23), "ompositiont"}, + {gfx::Range(22, 25), "tex"}, + }; + for (auto& c : kTestCases) { + base::string16 result; + EXPECT_TRUE(text_input()->GetTextFromRange(c.range, &result)) + << c.range.ToString(); + EXPECT_EQ(base::UTF8ToUTF16(c.expected), result) << c.range.ToString(); + } +} + } // anonymous namespace } // namespace exo
diff --git a/components/exo/wayland/zwp_text_input_manager.cc b/components/exo/wayland/zwp_text_input_manager.cc index 15ebfff..73ae15f 100644 --- a/components/exo/wayland/zwp_text_input_manager.cc +++ b/components/exo/wayland/zwp_text_input_manager.cc
@@ -25,14 +25,6 @@ //////////////////////////////////////////////////////////////////////////////// // text_input_v1 interface: -size_t OffsetFromUTF8Offset(const base::StringPiece& text, uint32_t offset) { - return base::UTF8ToUTF16(text.substr(0, offset)).size(); -} - -size_t OffsetFromUTF16Offset(const base::StringPiece16& text, uint32_t offset) { - return base::UTF16ToUTF8(text.substr(0, offset)).size(); -} - class WaylandTextInputDelegate : public TextInput::Delegate { public: WaylandTextInputDelegate(wl_resource* text_input) : text_input_(text_input) {} @@ -107,15 +99,13 @@ } void SetCursor(const gfx::Range& selection) override { - // TODO(mukai): compute the utf8 offset for |selection| and call - // zwp_text_input_v1_send_cursor_position. - NOTIMPLEMENTED(); + zwp_text_input_v1_send_cursor_position(text_input_, selection.end(), + selection.start()); } void DeleteSurroundingText(const gfx::Range& range) override { - // TODO(mukai): compute the utf8 offset for |range| and call - // zwp_text_input_send_delete_surrounding_text. - NOTIMPLEMENTED(); + zwp_text_input_v1_send_delete_surrounding_text(text_input_, range.start(), + range.length()); } void SendKey(const ui::KeyEvent& event) override { @@ -216,7 +206,8 @@ uint32_t anchor) { TextInput* text_input = GetUserDataAs<TextInput>(resource); text_input->SetSurroundingText(base::UTF8ToUTF16(text), - OffsetFromUTF8Offset(text, cursor)); + OffsetFromUTF8Offset(text, cursor), + OffsetFromUTF8Offset(text, anchor)); } void text_input_set_content_type(wl_client* client,
diff --git a/components/feed/core/feed_content_database.cc b/components/feed/core/feed_content_database.cc index 7e06660..a9b760a 100644 --- a/components/feed/core/feed_content_database.cc +++ b/components/feed/core/feed_content_database.cc
@@ -36,6 +36,12 @@ const size_t kDatabaseWriteBufferSizeBytes = 64 * 1024; // 64KB const size_t kDatabaseWriteBufferSizeBytesForLowEndDevice = 32 * 1024; // 32KB +leveldb::ReadOptions CreateReadOptions() { + leveldb::ReadOptions opts; + opts.fill_cache = false; + return opts; +} + bool DatabaseKeyFilter(const std::unordered_set<std::string>& key_set, const std::string& key) { return key_set.find(key) != key_set.end(); @@ -97,6 +103,7 @@ storage_database_->LoadEntriesWithFilter( base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)), + CreateReadOptions(), /* target_prefix */ "", base::BindOnce(&FeedContentDatabase::OnLoadEntriesForLoadContent, weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(), std::move(callback))); @@ -108,6 +115,7 @@ storage_database_->LoadEntriesWithFilter( base::BindRepeating(&DatabasePrefixFilter, std::move(prefix)), + CreateReadOptions(), /* target_prefix */ "", base::BindOnce(&FeedContentDatabase::OnLoadEntriesForLoadContent, weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(), std::move(callback)));
diff --git a/components/feed/core/feed_networking_host.cc b/components/feed/core/feed_networking_host.cc index 6bc4c4a..bf723ff6 100644 --- a/components/feed/core/feed_networking_host.cc +++ b/components/feed/core/feed_networking_host.cc
@@ -85,7 +85,13 @@ network::SharedURLLoaderFactory* loader_factory_; const std::string api_key_; const base::TickClock* tick_clock_; - base::TimeTicks start_ticks_; + + // Set when the NetworkFetch is constructed, before token and article fetch. + const base::TimeTicks entire_send_start_ticks_; + + // Should be set right before the article fetch, and after the token fetch if + // there is one. + base::TimeTicks loader_only_start_ticks_; DISALLOW_COPY_AND_ASSIGN(NetworkFetch); }; @@ -104,7 +110,7 @@ loader_factory_(loader_factory), api_key_(api_key), tick_clock_(tick_clock), - start_ticks_(tick_clock_->NowTicks()) {} + entire_send_start_ticks_(tick_clock_->NowTicks()) {} void NetworkFetch::Start(FeedNetworkingHost::ResponseCallback done_callback) { done_callback_ = std::move(done_callback); @@ -138,6 +144,7 @@ } void NetworkFetch::StartLoader() { + loader_only_start_ticks_ = tick_clock_->NowTicks(); simple_loader_ = MakeLoader(); simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie( loader_factory_, base::BindOnce(&NetworkFetch::OnSimpleLoaderComplete, @@ -250,9 +257,16 @@ response_body.assign(begin, end); } - base::TimeDelta duration = tick_clock_->NowTicks() - start_ticks_; + base::TimeDelta entire_send_duration = + tick_clock_->NowTicks() - entire_send_start_ticks_; UMA_HISTOGRAM_MEDIUM_TIMES("ContentSuggestions.Feed.Network.Duration", - duration); + entire_send_duration); + + base::TimeDelta loader_only_duration = + tick_clock_->NowTicks() - loader_only_start_ticks_; + // This histogram purposefully matches name and bucket size used in + // RemoteSuggestionsFetcherImpl. + UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", loader_only_duration); base::UmaHistogramSparse("ContentSuggestions.Feed.Network.RequestStatusCode", status_code);
diff --git a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc index db0d293..d9f438f 100644 --- a/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc +++ b/components/image_fetcher/core/cache/image_metadata_store_leveldb.cc
@@ -23,6 +23,12 @@ namespace { +leveldb::ReadOptions CreateReadOptions() { + leveldb::ReadOptions opts; + opts.fill_cache = false; + return opts; +} + int64_t ToDatabaseTime(base::Time time) { return time.since_origin().InMicroseconds(); } @@ -143,7 +149,8 @@ } database_->LoadEntriesWithFilter( - base::BindRepeating(&KeyMatcherFilter, key), + base::BindRepeating(&KeyMatcherFilter, key), CreateReadOptions(), + /* target_prefix */ "", base::BindOnce(&ImageMetadataStoreLevelDB::UpdateImageMetadataImpl, weak_ptr_factory_.GetWeakPtr())); }
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc index 9e99733..b0b6206 100644 --- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc +++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.cc
@@ -61,16 +61,6 @@ GLOutputSurfaceBufferQueueAndroid::~GLOutputSurfaceBufferQueueAndroid() = default; -void GLOutputSurfaceBufferQueueAndroid::HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback) { - DCHECK(sub_buffer_rect.IsEmpty()); - context_provider_->ContextSupport()->CommitOverlayPlanes( - flags, std::move(swap_callback), std::move(presentation_callback)); -} - OverlayCandidateValidator* GLOutputSurfaceBufferQueueAndroid::GetOverlayCandidateValidator() const { return overlay_candidate_validator_.get();
diff --git a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h index 822a78d..134006f 100644 --- a/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h +++ b/components/viz/service/display_embedder/gl_output_surface_buffer_queue_android.h
@@ -21,11 +21,6 @@ ~GLOutputSurfaceBufferQueueAndroid() override; // GLOutputSurfaceBufferQueue implementation: - void HandlePartialSwap( - const gfx::Rect& sub_buffer_rect, - uint32_t flags, - gpu::ContextSupport::SwapCompletedCallback swap_callback, - gpu::ContextSupport::PresentationCallback presentation_callback) override; OverlayCandidateValidator* GetOverlayCandidateValidator() const override; private:
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc index 1183e151..d1d073cf 100644 --- a/content/browser/accessibility/browser_accessibility_com_win.cc +++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1799,16 +1799,6 @@ FireNativeEvent(IA2_EVENT_TEXT_INSERTED); } } - - // Changing a static text node can affect the IA2 hypertext of its parent - // and, if the node is in a simple text control, the hypertext of the text - // control itself. - BrowserAccessibilityComWin* parent = - ToBrowserAccessibilityComWin(owner()->PlatformGetParent()); - if (parent && (parent->owner()->HasState(ax::mojom::State::kEditable) || - owner()->IsTextOnlyObject())) { - parent->owner()->UpdatePlatformAttributes(); - } } old_win_attributes_.reset(nullptr);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index 5162eaacb..0116955 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -390,7 +390,7 @@ // Fire the native event. BrowserAccessibility* event_target = GetFromID(event.id); if (!event_target || !event_target->CanFireEvents()) - return; + continue; if (event.event_type == ax::mojom::Event::kHover) GetRootManager()->CacheHitTestResult(event_target);
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc index 3b6ddfe..75d206d 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -15,6 +15,7 @@ #include "content/browser/accessibility/browser_accessibility_win.h" #include "content/browser/renderer_host/legacy_render_widget_host_win.h" #include "content/common/accessibility_messages.h" +#include "ui/accessibility/ax_role_properties.h" #include "ui/base/win/atl_module.h" namespace content { @@ -253,29 +254,63 @@ // Do a sequence of Windows-specific updates on each node. Each one is // done in a single pass that must complete before the next step starts. - // The first step moves win_attributes_ to old_win_attributes_ and then - // recomputes all of win_attributes_ other than IAccessibleText. + // The nodes that need to be updated are all of the nodes that were changed, + // plus some parents. + std::map<BrowserAccessibilityComWin*, bool /* is_subtree_created */> + objs_to_update; for (const auto& change : changes) { const ui::AXNode* changed_node = change.node; DCHECK(changed_node); + + bool is_subtree_created = change.type == AXTreeObserver::SUBTREE_CREATED; BrowserAccessibility* obj = GetFromAXNode(changed_node); if (obj && obj->IsNative()) { - ToBrowserAccessibilityWin(obj) - ->GetCOM() - ->UpdateStep1ComputeWinAttributes(); + objs_to_update[ToBrowserAccessibilityWin(obj)->GetCOM()] = + is_subtree_created; + } + + // When a node is a text node or line break, update its parent, because + // its text is part of its hypertext. + const ui::AXNode* parent = changed_node->parent(); + if (!parent) + continue; + if (ui::IsTextOrLineBreak(changed_node->data().role)) { + BrowserAccessibility* parent_obj = GetFromAXNode(parent); + if (parent_obj && parent_obj->IsNative()) { + BrowserAccessibilityComWin* parent_com_obj = + ToBrowserAccessibilityWin(parent_obj)->GetCOM(); + if (objs_to_update.find(parent_com_obj) == objs_to_update.end()) + objs_to_update[parent_com_obj] = false; + } + } + + // When a node is editable, update the editable root too. + if (!changed_node->data().HasState(ax::mojom::State::kEditable)) + continue; + const ui::AXNode* editable_root = changed_node; + while (editable_root->parent() && editable_root->parent()->data().HasState( + ax::mojom::State::kEditable)) { + editable_root = editable_root->parent(); + } + BrowserAccessibility* editable_root_obj = GetFromAXNode(editable_root); + if (editable_root_obj && editable_root_obj->IsNative()) { + BrowserAccessibilityComWin* editable_root_com_obj = + ToBrowserAccessibilityWin(editable_root_obj)->GetCOM(); + if (objs_to_update.find(editable_root_com_obj) == objs_to_update.end()) + objs_to_update[editable_root_com_obj] = false; } } + // The first step moves win_attributes_ to old_win_attributes_ and then + // recomputes all of win_attributes_ other than IAccessibleText. + for (auto& key_value : objs_to_update) + key_value.first->UpdateStep1ComputeWinAttributes(); + // The next step updates the hypertext of each node, which is a // concatenation of all of its child text nodes, so it can't run until // the text of all of the nodes was computed in the previous step. - for (const auto& change : changes) { - const ui::AXNode* changed_node = change.node; - DCHECK(changed_node); - BrowserAccessibility* obj = GetFromAXNode(changed_node); - if (obj && obj->IsNative()) - ToBrowserAccessibilityWin(obj)->GetCOM()->UpdateStep2ComputeHypertext(); - } + for (auto& key_value : objs_to_update) + key_value.first->UpdateStep2ComputeHypertext(); // The third step fires events on nodes based on what's changed - like // if the name, value, or description changed, or if the hypertext had @@ -285,14 +320,10 @@ // client may walk the tree when it receives any of these events. // At the end, it deletes old_win_attributes_ since they're not needed // anymore. - for (const auto& change : changes) { - const ui::AXNode* changed_node = change.node; - DCHECK(changed_node); - BrowserAccessibility* obj = GetFromAXNode(changed_node); - if (obj && obj->IsNative()) { - ToBrowserAccessibilityWin(obj)->GetCOM()->UpdateStep3FireEvents( - change.type == AXTreeObserver::SUBTREE_CREATED); - } + for (auto& key_value : objs_to_update) { + BrowserAccessibilityComWin* obj = key_value.first; + bool is_subtree_created = key_value.second; + obj->UpdateStep3FireEvents(is_subtree_created); } }
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc index 2e76caa..3eaa0a94 100644 --- a/content/browser/appcache/appcache_update_job.cc +++ b/content/browser/appcache/appcache_update_job.cc
@@ -729,12 +729,18 @@ const char kFormatString[] = "Manifest re-fetch failed (%d) %s"; std::string message = FormatUrlErrorMessage( kFormatString, manifest_url_, fetcher->result(), response_code); + ResultType result = fetcher->result(); + if (result == UPDATE_OK) { + // URLFetcher considers any 2xx response a success, however in this + // particular case we want to treat any non 200 responses as failures. + result = SERVER_ERROR; + } HandleCacheFailure( blink::mojom::AppCacheErrorDetails( message, blink::mojom::AppCacheErrorReason::APPCACHE_MANIFEST_ERROR, GURL(), response_code, false /*is_cross_origin*/), - fetcher->result(), GURL()); + result, GURL()); } } }
diff --git a/content/browser/find_request_manager.cc b/content/browser/find_request_manager.cc index 12e9039..5917984 100644 --- a/content/browser/find_request_manager.cc +++ b/content/browser/find_request_manager.cc
@@ -668,12 +668,22 @@ bool matches_only, bool wrap) const { DCHECK(from_rfh); + // If |from_rfh| is being detached, it might already be removed from + // its parent's list of children, meaning we can't traverse it correctly. + if (!static_cast<RenderFrameHostImpl*>(from_rfh)->is_active()) + return nullptr; FrameTreeNode* node = static_cast<RenderFrameHostImpl*>(from_rfh)->frame_tree_node(); - + FrameTreeNode* last_node = node; while ((node = TraverseNode(node, forward, wrap)) != nullptr) { - if (!CheckFrame(node->current_frame_host())) + if (!CheckFrame(node->current_frame_host())) { + // If we're in the same frame as before, we might got into an infinite + // loop. + if (last_node == node) + break; + last_node = node; continue; + } RenderFrameHost* current_rfh = node->current_frame_host(); if (!matches_only || find_in_page_clients_.find(current_rfh)->second->number_of_matches() ||
diff --git a/content/browser/find_request_manager_browsertest.cc b/content/browser/find_request_manager_browsertest.cc index 26a1fb9..b8f22c30 100644 --- a/content/browser/find_request_manager_browsertest.cc +++ b/content/browser/find_request_manager_browsertest.cc
@@ -594,6 +594,32 @@ } } +IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, DetachFrameWithMatch) { + // Detaching an iframe with matches when the main document doesn't + // have matches should work and just remove the matches from the + // removed frame. + LoadAndWait("/find_in_page_two_frames.html"); + auto options = blink::mojom::FindOptions::New(); + options->run_synchronously_for_testing = true; + + Find("result", options.Clone()); + delegate()->WaitForFinalReply(); + FindResults results = delegate()->GetFindResults(); + EXPECT_EQ(last_request_id(), results.request_id); + EXPECT_EQ(6, results.number_of_matches); + EXPECT_EQ(1, results.active_match_ordinal); + EXPECT_TRUE(ExecuteScript(shell(), + "document.body.removeChild(" + "document.querySelectorAll('iframe')[0])")); + + Find("result", options.Clone()); + delegate()->WaitForFinalReply(); + results = delegate()->GetFindResults(); + EXPECT_EQ(last_request_id(), results.request_id); + EXPECT_EQ(3, results.number_of_matches); + EXPECT_EQ(1, results.active_match_ordinal); +} + IN_PROC_BROWSER_TEST_F(FindRequestManagerTest, MAYBE(FindInPage_Issue644448)) { TestNavigationObserver navigation_observer(contents()); NavigateToURL(shell(), GURL("about:blank"));
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index ba9cdb72..6837e87 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3024,6 +3024,7 @@ void RenderFrameHostImpl::VisibilityChanged( blink::mojom::FrameVisibility visibility) { visibility_ = visibility; + UpdateFrameFrozenState(); } void RenderFrameHostImpl::OnDidBlockFramebust(const GURL& url) { @@ -3384,6 +3385,8 @@ // of this RenderFrameHost is being tracked. if (is_active()) frame_tree_node_->DidStopLoading(); + + UpdateFrameFrozenState(); } void RenderFrameHostImpl::OnDidChangeLoadProgress(double load_progress) { @@ -6575,4 +6578,27 @@ base::Unretained(this), navigation_request); } +void RenderFrameHostImpl::UpdateFrameFrozenState() { + if (!base::FeatureList::IsEnabled(features::kFreezeFramesOnVisibility)) + return; + + // If the document is in the loading state keep it still loading. + if (is_loading_) + return; + + // TODO(dtapuska): Adjust these based on feature policies when + // they are available. + // Feature policies don't support parameterized values yet. + // crbug.com/924568, crbug.com/907125 + if (visibility_ == blink::mojom::FrameVisibility::kNotRendered) { + frame_->SetLifecycleState(blink::mojom::FrameLifecycleState::kFrozen); + } else if (visibility_ == + blink::mojom::FrameVisibility::kRenderedOutOfViewport) { + frame_->SetLifecycleState( + blink::mojom::FrameLifecycleState::kFrozenAutoResumeMedia); + } else { + frame_->SetLifecycleState(blink::mojom::FrameLifecycleState::kRunning); + } +} + } // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 9d1335e..535cff9 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1496,6 +1496,10 @@ // - Speculative RenderFrameHost. void ResetNavigationsForPendingDeletion(); + // Update the frozen state of the frame applying current inputs (visibility, + // loaded state) to determine the new state. + void UpdateFrameFrozenState(); + // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a // refcount that calls Shutdown when it reaches zero. This allows each // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc index ecec626..af9310c 100644 --- a/content/browser/gpu/browser_gpu_channel_host_factory.cc +++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -129,7 +129,8 @@ void BrowserGpuChannelHostFactory::EstablishRequest::RestartTimeout() { BrowserGpuChannelHostFactory* factory = BrowserGpuChannelHostFactory::instance(); - factory->RestartTimeout(); + if (factory) + factory->RestartTimeout(); } void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
diff --git a/content/browser/plugin_list.cc b/content/browser/plugin_list.cc index a1e2bd9..29061b8 100644 --- a/content/browser/plugin_list.cc +++ b/content/browser/plugin_list.cc
@@ -37,12 +37,10 @@ if (mime_type.empty()) return false; - for (size_t i = 0; i < plugin.mime_types.size(); ++i) { - const WebPluginMimeType& mime_info = plugin.mime_types[i]; + for (const WebPluginMimeType& mime_info : plugin.mime_types) { if (net::MatchesMimeType(mime_info.mime_type, mime_type)) { - if (!allow_wildcard && mime_info.mime_type == "*") - continue; - return true; + if (allow_wildcard || mime_info.mime_type != "*") + return true; } } return false; @@ -55,10 +53,9 @@ bool SupportsExtension(const WebPluginInfo& plugin, const std::string& extension, std::string* actual_mime_type) { - for (size_t i = 0; i < plugin.mime_types.size(); ++i) { - const WebPluginMimeType& mime_type = plugin.mime_types[i]; - for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) { - if (mime_type.file_extensions[j] == extension) { + for (const WebPluginMimeType& mime_type : plugin.mime_types) { + for (const std::string& file_extension : mime_type.file_extensions) { + if (file_extension == extension) { *actual_mime_type = mime_type.mime_type; return true; } @@ -111,10 +108,8 @@ std::vector<WebPluginInfo>* internal_plugins) { base::AutoLock lock(lock_); - for (std::vector<WebPluginInfo>::iterator it = internal_plugins_.begin(); - it != internal_plugins_.end(); ++it) { - internal_plugins->push_back(*it); - } + for (const auto& plugin : internal_plugins_) + internal_plugins->push_back(plugin); } bool PluginList::ReadPluginInfo(const base::FilePath& filename, @@ -145,21 +140,20 @@ return; std::vector<WebPluginInfo> new_plugins; - base::Closure will_load_callback; + base::OnceClosure will_load_callback; { base::AutoLock lock(lock_); will_load_callback = will_load_plugins_callback_; } - if (!will_load_callback.is_null()) + if (will_load_callback) std::move(will_load_callback).Run(); std::vector<base::FilePath> plugin_paths; GetPluginPathsToLoad(&plugin_paths); - for (std::vector<base::FilePath>::const_iterator it = plugin_paths.begin(); - it != plugin_paths.end(); ++it) { + for (const base::FilePath& path : plugin_paths) { WebPluginInfo plugin_info; - LoadPluginIntoPluginList(*it, &new_plugins, &plugin_info); + LoadPluginIntoPluginList(path, &new_plugins, &plugin_info); } SetPlugins(new_plugins); @@ -172,12 +166,11 @@ return false; // TODO(piman): Do we still need this after NPAPI removal? - for (size_t i = 0; i < plugin_info->mime_types.size(); ++i) { + for (const content::WebPluginMimeType& mime_type : plugin_info->mime_types) { // TODO: don't load global handlers for now. // WebKit hands to the Plugin before it tries // to handle mimeTypes on its own. - const std::string& mime_type = plugin_info->mime_types[i].mime_type; - if (mime_type == "*") + if (mime_type.mime_type == "*") return false; } plugins->push_back(*plugin_info); @@ -194,8 +187,7 @@ extra_plugin_paths = extra_plugin_paths_; } - for (size_t i = 0; i < extra_plugin_paths.size(); ++i) { - const base::FilePath& path = extra_plugin_paths[i]; + for (const base::FilePath& path : extra_plugin_paths) { if (base::ContainsValue(*plugin_paths, path)) continue; plugin_paths->push_back(path); @@ -213,7 +205,8 @@ plugins_list_ = plugins; } -void PluginList::set_will_load_plugins_callback(const base::Closure& callback) { +void PluginList::set_will_load_plugins_callback( + const base::RepeatingClosure& callback) { base::AutoLock lock(lock_); will_load_plugins_callback_ = callback; } @@ -253,11 +246,11 @@ std::set<base::FilePath> visited_plugins; // Add in plugins by mime type. - for (size_t i = 0; i < plugins_list_.size(); ++i) { - if (SupportsType(plugins_list_[i], mime_type, allow_wildcard)) { - base::FilePath path = plugins_list_[i].path; + for (const WebPluginInfo& plugin : plugins_list_) { + if (SupportsType(plugin, mime_type, allow_wildcard)) { + const base::FilePath& path = plugin.path; if (visited_plugins.insert(path).second) { - info->push_back(plugins_list_[i]); + info->push_back(plugin); if (actual_mime_types) actual_mime_types->push_back(mime_type); } @@ -272,18 +265,19 @@ // as when the user doesn't have the Flash plugin enabled. std::string path = url.path(); std::string::size_type last_dot = path.rfind('.'); - if (last_dot != std::string::npos && mime_type.empty()) { - std::string extension = - base::ToLowerASCII(base::StringPiece(path).substr(last_dot + 1)); - std::string actual_mime_type; - for (size_t i = 0; i < plugins_list_.size(); ++i) { - if (SupportsExtension(plugins_list_[i], extension, &actual_mime_type)) { - base::FilePath plugin_path = plugins_list_[i].path; - if (visited_plugins.insert(plugin_path).second) { - info->push_back(plugins_list_[i]); - if (actual_mime_types) - actual_mime_types->push_back(actual_mime_type); - } + if (last_dot == std::string::npos || !mime_type.empty()) + return; + + std::string extension = + base::ToLowerASCII(base::StringPiece(path).substr(last_dot + 1)); + std::string actual_mime_type; + for (const WebPluginInfo& plugin : plugins_list_) { + if (SupportsExtension(plugin, extension, &actual_mime_type)) { + base::FilePath plugin_path = plugin.path; + if (visited_plugins.insert(plugin_path).second) { + info->push_back(plugin); + if (actual_mime_types) + actual_mime_types->push_back(actual_mime_type); } } } @@ -298,6 +292,6 @@ extra_plugin_paths_.erase(it); } -PluginList::~PluginList() {} +PluginList::~PluginList() = default; } // namespace content
diff --git a/content/browser/plugin_list.h b/content/browser/plugin_list.h index 9f4732f..d55fc2d 100644 --- a/content/browser/plugin_list.h +++ b/content/browser/plugin_list.h
@@ -86,7 +86,7 @@ std::vector<WebPluginInfo>* info, std::vector<std::string>* actual_mime_types); - void set_will_load_plugins_callback(const base::Closure& callback); + void set_will_load_plugins_callback(const base::RepeatingClosure& callback); private: enum LoadingState { @@ -155,7 +155,7 @@ std::vector<WebPluginInfo> plugins_list_ GUARDED_BY(lock_); // Callback that is invoked whenever the PluginList will reload the plugins. - base::Closure will_load_plugins_callback_ GUARDED_BY(lock_); + base::RepeatingClosure will_load_plugins_callback_ GUARDED_BY(lock_); // Need synchronization for the above members since this object can be // accessed on multiple threads.
diff --git a/content/browser/plugin_service_impl.cc b/content/browser/plugin_service_impl.cc index a029fbb..74b629f5 100644 --- a/content/browser/plugin_service_impl.cc +++ b/content/browser/plugin_service_impl.cc
@@ -99,8 +99,6 @@ } PluginServiceImpl::PluginServiceImpl() : filter_(nullptr) { - plugin_list_sequence_checker_.DetachFromSequence(); - // Collect the total number of browser processes (which create // PluginServiceImpl objects, to be precise). The number is used to normalize // the number of processes which start at least one NPAPI/PPAPI Flash process. @@ -119,8 +117,11 @@ plugin_list_task_runner_ = base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), base::TaskPriority::USER_VISIBLE, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); - PluginList::Singleton()->set_will_load_plugins_callback( - base::Bind(&WillLoadPluginsCallback, &plugin_list_sequence_checker_)); + + // Setup the sequence checker right after setting up the task runner. + plugin_list_sequence_checker_.DetachFromSequence(); + PluginList::Singleton()->set_will_load_plugins_callback(base::BindRepeating( + &WillLoadPluginsCallback, &plugin_list_sequence_checker_)); RegisterPepperPlugins(); }
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc index 0d3a9e7..4833cb6 100644 --- a/content/browser/renderer_host/compositor_impl_android.cc +++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -1194,6 +1194,7 @@ display_client_->GetBoundPtr(task_runner).PassInterface(); viz::RendererSettings renderer_settings; + renderer_settings.partial_swap_enabled = true; renderer_settings.allow_antialiasing = false; renderer_settings.highp_threshold_min = 2048; renderer_settings.requires_alpha_channel = requires_alpha_channel_;
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc index 5fad7ad..0ab5c78 100644 --- a/content/browser/renderer_host/render_widget_host_input_event_router.cc +++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -950,12 +950,11 @@ void RenderWidgetHostInputEventRouter::ReportBubblingScrollToSameView( const blink::WebGestureEvent& event, const RenderWidgetHostViewBase* view) { -#if 0 - // For now, we've disabled the DumpWithoutCrashing as it's no longer - // providing useful information. - // TODO(828422): Determine useful crash keys and reenable the report. + static auto* device_key = base::debug::AllocateCrashKeyString( + "same-view-bubble-source-device", base::debug::CrashKeySize::Size32); + base::debug::ScopedCrashKeyString device_key_value( + device_key, std::to_string(event.SourceDevice())); base::debug::DumpWithoutCrashing(); -#endif } namespace {
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc index e0ac361..876b7db 100644 --- a/content/browser/service_worker/embedded_worker_instance.cc +++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -645,9 +645,6 @@ EmbeddedWorkerInstance::~EmbeddedWorkerInstance() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(status_ == EmbeddedWorkerStatus::STOPPING || - status_ == EmbeddedWorkerStatus::STOPPED) - << static_cast<int>(status_); devtools_proxy_.reset(); if (registry_->GetWorker(embedded_worker_id_)) registry_->RemoveWorker(process_id(), embedded_worker_id_);
diff --git a/content/browser/service_worker/service_worker_installed_script_loader.cc b/content/browser/service_worker/service_worker_installed_script_loader.cc index f7a4096..d25fe43 100644 --- a/content/browser/service_worker/service_worker_installed_script_loader.cc +++ b/content/browser/service_worker/service_worker_installed_script_loader.cc
@@ -25,10 +25,27 @@ ServiceWorkerInstalledScriptLoader::ServiceWorkerInstalledScriptLoader( uint32_t options, network::mojom::URLLoaderClientPtr client, - std::unique_ptr<ServiceWorkerResponseReader> response_reader) + std::unique_ptr<ServiceWorkerResponseReader> response_reader, + scoped_refptr<ServiceWorkerVersion> + version_for_main_script_http_response_info, + const GURL& request_url) : options_(options), client_(std::move(client)), request_start_(base::TimeTicks::Now()) { + // Normally, the main script info is set by ServiceWorkerNewScriptLoader for + // new service workers and ServiceWorkerInstalledScriptsSender for installed + // service workes. But some embedders might preinstall scripts to the + // ServiceWorkerScriptCacheMap while not setting the ServiceWorkerVersion + // status to INSTALLED, so we can come to here instead of using + // SeviceWorkerInstalledScriptsSender. + // In this case, the main script info would not yet have been set, so set it + // here. + if (request_url == version_for_main_script_http_response_info->script_url() && + !version_for_main_script_http_response_info + ->GetMainScriptHttpResponseInfo()) { + version_for_main_script_http_response_info_ = + std::move(version_for_main_script_http_response_info); + } reader_ = std::make_unique<ServiceWorkerInstalledScriptReader>( std::move(response_reader), this); reader_->Start(); @@ -61,6 +78,12 @@ void ServiceWorkerInstalledScriptLoader::OnHttpInfoRead( scoped_refptr<HttpResponseInfoIOBuffer> http_info) { net::HttpResponseInfo* info = http_info->http_info.get(); + DCHECK(info); + + if (version_for_main_script_http_response_info_) { + version_for_main_script_http_response_info_->SetMainScriptHttpResponseInfo( + *info); + } network::ResourceResponseHead head; head.request_start = request_start_;
diff --git a/content/browser/service_worker/service_worker_installed_script_loader.h b/content/browser/service_worker/service_worker_installed_script_loader.h index c88bf5e..18733b0c 100644 --- a/content/browser/service_worker/service_worker_installed_script_loader.h +++ b/content/browser/service_worker/service_worker_installed_script_loader.h
@@ -16,6 +16,8 @@ namespace content { +class ServiceWorkerVersion; + // S13nServiceWorker: A URLLoader that loads an installed service worker script // for a service worker that doesn't have a // ServiceWorkerInstalledScriptsManager. @@ -34,7 +36,10 @@ ServiceWorkerInstalledScriptLoader( uint32_t options, network::mojom::URLLoaderClientPtr client, - std::unique_ptr<ServiceWorkerResponseReader> response_reader); + std::unique_ptr<ServiceWorkerResponseReader> response_reader, + scoped_refptr<ServiceWorkerVersion> + version_for_main_script_http_response_info, + const GURL& request_url); ~ServiceWorkerInstalledScriptLoader() override; // ServiceWorkerInstalledScriptReader::Client overrides: @@ -67,6 +72,8 @@ uint32_t options_ = network::mojom::kURLLoadOptionNone; network::mojom::URLLoaderClientPtr client_; + scoped_refptr<ServiceWorkerVersion> + version_for_main_script_http_response_info_; base::TimeTicks request_start_; std::unique_ptr<ServiceWorkerInstalledScriptReader> reader_;
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc index 4011411..e76f057 100644 --- a/content/browser/service_worker/service_worker_navigation_loader.cc +++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -420,6 +420,7 @@ // browser. See https://crbug.com/392409 for details about this design. // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a // ServiceWorker, we have to check the security level of the responses. + DCHECK(version->GetMainScriptHttpResponseInfo()); response_head_.ssl_info = version->GetMainScriptHttpResponseInfo()->ssl_info; // Handle a redirect response. ComputeRedirectInfo returns non-null redirect
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.cc b/content/browser/service_worker/service_worker_script_loader_factory.cc index 9e6dd9b..b24330f9 100644 --- a/content/browser/service_worker/service_worker_script_loader_factory.cc +++ b/content/browser/service_worker/service_worker_script_loader_factory.cc
@@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "base/debug/crash_logging.h" #include "content/browser/service_worker/service_worker_cache_writer.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_installed_script_loader.h" @@ -87,7 +88,8 @@ context_->storage()->CreateResponseReader(resource_id); mojo::MakeStrongBinding( std::make_unique<ServiceWorkerInstalledScriptLoader>( - options, std::move(client), std::move(response_reader)), + options, std::move(client), std::move(response_reader), version, + resource_request.url), std::move(request)); return; } @@ -164,6 +166,10 @@ // or importScripts() (RESOURCE_TYPE_SCRIPT). if (resource_request.resource_type != RESOURCE_TYPE_SERVICE_WORKER && resource_request.resource_type != RESOURCE_TYPE_SCRIPT) { + static auto* key = base::debug::AllocateCrashKeyString( + "swslf_bad_type", base::debug::CrashKeySize::Size32); + base::debug::SetCrashKeyString( + key, base::NumberToString(resource_request.resource_type)); mojo::ReportBadMessage("SWSLF_BAD_RESOURCE_TYPE"); return false; } @@ -238,7 +244,8 @@ mojo::MakeStrongBinding( std::make_unique<ServiceWorkerInstalledScriptLoader>( options, std::move(client), - context_->storage()->CreateResponseReader(new_resource_id)), + context_->storage()->CreateResponseReader(new_resource_id), version, + resource_request.url), std::move(request)); } } // namespace content
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index f9841aa..6997223 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -256,6 +256,8 @@ } ServiceWorkerVersion::~ServiceWorkerVersion() { + // TODO(falken): Investigate whether this can be removed. The destructor used + // to be more complicated and could result in various methods being called. in_dtor_ = true; // Record UMA if the worker was trying to start. One way we get here is if the @@ -271,10 +273,6 @@ if (context_) context_->RemoveLiveVersion(version_id_); - if (running_status() == EmbeddedWorkerStatus::STARTING || - running_status() == EmbeddedWorkerStatus::RUNNING) { - embedded_worker_->Stop(); - } embedded_worker_->RemoveObserver(this); }
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc index a60edc92..dd147c0 100644 --- a/content/browser/service_worker/service_worker_version_unittest.cc +++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -688,21 +688,6 @@ EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version_->running_status()); } -TEST_F(ServiceWorkerVersionTest, StoppingBeforeDestruct) { - RunningStateListener listener; - version_->AddObserver(&listener); - StartWorker(version_.get(), ServiceWorkerMetrics::EventType::UNKNOWN); - EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, listener.last_status); - - // Destruct |version_| by releasing all references, including the provider - // host's. - helper_->context()->RemoveProviderHost( - version_->provider_host()->process_id(), - version_->provider_host()->provider_id()); - version_ = nullptr; - EXPECT_EQ(EmbeddedWorkerStatus::STOPPING, listener.last_status); -} - // Test that update isn't triggered for a non-stale worker. TEST_F(ServiceWorkerVersionTest, StaleUpdate_FreshWorker) { version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 63b892510..74c9f5a 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3749,11 +3749,6 @@ return interstitial_page_; } -void WebContentsImpl::PausePageScheduledTasks(bool paused) { - SendPageMessage( - new PageMsg_PausePageScheduledTasks(MSG_ROUTING_NONE, paused)); -} - bool WebContentsImpl::IsSavable() { // WebKit creates Document object when MIME type is application/xhtml+xml, // so we also support this MIME type.
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 9dfdc90..9e72788 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -460,7 +460,6 @@ void ClearFocusedElement() override; bool IsShowingContextMenu() override; void SetShowingContextMenu(bool showing) override; - void PausePageScheduledTasks(bool paused) override; base::UnguessableToken GetAudioGroupId() override; bool CompletedFirstVisuallyNonEmptyPaint() override;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc index 47db5cf5..7e7cee1 100644 --- a/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -2842,7 +2842,7 @@ SetBrowserClientForTesting(old_client); } -IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, PausePageScheduledTasks) { +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, SetPageFrozen) { EXPECT_TRUE(embedded_test_server()->Start()); GURL test_url = embedded_test_server()->GetURL("/pause_schedule_task.html"); @@ -2862,8 +2862,9 @@ break; } - // Suspend blink schedule tasks. - shell()->web_contents()->PausePageScheduledTasks(true); + // Freeze the blink page. + shell()->web_contents()->WasHidden(); + shell()->web_contents()->SetPageFrozen(true); // Make the javascript work. for (int i = 0; i < 10; i++) { @@ -2883,8 +2884,9 @@ &next_text_length)); EXPECT_EQ(text_length, next_text_length); - // Resume the paused blink schedule tasks. - shell()->web_contents()->PausePageScheduledTasks(false); + // Wake the frozen page up. + shell()->web_contents()->WasHidden(); + shell()->web_contents()->SetPageFrozen(false); // Wait for an amount of time in order to give the javascript time to // work again. If the javascript doesn't work again, the test will fail due to
diff --git a/content/common/frame.mojom b/content/common/frame.mojom index 57f2cfc..f660470 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom
@@ -58,6 +58,9 @@ // activated. OnPortalActivated(); + // Set the lifecycle state. + SetLifecycleState(blink.mojom.FrameLifecycleState state); + // Samsung Galaxy Note-specific "smart clip" stylus text getter. // Extracts the data at the given rect. [EnableIf=is_android]
diff --git a/content/common/page_messages.h b/content/common/page_messages.h index e80e230a..83d8a3c3 100644 --- a/content/common/page_messages.h +++ b/content/common/page_messages.h
@@ -34,10 +34,6 @@ IPC_MESSAGE_ROUTED1(PageMsg_AudioStateChanged, bool /* is_audio_playing */) -// Pause and unpause active tasks regarding deferLoading, active javascripts, -// timer, scheduled task through |blink::WebFrameScheduler|. -IPC_MESSAGE_ROUTED1(PageMsg_PausePageScheduledTasks, bool /* paused */) - // Sent to OOPIF renderers when the main frame's ScreenInfo changes. IPC_MESSAGE_ROUTED1(PageMsg_UpdateScreenInfo, content::ScreenInfo /* screen_info */)
diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc index d16a9348..60e2ada 100644 --- a/content/public/browser/gpu_utils.cc +++ b/content/public/browser/gpu_utils.cc
@@ -71,10 +71,6 @@ base::CommandLine::ForCurrentProcess(); gpu::GpuPreferences gpu_preferences = gpu::gles2::ParseGpuPreferences(command_line); - gpu_preferences.single_process = - command_line->HasSwitch(switches::kSingleProcess); - gpu_preferences.in_process_gpu = - command_line->HasSwitch(switches::kInProcessGPU); gpu_preferences.disable_accelerated_video_decode = command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode); gpu_preferences.disable_accelerated_video_encode = @@ -105,7 +101,8 @@ command_line->HasSwitch(switches::kGpuStartupDialog); gpu_preferences.disable_gpu_watchdog = command_line->HasSwitch(switches::kDisableGpuWatchdog) || - (gpu_preferences.single_process || gpu_preferences.in_process_gpu); + command_line->HasSwitch(switches::kSingleProcess) || + command_line->HasSwitch(switches::kInProcessGPU); gpu_preferences.gpu_sandbox_start_early = command_line->HasSwitch(switches::kGpuSandboxStartEarly);
diff --git a/content/public/browser/navigation_throttle.cc b/content/public/browser/navigation_throttle.cc index 14270f9..45ff97e 100644 --- a/content/public/browser/navigation_throttle.cc +++ b/content/public/browser/navigation_throttle.cc
@@ -81,7 +81,7 @@ } void NavigationThrottle::Resume() { - if (!resume_callback_.is_null()) { + if (resume_callback_) { resume_callback_.Run(); return; } @@ -90,6 +90,10 @@ void NavigationThrottle::CancelDeferredNavigation( NavigationThrottle::ThrottleCheckResult result) { + if (cancel_deferred_navigation_callback_) { + cancel_deferred_navigation_callback_.Run(result); + return; + } static_cast<NavigationHandleImpl*>(navigation_handle_) ->CancelDeferredNavigation(this, result); }
diff --git a/content/public/browser/navigation_throttle.h b/content/public/browser/navigation_throttle.h index a25f91a5..fc2610fa 100644 --- a/content/public/browser/navigation_throttle.h +++ b/content/public/browser/navigation_throttle.h
@@ -180,6 +180,13 @@ resume_callback_ = callback; } + // Overrides the default CancelDeferredNavigation method and replaces it by + // |callback|. This should only be used in tests. + void set_cancel_deferred_navigation_callback_for_testing( + const base::RepeatingCallback<void(ThrottleCheckResult)> callback) { + cancel_deferred_navigation_callback_ = callback; + } + protected: // Resumes a navigation that was previously deferred by this // NavigationThrottle. @@ -201,6 +208,8 @@ // Used in tests. base::RepeatingClosure resume_callback_; + base::RepeatingCallback<void(ThrottleCheckResult)> + cancel_deferred_navigation_callback_; }; #if defined(UNIT_TEST)
diff --git a/content/public/browser/render_widget_host_view.h b/content/public/browser/render_widget_host_view.h index b2ee7df..d02f832 100644 --- a/content/public/browser/render_widget_host_view.h +++ b/content/public/browser/render_widget_host_view.h
@@ -143,9 +143,7 @@ virtual base::string16 GetSelectedText() = 0; // This only returns non-null on platforms that implement touch - // selection editing (TSE), currently Aura and (soon) Android. - // TODO(wjmaclean): update this comment when OOPIF TSE is implemented on - // Android. https://crbug.com/470662. + // selection editing (TSE), currently Aura and Android. virtual TouchSelectionControllerClientManager* GetTouchSelectionControllerClientManager() = 0;
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 758415f7..87031ac 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -893,20 +893,6 @@ // Tells the WebContents whether the context menu is showing. virtual void SetShowingContextMenu(bool showing) = 0; - // Pause and unpause scheduled tasks in the page of blink. This function will - // suspend page loadings and all background processing like active javascript, - // and timers through |blink::Page::SetPaused|. If you want to resume the - // paused state, you have to call this function with |false| argument again. - // The function with |false| should be called after calling it with |true|. If - // not, assertion will happen. - // - // WARNING: This only pauses the activities in the particular page in the - // renderer process, but may indirectly block or break other pages when they - // wait for the common backend (e.g. storage) in the browser process. - // TODO(gyuyoung): https://crbug.com/822564 - Make this feature safer and fix - // bugs. - virtual void PausePageScheduledTasks(bool paused) = 0; - #if defined(OS_ANDROID) CONTENT_EXPORT static WebContents* FromJavaWebContents( const base::android::JavaRef<jobject>& jweb_contents_android);
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 51dc6571..9f93e3f 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -171,6 +171,10 @@ "FramebustingNeedsSameOriginOrUserGesture", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables freezing frame support based on feature policies. +const base::Feature kFreezeFramesOnVisibility{ + "FreezeFramesOnVisibility", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kFreezePurgeMemoryBackgroundedOnly{ "FreezePurgeMemoryBackgroundedOnly", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 3fd33e0..dd36944 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -49,6 +49,7 @@ CONTENT_EXPORT extern const base::Feature kFontSrcLocalMatching; CONTENT_EXPORT extern const base::Feature kFramebustingNeedsSameOriginOrUserGesture; +CONTENT_EXPORT extern const base::Feature kFreezeFramesOnVisibility; CONTENT_EXPORT extern const base::Feature kFreezePurgeMemoryBackgroundedOnly; CONTENT_EXPORT extern const base::Feature kGamepadVibration; CONTENT_EXPORT extern const base::Feature kGuestViewCrossProcessFrames;
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h index 56261b6..7a43701 100644 --- a/content/public/test/mock_navigation_handle.h +++ b/content/public/test/mock_navigation_handle.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_ #define CONTENT_PUBLIC_TEST_MOCK_NAVIGATION_HANDLE_H_ +#include "base/memory/ref_counted.h" #include "content/public/browser/global_request_id.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" @@ -72,7 +73,7 @@ MOCK_METHOD1(RemoveRequestHeader, void(const std::string&)); MOCK_METHOD2(SetRequestHeader, void(const std::string&, const std::string&)); const net::HttpResponseHeaders* GetResponseHeaders() override { - return response_headers_; + return response_headers_.get(); } MOCK_METHOD0(GetConnectionInfo, net::HttpResponseInfo::ConnectionInfo()); const net::SSLInfo& GetSSLInfo() override { return ssl_info_; } @@ -117,8 +118,9 @@ void set_request_headers(const net::HttpRequestHeaders& request_headers) { request_headers_ = request_headers; } - void set_response_headers(net::HttpResponseHeaders* reponse_headers) { - response_headers_ = reponse_headers; + void set_response_headers( + scoped_refptr<net::HttpResponseHeaders> response_headers) { + response_headers_ = response_headers; } void set_ssl_info(const net::SSLInfo& ssl_info) { ssl_info_ = ssl_info; } void set_is_form_submission(bool is_form_submission) { @@ -146,7 +148,7 @@ bool has_committed_ = false; bool is_error_page_ = false; net::HttpRequestHeaders request_headers_; - net::HttpResponseHeaders* response_headers_ = nullptr; + scoped_refptr<net::HttpResponseHeaders> response_headers_; net::SSLInfo ssl_info_; bool is_form_submission_ = false; bool was_response_cached_ = false;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index b2cec49..a7fbf37 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -819,7 +819,8 @@ mojom::NavigationClient::CommitNavigationCallback per_navigation_mojo_interface_commit_callback, const network::ResourceResponseHead* head, - std::unique_ptr<NavigationClient> navigation_client) { + std::unique_ptr<NavigationClient> navigation_client, + int request_id) { std::unique_ptr<DocumentState> document_state(new DocumentState()); InternalDocumentStateData* internal_data = InternalDocumentStateData::FromDocumentState(document_state.get()); @@ -848,7 +849,7 @@ common_params.navigation_type == FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL); internal_data->set_previews_state(common_params.previews_state); - internal_data->set_request_id(ResourceDispatcher::MakeRequestID()); + internal_data->set_request_id(request_id); document_state->set_can_load_local_resources( commit_params.can_load_local_resources); @@ -971,6 +972,60 @@ } // namespace +// This class uses existing WebNavigationBodyLoader to read the whole response +// body into in-memory buffer, and then creates another body loader with static +// data so that we can parse mhtml archive synchronously. This is a workaround +// for the fact that we need the whole archive to determine the document's mime +// type and construct a right document instance. +class RenderFrameImpl::MHTMLBodyLoaderClient + : public blink::WebNavigationBodyLoader::Client { + public: + // Once the body is read, fills |navigation_params| with the new body loader + // and calls |done_callbcak|. + MHTMLBodyLoaderClient( + std::unique_ptr<blink::WebNavigationParams> navigation_params, + base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)> + done_callback) + : navigation_params_(std::move(navigation_params)), + done_callback_(std::move(done_callback)) { + body_loader_ = std::move(navigation_params_->body_loader); + body_loader_->StartLoadingBody(this, false /* use_isolated_code_cache */); + } + + ~MHTMLBodyLoaderClient() override {} + + void BodyCodeCacheReceived(base::span<const uint8_t>) override {} + + void BodyDataReceived(base::span<const char> data) override { + data_.Append(data.data(), data.size()); + } + + void BodyLoadingFinished(base::TimeTicks completion_time, + int64_t total_encoded_data_length, + int64_t total_encoded_body_length, + int64_t total_decoded_body_length, + bool should_report_corb_blocking, + const base::Optional<WebURLError>& error) override { + if (!error.has_value()) { + WebNavigationParams::FillBodyLoader(navigation_params_.get(), data_); + // Clear |is_static_data| flag to avoid the special behavior it triggers, + // e.g. skipping content disposition check. We want this load to be + // regular, just like with an original body loader. + navigation_params_->is_static_data = false; + } + std::move(done_callback_).Run(std::move(navigation_params_)); + } + + private: + WebData data_; + std::unique_ptr<blink::WebNavigationParams> navigation_params_; + std::unique_ptr<blink::WebNavigationBodyLoader> body_loader_; + base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)> + done_callback_; + + DISALLOW_COPY_AND_ASSIGN(MHTMLBodyLoaderClient); +}; + class RenderFrameImpl::FrameURLLoaderFactory : public blink::WebURLLoaderFactory { public: @@ -2562,6 +2617,11 @@ frame_->OnPortalActivated(); } +void RenderFrameImpl::SetLifecycleState( + blink::mojom::FrameLifecycleState state) { + frame_->SetLifecycleState(state); +} + void RenderFrameImpl::VisibilityChanged( blink::mojom::FrameVisibility visibility) { GetFrameHost()->VisibilityChanged(visibility); @@ -2844,7 +2904,8 @@ navigation_state->common_params(), navigation_state->commit_params(), base::TimeTicks(), // Not used for failed navigation. mojom::FrameNavigationControl::CommitNavigationCallback(), - mojom::NavigationClient::CommitNavigationCallback(), nullptr, nullptr); + mojom::NavigationClient::CommitNavigationCallback(), nullptr, nullptr, + ResourceDispatcher::MakeRequestID()); FillMiscNavigationParams(navigation_state->common_params(), navigation_state->commit_params(), navigation_params.get()); @@ -3273,18 +3334,12 @@ DCHECK(!IsRendererDebugURL(common_params.url)); DCHECK( !FrameMsg_Navigate_Type::IsSameDocument(common_params.navigation_type)); - // If this was a renderer-initiated navigation (nav_entry_id == 0) from this - // frame, but it was aborted, then ignore it. - if (!browser_side_navigation_pending_ && - !browser_side_navigation_pending_url_.is_empty() && - browser_side_navigation_pending_url_ == commit_params.original_url && - commit_params.nav_entry_id == 0) { + if (ShouldIgnoreCommitNavigation(commit_params)) { browser_side_navigation_pending_url_ = GURL(); - if (IsPerNavigationMojoInterfaceEnabled()) { + if (IsPerNavigationMojoInterfaceEnabled()) navigation_client_impl_.reset(); - } else { + else std::move(callback).Run(blink::mojom::CommitResult::Aborted); - } return; } @@ -3294,6 +3349,130 @@ !base::FeatureList::IsEnabled(network::features::kNetworkService) || subresource_loader_factories); + // We only save metrics of the main frame's main resource to the + // document state. In view source mode, we effectively let the user + // see the source of the server's error page instead of using custom + // one derived from the metrics saved to document state. + const network::ResourceResponseHead* response_head = nullptr; + if (!frame_->Parent() && !frame_->IsViewSourceModeEnabled()) + response_head = &head; + int request_id = ResourceDispatcher::MakeRequestID(); + std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams( + common_params, commit_params, base::TimeTicks::Now(), std::move(callback), + std::move(per_navigation_mojo_interface_callback), response_head, + std::move(navigation_client_impl_), request_id); + + // Check if the navigation being committed originated as a client redirect. + bool is_client_redirect = + !!(common_params.transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT); + auto navigation_params = + std::make_unique<WebNavigationParams>(devtools_navigation_token); + navigation_params->is_client_redirect = is_client_redirect; + FillMiscNavigationParams(common_params, commit_params, + navigation_params.get()); + + auto commit_with_params = base::BindOnce( + &RenderFrameImpl::CommitNavigationWithParams, weak_factory_.GetWeakPtr(), + common_params, commit_params, std::move(subresource_loader_factories), + std::move(subresource_overrides), + std::move(controller_service_worker_info), + std::move(prefetch_loader_factory), std::move(document_state)); + + // Perform a navigation to a data url if needed (for main frames). + // Note: the base URL might be invalid, so also check the data URL string. + bool should_load_data_url = !common_params.base_url_for_data_url.is_empty(); +#if defined(OS_ANDROID) + should_load_data_url |= !commit_params.data_url_as_string.empty(); +#endif + if (is_main_frame_ && should_load_data_url) { + std::string mime_type, charset, data; + GURL base_url; + DecodeDataURL(common_params, commit_params, &mime_type, &charset, &data, + &base_url); + navigation_params->url = base_url; + WebNavigationParams::FillStaticResponse(navigation_params.get(), + WebString::FromUTF8(mime_type), + WebString::FromUTF8(charset), data); + // Needed so that history-url-only changes don't become reloads. + navigation_params->unreachable_url = common_params.history_url_for_data_url; + std::move(commit_with_params).Run(std::move(navigation_params)); + return; + } + + FillNavigationParamsRequest(common_params, commit_params, + navigation_params.get()); + if (!url_loader_client_endpoints && + common_params.url.SchemeIs(url::kDataScheme)) { + // Normally, data urls will have |url_loader_client_endpoints| set. + // However, tests and interstitial pages pass data urls directly, + // without creating url loader. + std::string mime_type, charset, data; + if (!net::DataURL::Parse(common_params.url, &mime_type, &charset, &data)) { + CHECK(false) << "Invalid URL passed: " + << common_params.url.possibly_invalid_spec(); + return; + } + WebNavigationParams::FillStaticResponse(navigation_params.get(), + WebString::FromUTF8(mime_type), + WebString::FromUTF8(charset), data); + } else { + NavigationBodyLoader::FillNavigationParamsResponseAndBodyLoader( + common_params, commit_params, request_id, head, + std::move(url_loader_client_endpoints), + GetTaskRunner(blink::TaskType::kInternalLoading), GetRoutingID(), + !frame_->Parent(), navigation_params.get()); + } + + // The MHTML mime type should be same as the one we check in the browser + // process's download_utils::MustDownload. + bool is_mhtml_archive = + base::LowerCaseEqualsASCII(head.mime_type, "multipart/related") || + base::LowerCaseEqualsASCII(head.mime_type, "message/rfc822"); + if (is_mhtml_archive && navigation_params->body_loader) { + // Load full mhtml archive before committing navigation. + // We need this to retrieve the document mime type prior to committing. + mhtml_body_loader_client_ = + std::make_unique<RenderFrameImpl::MHTMLBodyLoaderClient>( + std::move(navigation_params), std::move(commit_with_params)); + return; + } + + // Common case - fill navigation params from provided information and commit. + std::move(commit_with_params).Run(std::move(navigation_params)); +} + +bool RenderFrameImpl::ShouldIgnoreCommitNavigation( + const CommitNavigationParams& commit_params) { + // We can ignore renderer-initiated navigations (nav_entry_id == 0) which + // have been canceled in the renderer, but browser was not aware yet at the + // moment of issuing a CommitNavigation call. + if (!browser_side_navigation_pending_ && + !browser_side_navigation_pending_url_.is_empty() && + browser_side_navigation_pending_url_ == commit_params.original_url && + commit_params.nav_entry_id == 0) { + return true; + } + return false; +} + +void RenderFrameImpl::CommitNavigationWithParams( + const CommonNavigationParams& common_params, + const CommitNavigationParams& commit_params, + std::unique_ptr<blink::URLLoaderFactoryBundleInfo> + subresource_loader_factories, + base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>> + subresource_overrides, + blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info, + network::mojom::URLLoaderFactoryPtr prefetch_loader_factory, + std::unique_ptr<DocumentState> document_state, + std::unique_ptr<WebNavigationParams> navigation_params) { + if (ShouldIgnoreCommitNavigation(commit_params)) { + browser_side_navigation_pending_url_ = GURL(); + if (IsPerNavigationMojoInterfaceEnabled()) + navigation_client_impl_.reset(); + return; + } + // TODO(yoichio): This is temporary switch to have chrome WebUI // use the old web APIs. // After completion of the migration, we should remove this. @@ -3315,18 +3494,6 @@ PrepareFrameForCommit(common_params.url, commit_params); - // We only save metrics of the main frame's main resource to the - // document state. In view source mode, we effectively let the user - // see the source of the server's error page instead of using custom - // one derived from the metrics saved to document state. - const network::ResourceResponseHead* response_head = nullptr; - if (!frame_->Parent() && !frame_->IsViewSourceModeEnabled()) - response_head = &head; - std::unique_ptr<DocumentState> document_state(BuildDocumentStateFromParams( - common_params, commit_params, base::TimeTicks::Now(), std::move(callback), - std::move(per_navigation_mojo_interface_callback), response_head, - std::move(navigation_client_impl_))); - blink::WebFrameLoadType load_type = NavigationTypeToLoadType( common_params.navigation_type, common_params.should_replace_current_entry, commit_params.page_state.IsValid()); @@ -3354,66 +3521,11 @@ return; } - // Check if the navigation being committed originated as a client redirect. - bool is_client_redirect = - !!(common_params.transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT); - - auto navigation_params = - std::make_unique<WebNavigationParams>(devtools_navigation_token); navigation_params->frame_load_type = load_type; navigation_params->history_item = item_for_history_navigation; - navigation_params->is_client_redirect = is_client_redirect; navigation_params->service_worker_network_provider = BuildServiceWorkerNetworkProviderForNavigation( &commit_params, std::move(controller_service_worker_info)); - FillMiscNavigationParams(common_params, commit_params, - navigation_params.get()); - - // Perform a navigation to a data url if needed (for main frames). - // Note: the base URL might be invalid, so also check the data URL string. - bool should_load_data_url = !common_params.base_url_for_data_url.is_empty(); -#if defined(OS_ANDROID) - should_load_data_url |= !commit_params.data_url_as_string.empty(); -#endif - if (is_main_frame_ && should_load_data_url) { - std::string mime_type, charset, data; - GURL base_url; - DecodeDataURL(common_params, commit_params, &mime_type, &charset, &data, - &base_url); - navigation_params->url = base_url; - WebNavigationParams::FillStaticResponse(navigation_params.get(), - WebString::FromUTF8(mime_type), - WebString::FromUTF8(charset), data); - // Needed so that history-url-only changes don't become reloads. - navigation_params->unreachable_url = common_params.history_url_for_data_url; - } else { - InternalDocumentStateData* internal_data = - InternalDocumentStateData::FromDocumentState(document_state.get()); - FillNavigationParamsRequest(common_params, commit_params, - navigation_params.get()); - if (!url_loader_client_endpoints && - common_params.url.SchemeIs(url::kDataScheme)) { - // Normally, data urls will have |url_loader_client_endpoints| set. - // However, tests and interstitial pages pass data urls directly, - // without creating url loader. - std::string mime_type, charset, data; - if (!net::DataURL::Parse(common_params.url, &mime_type, &charset, - &data)) { - CHECK(false) << "Invalid URL passed: " - << common_params.url.possibly_invalid_spec(); - return; - } - WebNavigationParams::FillStaticResponse( - navigation_params.get(), WebString::FromUTF8(mime_type), - WebString::FromUTF8(charset), data); - } else { - NavigationBodyLoader::FillNavigationParamsResponseAndBodyLoader( - common_params, commit_params, internal_data->request_id(), head, - std::move(url_loader_client_endpoints), - GetTaskRunner(blink::TaskType::kInternalLoading), GetRoutingID(), - !frame_->Parent(), navigation_params.get()); - } - } frame_->CommitNavigation(std::move(navigation_params), std::move(document_state)); @@ -3476,6 +3588,7 @@ RenderFrameImpl::PrepareRenderViewForNavigation(common_params.url, commit_params); sync_navigation_callback_.Cancel(); + mhtml_body_loader_client_.reset(); GetContentClient()->SetActiveURL( common_params.url, frame_->Top()->GetSecurityOrigin().ToString().Utf8()); @@ -3591,7 +3704,7 @@ std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams( common_params, commit_params, base::TimeTicks(), std::move(callback), std::move(per_navigation_mojo_interface_callback), nullptr, - std::move(navigation_client_impl_)); + std::move(navigation_client_impl_), ResourceDispatcher::MakeRequestID()); // The load of the error page can result in this frame being removed. // Use a WeakPtr as an easy way to detect whether this has occured. If so, @@ -4844,6 +4957,7 @@ void RenderFrameImpl::AbortClientNavigation() { browser_side_navigation_pending_ = false; sync_navigation_callback_.Cancel(); + mhtml_body_loader_client_.reset(); if (!IsPerNavigationMojoInterfaceEnabled()) Send(new FrameHostMsg_AbortNavigation(routing_id_)); } @@ -5860,6 +5974,7 @@ browser_side_navigation_pending_ = false; browser_side_navigation_pending_url_ = GURL(); sync_navigation_callback_.Cancel(); + mhtml_body_loader_client_.reset(); GetContentClient()->SetActiveURL( url, frame_->Top()->GetSecurityOrigin().ToString().Utf8()); @@ -6185,6 +6300,7 @@ } sync_navigation_callback_.Cancel(); + mhtml_body_loader_client_.reset(); // When an MHTML Archive is present, it should be used to serve iframe // content instead of doing a network request.
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index d26a27ca..75a649b 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -157,6 +157,7 @@ class BlinkInterfaceRegistryImpl; class CompositorDependencies; +class DocumentState; class ExternalPopupMenu; class FrameRequestBlocker; class ManifestManager; @@ -547,6 +548,8 @@ void ResumeBlockedRequests() override; void CancelBlockedRequests() override; void OnPortalActivated() override; + void SetLifecycleState(blink::mojom::FrameLifecycleState state) override; + #if defined(OS_ANDROID) void ExtractSmartClipData( const gfx::Rect& rect, @@ -1227,6 +1230,26 @@ // document or an MHTML archive). void CommitSyncNavigation(std::unique_ptr<blink::WebNavigationInfo> info); + // Commit navigation with |navigation_params| prepared. + void CommitNavigationWithParams( + const CommonNavigationParams& common_params, + const CommitNavigationParams& commit_params, + std::unique_ptr<blink::URLLoaderFactoryBundleInfo> + subresource_loader_factories, + base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>> + subresource_overrides, + blink::mojom::ControllerServiceWorkerInfoPtr + controller_service_worker_info, + network::mojom::URLLoaderFactoryPtr prefetch_loader_factory, + std::unique_ptr<DocumentState> document_state, + std::unique_ptr<blink::WebNavigationParams> navigation_params); + + // We can ignore renderer-initiated navigations which have been canceled + // in the renderer, but browser was not aware yet at the moment of issuing + // a CommitNavigation call. + bool ShouldIgnoreCommitNavigation( + const CommitNavigationParams& commit_params); + // Decodes a data url for navigation commit. void DecodeDataURL(const CommonNavigationParams& common_params, const CommitNavigationParams& commit_params, @@ -1703,6 +1726,9 @@ base::CancelableOnceCallback<void()> sync_navigation_callback_; + class MHTMLBodyLoaderClient; + std::unique_ptr<MHTMLBodyLoaderClient> mhtml_body_loader_client_; + base::WeakPtrFactory<RenderFrameImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(RenderFrameImpl);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 73b2a3c..0623b9f 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc
@@ -1196,10 +1196,6 @@ webview()->AudioStateChanged(is_audio_playing); } -void RenderViewImpl::OnPausePageScheduledTasks(bool paused) { - webview()->PausePageScheduledTasks(paused); -} - /////////////////////////////////////////////////////////////////////////////// void RenderViewImpl::ShowCreatedPopupWidget(RenderWidget* popup_widget, @@ -1268,8 +1264,6 @@ IPC_MESSAGE_HANDLER(PageMsg_SetHistoryOffsetAndLength, OnSetHistoryOffsetAndLength) IPC_MESSAGE_HANDLER(PageMsg_AudioStateChanged, OnAudioStateChanged) - IPC_MESSAGE_HANDLER(PageMsg_PausePageScheduledTasks, - OnPausePageScheduledTasks) IPC_MESSAGE_HANDLER(PageMsg_UpdateScreenInfo, OnUpdateScreenInfo) IPC_MESSAGE_HANDLER(PageMsg_SetPageFrozen, SetPageFrozen)
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index a35989a..e454434c 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h
@@ -462,7 +462,6 @@ void OnUpdateWebPreferences(const WebPreferences& prefs); void OnSetPageScale(float page_scale_factor); void OnAudioStateChanged(bool is_audio_playing); - void OnPausePageScheduledTasks(bool paused); void OnSetBackgroundOpaque(bool opaque); // Page message handlers -----------------------------------------------------
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc index 008f3ee..689f771 100644 --- a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc +++ b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/debug/alias.h" #include "content/public/common/resource_type.h" #include "content/public/renderer/url_loader_throttle_provider.h" #include "content/renderer/loader/request_extra_data.h" @@ -15,9 +16,25 @@ #include "ipc/ipc_message.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "third_party/blink/public/common/service_worker/service_worker_utils.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "url/gurl.h" namespace content { +namespace { + +// TODO(https://crbug.com/929042): Remove this after the linked bug is fixed. +void CrashBecauseNotMainScriptRequest(const blink::WebURLRequest& request) { + GURL url(request.Url()); + DEBUG_ALIAS_FOR_GURL(url_buf, url); + blink::mojom::RequestContextType context = request.GetRequestContext(); + base::debug::Alias(&context); + CHECK(false); +} + +} // namespace + ServiceWorkerNetworkProviderForServiceWorker:: ServiceWorkerNetworkProviderForServiceWorker( int provider_id, @@ -61,6 +78,11 @@ // We only get here for the main script request from the shadow page. // importScripts() and other subresource fetches are handled on the worker // thread by ServiceWorkerFetchContextImpl. + if (request.GetRequestContext() != + blink::mojom::RequestContextType::SERVICE_WORKER) { + CrashBecauseNotMainScriptRequest(request); + return nullptr; + } DCHECK_EQ(blink::mojom::RequestContextType::SERVICE_WORKER, request.GetRequestContext());
diff --git a/content/test/data/find_in_page_simple_frame.html b/content/test/data/find_in_page_simple_frame.html new file mode 100644 index 0000000..fae4833 --- /dev/null +++ b/content/test/data/find_in_page_simple_frame.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> +<body> + this frame contains a result. + and another result. + and another result. +</body> +</html>
diff --git a/content/test/data/find_in_page_two_frames.html b/content/test/data/find_in_page_two_frames.html new file mode 100644 index 0000000..e702a174 --- /dev/null +++ b/content/test/data/find_in_page_two_frames.html
@@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<body> +Making two frames to reproduce crbug.com/889075. +<iframe src="find_in_page_simple_frame.html"></iframe> +<iframe src="find_in_page_simple_frame.html"></iframe> +</body> +</html>
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py index 0e9bcbd..074f245 100644 --- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -77,9 +77,6 @@ 'vector-scalar-arithmetic-inside-loop-complex.html', ['nvidia'], bug=772651) - # All platforms. - self.Fail('conformance2/glsl3/tricky-loop-conditions.html', bug=905001) - # This test needs to be rewritten to measure its expected # performance; it's currently too flaky even on release bots. self.Skip('conformance/rendering/texture-switch-performance.html', @@ -117,6 +114,8 @@ self.Fail('conformance2/textures/misc/' + 'generate-mipmap-with-large-base-level.html', ['win', 'no_passthrough'], bug=3033) # angle bug ID + self.Fail('conformance2/glsl3/tricky-loop-conditions.html', + ['win'], bug=1465) # anglebug.com/1465 # Win / NVidia self.Flaky('deqp/functional/gles3/fbomultisample*', @@ -673,6 +672,9 @@ self.Flaky('conformance2/textures/canvas/tex-3d-r16f-red-half_float.html', ['mac', ('nvidia', 0xfe9)], bug=911772) + self.Fail('conformance2/glsl3/tricky-loop-conditions.html', + ['mac', ('nvidia', 0xfe9)], bug=929398) + # When these fail on this configuration, they fail multiple times in a row. self.Fail('deqp/functional/gles3/shaderoperator/*', ['mac', 'nvidia'], bug=756537) @@ -1324,14 +1326,6 @@ # Basic failures that need to be investigated on multiple devices self.Fail('conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html', ['android'], bug=709351) - # Video uploads to some texture formats new in WebGL 2.0 are - # failing. - self.Fail('conformance2/textures/video/' + - 'tex-2d-rg8ui-rg_integer-unsigned_byte.html', - ['android'], bug=906735) - self.Fail('conformance2/textures/video/' + - 'tex-2d-rgb8ui-rgb_integer-unsigned_byte.html', - ['android'], bug=906735) # Qualcomm (Pixel 2) failures self.Fail('conformance/extensions/webgl-compressed-texture-astc.html', @@ -1342,9 +1336,6 @@ ['android', 'qualcomm'], bug=906742) self.Fail('conformance2/rendering/uniform-block-buffer-size.html', ['android', 'qualcomm'], bug=906743) - self.Fail('conformance2/textures/video/' + - 'tex-2d-rgba8ui-rgba_integer-unsigned_byte.html', - ['android', 'qualcomm'], bug=906735) self.Fail('conformance2/textures/misc/tex-new-formats.html', ['android', 'qualcomm'], bug=906740) self.Fail('conformance2/textures/misc/copy-texture-image-luma-format.html',
diff --git a/extensions/browser/api/declarative_net_request/constants.cc b/extensions/browser/api/declarative_net_request/constants.cc index e530344..084b92f 100644 --- a/extensions/browser/api/declarative_net_request/constants.cc +++ b/extensions/browser/api/declarative_net_request/constants.cc
@@ -8,34 +8,34 @@ namespace declarative_net_request { const char kErrorResourceTypeDuplicated[] = - "*: Rule at index * includes and excludes the same resource."; + "*: Rule with id * includes and excludes the same resource."; const char kErrorEmptyRedirectRuleKey[] = - "*: Rule at index * does not specify the value for * key. This is required " + "*: Rule with id * does not specify the value for * key. This is required " "for redirect rules."; const char kErrorInvalidRuleKey[] = - "*: Rule at index * has an invalid value for * key. This should be greater " + "*: Rule with id * has an invalid value for * key. This should be greater " "than or equal to *."; const char kErrorNoApplicableResourceTypes[] = - "*: Rule at index * is not applicable to any resource type."; + "*: Rule with id * is not applicable to any resource type."; const char kErrorEmptyList[] = - "*: Rule at index * cannot have an empty list as the value for * key."; + "*: Rule with id * cannot have an empty list as the value for * key."; const char kErrorEmptyUrlFilter[] = - "*: Rule at index * cannot have an empty value for * key."; + "*: Rule with id * cannot have an empty value for * key."; const char kErrorInvalidRedirectUrl[] = - "*: Rule at index * does not provide a valid URL for * key."; + "*: Rule with id * does not provide a valid URL for * key."; const char kErrorDuplicateIDs[] = - "*: Rule at index * does not have a unique ID."; + "*: Rule with id * does not have a unique ID."; // Don't surface the actual error to the user, since it's an implementation // detail. const char kErrorPersisting[] = "*: Rules file could not be parsed."; const char kErrorListNotPassed[] = "*: Rules file must contain a list."; const char kErrorNonAscii[] = - "*: Rule at index * cannot have non-ascii characters as part of \"*\" key."; + "*: Rule with id * cannot have non-ascii characters as part of \"*\" key."; const char kRuleCountExceeded[] = "Declarative Net Request: Rule count exceeded. Some rules were ignored."; const char kRuleNotParsedWarning[] = - "Declarative Net Request: Rule at index * couldn't be parsed. Parse error: " + "Declarative Net Request: Rule with * couldn't be parsed. Parse error: " "*."; const char kTooManyParseFailuresWarning[] = "Declarative Net Request: Too many rule parse failures; Reporting the "
diff --git a/extensions/browser/api/declarative_net_request/parse_info.cc b/extensions/browser/api/declarative_net_request/parse_info.cc index 7f39d50..356b5d8 100644 --- a/extensions/browser/api/declarative_net_request/parse_info.cc +++ b/extensions/browser/api/declarative_net_request/parse_info.cc
@@ -12,16 +12,16 @@ namespace declarative_net_request { ParseInfo::ParseInfo(ParseResult result) : result_(result) {} -ParseInfo::ParseInfo(ParseResult result, size_t rule_index) - : result_(result), rule_index_(rule_index) {} +ParseInfo::ParseInfo(ParseResult result, int rule_id) + : result_(result), rule_id_(rule_id) {} ParseInfo::ParseInfo(const ParseInfo&) = default; ParseInfo& ParseInfo::operator=(const ParseInfo&) = default; std::string ParseInfo::GetErrorDescription( const base::StringPiece json_rules_filename) const { // Every error except ERROR_PERSISTING_RULESET and ERROR_LIST_NOT_PASSED - // requires |rule_index_|. - DCHECK_EQ(!rule_index_.has_value(), + // requires |rule_id_|. + DCHECK_EQ(!rule_id_.has_value(), result_ == ParseResult::ERROR_PERSISTING_RULESET || result_ == ParseResult::ERROR_LIST_NOT_PASSED); @@ -33,58 +33,56 @@ case ParseResult::ERROR_RESOURCE_TYPE_DUPLICATED: error = ErrorUtils::FormatErrorMessage(kErrorResourceTypeDuplicated, json_rules_filename, - std::to_string(*rule_index_)); + std::to_string(*rule_id_)); break; case ParseResult::ERROR_EMPTY_REDIRECT_RULE_PRIORITY: error = ErrorUtils::FormatErrorMessage( kErrorEmptyRedirectRuleKey, json_rules_filename, - std::to_string(*rule_index_), kPriorityKey); + std::to_string(*rule_id_), kPriorityKey); break; case ParseResult::ERROR_EMPTY_REDIRECT_URL: error = ErrorUtils::FormatErrorMessage( kErrorEmptyRedirectRuleKey, json_rules_filename, - std::to_string(*rule_index_), kRedirectUrlKey); + std::to_string(*rule_id_), kRedirectUrlKey); break; case ParseResult::ERROR_INVALID_RULE_ID: error = ErrorUtils::FormatErrorMessage( - kErrorInvalidRuleKey, json_rules_filename, - std::to_string(*rule_index_), kIDKey, std::to_string(kMinValidID)); + kErrorInvalidRuleKey, json_rules_filename, std::to_string(*rule_id_), + kIDKey, std::to_string(kMinValidID)); break; case ParseResult::ERROR_INVALID_REDIRECT_RULE_PRIORITY: error = ErrorUtils::FormatErrorMessage( - kErrorInvalidRuleKey, json_rules_filename, - std::to_string(*rule_index_), kPriorityKey, - std::to_string(kMinValidPriority)); + kErrorInvalidRuleKey, json_rules_filename, std::to_string(*rule_id_), + kPriorityKey, std::to_string(kMinValidPriority)); break; case ParseResult::ERROR_NO_APPLICABLE_RESOURCE_TYPES: error = ErrorUtils::FormatErrorMessage(kErrorNoApplicableResourceTypes, json_rules_filename, - std::to_string(*rule_index_)); + std::to_string(*rule_id_)); break; case ParseResult::ERROR_EMPTY_DOMAINS_LIST: error = ErrorUtils::FormatErrorMessage( - kErrorEmptyList, json_rules_filename, std::to_string(*rule_index_), + kErrorEmptyList, json_rules_filename, std::to_string(*rule_id_), kDomainsKey); break; case ParseResult::ERROR_EMPTY_RESOURCE_TYPES_LIST: error = ErrorUtils::FormatErrorMessage( - kErrorEmptyList, json_rules_filename, std::to_string(*rule_index_), + kErrorEmptyList, json_rules_filename, std::to_string(*rule_id_), kResourceTypesKey); break; case ParseResult::ERROR_EMPTY_URL_FILTER: error = ErrorUtils::FormatErrorMessage( - kErrorEmptyUrlFilter, json_rules_filename, - std::to_string(*rule_index_), kUrlFilterKey); + kErrorEmptyUrlFilter, json_rules_filename, std::to_string(*rule_id_), + kUrlFilterKey); break; case ParseResult::ERROR_INVALID_REDIRECT_URL: error = ErrorUtils::FormatErrorMessage( kErrorInvalidRedirectUrl, json_rules_filename, - std::to_string(*rule_index_), kRedirectUrlKey); + std::to_string(*rule_id_), kRedirectUrlKey); break; case ParseResult::ERROR_DUPLICATE_IDS: - error = ErrorUtils::FormatErrorMessage(kErrorDuplicateIDs, - json_rules_filename, - std::to_string(*rule_index_)); + error = ErrorUtils::FormatErrorMessage( + kErrorDuplicateIDs, json_rules_filename, std::to_string(*rule_id_)); break; case ParseResult::ERROR_PERSISTING_RULESET: error = @@ -96,17 +94,17 @@ break; case ParseResult::ERROR_NON_ASCII_URL_FILTER: error = ErrorUtils::FormatErrorMessage( - kErrorNonAscii, json_rules_filename, std::to_string(*rule_index_), + kErrorNonAscii, json_rules_filename, std::to_string(*rule_id_), kUrlFilterKey); break; case ParseResult::ERROR_NON_ASCII_DOMAIN: error = ErrorUtils::FormatErrorMessage( - kErrorNonAscii, json_rules_filename, std::to_string(*rule_index_), + kErrorNonAscii, json_rules_filename, std::to_string(*rule_id_), kDomainsKey); break; case ParseResult::ERROR_NON_ASCII_EXCLUDED_DOMAIN: error = ErrorUtils::FormatErrorMessage( - kErrorNonAscii, json_rules_filename, std::to_string(*rule_index_), + kErrorNonAscii, json_rules_filename, std::to_string(*rule_id_), kExcludedDomainsKey); break; }
diff --git a/extensions/browser/api/declarative_net_request/parse_info.h b/extensions/browser/api/declarative_net_request/parse_info.h index aae037c..e59010d 100644 --- a/extensions/browser/api/declarative_net_request/parse_info.h +++ b/extensions/browser/api/declarative_net_request/parse_info.h
@@ -15,12 +15,12 @@ namespace extensions { namespace declarative_net_request { -// Holds the ParseResult together with the index of the rule at which the error +// Holds the ParseResult together with the id of the rule at which the error // occurred, if any. class ParseInfo { public: explicit ParseInfo(ParseResult result); - ParseInfo(ParseResult result, size_t rule_index); + ParseInfo(ParseResult result, int rule_id); ParseInfo(const ParseInfo&); ParseInfo& operator=(const ParseInfo&); @@ -33,9 +33,9 @@ private: ParseResult result_; - // When set, denotes the index of the rule with which the |result_| is + // When set, denotes the id of the rule with which the |result_| is // associated. - base::Optional<size_t> rule_index_; + base::Optional<int> rule_id_; }; } // namespace declarative_net_request
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc index c90942a9..9b2fa30b 100644 --- a/extensions/browser/api/declarative_net_request/utils.cc +++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -184,9 +184,20 @@ if (!parsed_rule || !parse_error.empty()) { if (unparsed_warning_count < kMaxUnparsedRulesWarnings) { ++unparsed_warning_count; + std::string rule_location; + + // If possible use the rule ID in the install warning. + if (auto* id_val = rules_list[i].FindKeyOfType( + kIDKey, base::Value::Type::INTEGER)) { + rule_location = base::StringPrintf("id %d", id_val->GetInt()); + } else { + // Use one-based indices. + rule_location = base::StringPrintf("index %zu", i + 1); + } + warnings->push_back( CreateInstallWarning(ErrorUtils::FormatErrorMessage( - kRuleNotParsedWarning, std::to_string(i), + kRuleNotParsedWarning, rule_location, base::UTF16ToUTF8(parse_error)))); } else { unparsed_warnings_limit_exeeded = true; @@ -194,15 +205,16 @@ continue; } - bool inserted = id_set.insert(parsed_rule->id).second; + int rule_id = parsed_rule->id; + bool inserted = id_set.insert(rule_id).second; if (!inserted) - return ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, i); + return ParseInfo(ParseResult::ERROR_DUPLICATE_IDS, rule_id); IndexedRule indexed_rule; ParseResult parse_result = IndexedRule::CreateIndexedRule(std::move(parsed_rule), &indexed_rule); if (parse_result != ParseResult::SUCCESS) - return ParseInfo(parse_result, i); + return ParseInfo(parse_result, rule_id); if (indexer.indexed_rules_count() >= kRuleCountLimit) { rule_count_exceeded = true;
diff --git a/extensions/browser/api/messaging/extension_message_port.cc b/extensions/browser/api/messaging/extension_message_port.cc index cbb583d..bd8bd697 100644 --- a/extensions/browser/api/messaging/extension_message_port.cc +++ b/extensions/browser/api/messaging/extension_message_port.cc
@@ -18,6 +18,7 @@ #include "extensions/browser/process_manager.h" #include "extensions/browser/process_manager_observer.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/extension_messages.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -207,7 +208,7 @@ int source_frame_id, int guest_process_id, int guest_render_frame_routing_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url) { ExtensionMsg_TabConnectionInfo source; @@ -217,7 +218,7 @@ ExtensionMsg_ExternalConnectionInfo info; info.target_id = target_extension_id; - info.source_id = source_extension_id; + info.source_endpoint = source_endpoint; info.source_url = source_url; info.guest_process_id = guest_process_id; info.guest_render_frame_routing_id = guest_render_frame_routing_id;
diff --git a/extensions/browser/api/messaging/extension_message_port.h b/extensions/browser/api/messaging/extension_message_port.h index 606cfe28..cc7582fd 100644 --- a/extensions/browser/api/messaging/extension_message_port.h +++ b/extensions/browser/api/messaging/extension_message_port.h
@@ -61,7 +61,7 @@ int source_frame_id, int guest_process_id, int guest_render_frame_routing_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url) override; void DispatchOnDisconnect(const std::string& error_message) override;
diff --git a/extensions/browser/api/messaging/message_port.cc b/extensions/browser/api/messaging/message_port.cc index 115e8a9..20eea06 100644 --- a/extensions/browser/api/messaging/message_port.cc +++ b/extensions/browser/api/messaging/message_port.cc
@@ -21,7 +21,7 @@ int source_frame_id, int guest_process_id, int guest_render_frame_routing_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url) {}
diff --git a/extensions/browser/api/messaging/message_port.h b/extensions/browser/api/messaging/message_port.h index 9c8f7a5..676a4b0 100644 --- a/extensions/browser/api/messaging/message_port.h +++ b/extensions/browser/api/messaging/message_port.h
@@ -19,6 +19,7 @@ namespace extensions { struct Message; +struct MessagingEndpoint; struct PortId; // One side of the communication handled by extensions::MessageService. @@ -58,7 +59,7 @@ int source_frame_id, int guest_process_id, int guest_render_frame_routing_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url);
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc index 8258afb..7d64865 100644 --- a/extensions/browser/api/messaging/message_service.cc +++ b/extensions/browser/api/messaging/message_service.cc
@@ -39,6 +39,7 @@ #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/guest_view/web_view/web_view_guest.h" #include "extensions/browser/process_manager.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/extension.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -80,7 +81,7 @@ int source_frame_id; std::unique_ptr<MessagePort> receiver; PortId receiver_port_id; - std::string source_extension_id; + MessagingEndpoint source_endpoint; std::string target_extension_id; GURL source_url; std::string channel_name; @@ -93,7 +94,7 @@ int source_frame_id, MessagePort* receiver, const PortId& receiver_port_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url, const std::string& channel_name, @@ -103,7 +104,7 @@ source_frame_id(source_frame_id), receiver(receiver), receiver_port_id(receiver_port_id), - source_extension_id(source_extension_id), + source_endpoint(source_endpoint), target_extension_id(target_extension_id), source_url(source_url), channel_name(channel_name), @@ -161,12 +162,16 @@ int source_process_id, int source_routing_id, const PortId& source_port_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url, const std::string& channel_name) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(source_port_id.is_opener); + DCHECK(!target_extension_id.empty()); + DCHECK_NE(source_endpoint.type, MessagingEndpoint::Type::kNativeApp); + DCHECK(source_endpoint.extension_id.has_value() || + source_endpoint.type == MessagingEndpoint::Type::kTab); content::RenderFrameHost* source_render_frame_host = content::RenderFrameHost::FromID(source_process_id, source_routing_id); @@ -188,7 +193,9 @@ bool is_web_connection = false; - if (source_extension_id != target_extension_id) { + if ((source_endpoint.type == MessagingEndpoint::Type::kTab || + source_endpoint.type == MessagingEndpoint::Type::kExtension) && + source_endpoint.extension_id != target_extension_id) { // It's an external connection. Check the externally_connectable manifest // key if it's present. If it's not, we allow connection from any extension // but not webpages. @@ -199,21 +206,20 @@ bool is_externally_connectable = false; if (externally_connectable) { - if (source_extension_id.empty()) { - // No source extension ID so the source was a web page. Check that the - // URL matches. + if (source_endpoint.extension_id) { + // The source was an extension or a content script. Check that the + // extension ID matches. + is_externally_connectable = + externally_connectable->IdCanConnect(*source_endpoint.extension_id); + } else { + // Check that the web page URL matches. is_web_connection = true; is_externally_connectable = externally_connectable->matches.MatchesURL(source_url); - } else { - // Source extension ID so the source was an extension. Check that the - // extension matches. - is_externally_connectable = - externally_connectable->IdCanConnect(source_extension_id); } } else { - // Default behaviour. Any extension, no webpages. - is_externally_connectable = !source_extension_id.empty(); + // Default behaviour. Any extension or content script, no webpages. + is_externally_connectable = source_endpoint.extension_id.has_value(); } if (!is_externally_connectable) { @@ -254,7 +260,7 @@ std::unique_ptr<OpenChannelParams> params(new OpenChannelParams( source_process_id, source_routing_id, std::move(source_tab), - source_frame_id, nullptr, receiver_port_id, source_extension_id, + source_frame_id, nullptr, receiver_port_id, source_endpoint, target_extension_id, source_url, channel_name, include_guest_process_info)); @@ -441,7 +447,8 @@ // sense // for opening to tabs. -1, // If there is no tab, then there is no frame either. - receiver.release(), receiver_port_id, extension_id, extension_id, + receiver.release(), receiver_port_id, + MessagingEndpoint::ForExtension(extension_id), extension_id, GURL(), // Source URL doesn't make sense for opening to tabs. channel_name, false)); // Connections to tabs aren't webview guests. @@ -470,7 +477,10 @@ std::unique_ptr<ExtensionMessagePort> opener(new ExtensionMessagePort( weak_factory_.GetWeakPtr(), params->receiver_port_id.GetOppositePortId(), - params->source_extension_id, source, false)); + params->source_endpoint.extension_id + ? *params->source_endpoint.extension_id + : ExtensionId(), + source, false)); if (!opener->IsValidPort()) return; opener->OpenPort(params->source_process_id, params->source_routing_id); @@ -504,8 +514,7 @@ channel->receiver->DispatchOnConnect( params->channel_name, std::move(params->source_tab), params->source_frame_id, guest_process_id, guest_render_frame_routing_id, - params->source_extension_id, params->target_extension_id, - params->source_url); + params->source_endpoint, params->target_extension_id, params->source_url); // Report the event to the event router, if the target is an extension. // @@ -523,7 +532,7 @@ if (target_extension) { events::HistogramValue histogram_value = events::UNKNOWN; bool is_external = - params->source_extension_id != params->target_extension_id; + params->source_endpoint.extension_id != params->target_extension_id; if (params->channel_name == "chrome.runtime.onRequest") { histogram_value = is_external ? events::RUNTIME_ON_REQUEST_EXTERNAL : events::RUNTIME_ON_REQUEST;
diff --git a/extensions/browser/api/messaging/message_service.h b/extensions/browser/api/messaging/message_service.h index f9f746d..90c421a8 100644 --- a/extensions/browser/api/messaging/message_service.h +++ b/extensions/browser/api/messaging/message_service.h
@@ -33,6 +33,7 @@ class Extension; class ExtensionHost; class MessagingDelegate; +struct MessagingEndpoint; // This class manages message and event passing between renderer processes. // It maintains a list of processes that are listening to events and a set of @@ -81,7 +82,7 @@ void OpenChannelToExtension(int source_process_id, int source_routing_id, const PortId& source_port_id, - const std::string& source_extension_id, + const MessagingEndpoint& source_endpoint, const std::string& target_extension_id, const GURL& source_url, const std::string& channel_name);
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc index f8258eef..f9e7896 100644 --- a/extensions/browser/api/web_request/web_request_api.cc +++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -145,14 +145,6 @@ keys::kOnHeadersReceivedEvent, }; -// List of all webRequest events that support extraHeaders in the extraInfoSpec. -const char* const kWebRequestExtraHeadersEventNames[] = { - keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent, - keys::kOnHeadersReceivedEvent, keys::kOnAuthRequiredEvent, - keys::kOnResponseStartedEvent, keys::kOnBeforeRedirectEvent, - keys::kOnCompletedEvent, -}; - // User data key for WebRequestAPI::ProxySet. const void* const kWebRequestProxySetUserDataKey = &kWebRequestProxySetUserDataKey; @@ -997,8 +989,7 @@ } request_time_tracker_->LogRequestStartTime( request->id, base::TimeTicks::Now(), has_listener, - HasExtraHeadersListenerForRequest(browser_context, extension_info_map, - request)); + HasExtraHeadersListener(browser_context, extension_info_map, request)); const bool is_incognito_context = IsIncognitoBrowserContext(browser_context); @@ -1740,12 +1731,18 @@ callbacks_for_page_load_.push_back(callback); } -bool ExtensionWebRequestEventRouter::HasExtraHeadersListenerForRequest( +bool ExtensionWebRequestEventRouter::HasExtraHeadersListener( void* browser_context, const extensions::InfoMap* extension_info_map, const WebRequestInfo* request) { + static const char* kEventNames[] = { + keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent, + keys::kOnHeadersReceivedEvent, keys::kOnAuthRequiredEvent, + keys::kOnResponseStartedEvent, keys::kOnBeforeRedirectEvent, + keys::kOnCompletedEvent, + }; int extra_info_spec = 0; - for (const char* name : kWebRequestExtraHeadersEventNames) { + for (const char* name : kEventNames) { GetMatchingListeners(browser_context, extension_info_map, name, request, &extra_info_spec); if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS) @@ -1754,30 +1751,6 @@ return false; } -bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListener( - void* browser_context) { - if (HasAnyExtraHeadersListenerImpl(browser_context)) - return true; - - void* cross_browser_context = GetCrossBrowserContext(browser_context); - if (cross_browser_context) - return HasAnyExtraHeadersListenerImpl(cross_browser_context); - - return false; -} - -bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListenerImpl( - void* browser_context) { - for (const char* name : kWebRequestExtraHeadersEventNames) { - const Listeners& listeners = listeners_[browser_context][name]; - for (const auto& listener : listeners) { - if (listener->extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS) - return true; - } - } - return false; -} - bool ExtensionWebRequestEventRouter::IsPageLoad( const WebRequestInfo& request) const { return request.type == content::RESOURCE_TYPE_MAIN_FRAME;
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h index 1e525d5..26f6563 100644 --- a/extensions/browser/api/web_request/web_request_api.h +++ b/extensions/browser/api/web_request/web_request_api.h
@@ -480,14 +480,9 @@ // Whether there is a listener matching the request that has // ExtraInfoSpec::EXTRA_HEADERS set. - bool HasExtraHeadersListenerForRequest( - void* browser_context, - const extensions::InfoMap* extension_info_map, - const WebRequestInfo* request); - - // Whether there are any listeners for this context that have - // ExtraInfoSpec::EXTRA_HEADERS set. - bool HasAnyExtraHeadersListener(void* browser_context); + bool HasExtraHeadersListener(void* browser_context, + const extensions::InfoMap* extension_info_map, + const WebRequestInfo* request); private: friend class WebRequestAPI; @@ -704,9 +699,6 @@ // Returns true if |request| was already signaled to some event handlers. bool WasSignaled(const WebRequestInfo& request) const; - // Helper for |HasAnyExtraHeadersListener()|. - bool HasAnyExtraHeadersListenerImpl(void* browser_context); - // Get the number of listeners - for testing only. size_t GetListenerCountForTesting(void* browser_context, const std::string& event_name);
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc index a8c522b..75ad640 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -22,22 +22,6 @@ namespace extensions { -struct WebRequestProxyingURLLoaderFactory::InProgressRequest:: - PendingBeforeSendHeadersData { - PendingBeforeSendHeadersData(const net::HttpRequestHeaders& headers_, - OnBeforeSendHeadersCallback callback_) - : headers(headers_), callback(std::move(callback_)) {} - PendingBeforeSendHeadersData(PendingBeforeSendHeadersData&&) = default; - - ~PendingBeforeSendHeadersData() { - if (callback) - std::move(callback).Run(net::ERR_ABORTED, base::nullopt); - } - - net::HttpRequestHeaders headers; - OnBeforeSendHeadersCallback callback; -}; - WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest( WebRequestProxyingURLLoaderFactory* factory, uint64_t request_id, @@ -58,9 +42,6 @@ proxied_loader_binding_(this, std::move(loader_request)), target_client_(std::move(client)), proxied_client_binding_(this), - has_any_extra_headers_listeners_( - ExtensionWebRequestEventRouter::GetInstance() - ->HasAnyExtraHeadersListener(factory_->browser_context_)), weak_factory_(this) { // If there is a client error, clean up the request. target_client_.set_connection_error_handler(base::BindOnce( @@ -88,7 +69,6 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::Restart() { request_completed_ = false; - network_request_started_ = false; // Derive a new WebRequestInfo value any time |Restart()| is called, because // the details in |request_| may have changed e.g. if we've been redirected. info_.emplace( @@ -98,18 +78,17 @@ routing_id_, factory_->resource_context_, request_, !(options_ & network::mojom::kURLLoadOptionSynchronous)); - current_request_uses_header_client_ = + uses_header_client_ = factory_->header_client_binding_ && request_.url.SchemeIsHTTPOrHTTPS() && network_service_request_id_ != 0 && - ExtensionWebRequestEventRouter::GetInstance() - ->HasExtraHeadersListenerForRequest( - factory_->browser_context_, factory_->info_map_, &info_.value()); + ExtensionWebRequestEventRouter::GetInstance()->HasExtraHeadersListener( + factory_->browser_context_, factory_->info_map_, &info_.value()); // If the header client will be used, we start the request immediately, and // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise, // send these events before the request starts. base::RepeatingCallback<void(int)> continuation; - if (current_request_uses_header_client_) { + if (uses_header_client_) { continuation = base::BindRepeating( &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr()); } else { @@ -196,7 +175,7 @@ const network::ResourceResponseHead& head) { current_response_ = head; on_receive_response_received_ = true; - if (current_request_uses_header_client_) { + if (uses_header_client_) { ContinueToResponseStarted(net::OK); } else { HandleResponseOrRedirectHeaders( @@ -216,7 +195,7 @@ } current_response_ = head; - if (current_request_uses_header_client_) { + if (uses_header_client_) { ContinueToBeforeRedirect(redirect_info, net::OK); } else { HandleResponseOrRedirectHeaders( @@ -290,19 +269,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders( const net::HttpRequestHeaders& headers, OnBeforeSendHeadersCallback callback) { - if (!current_request_uses_header_client_) { - std::move(callback).Run(net::OK, base::nullopt); - return; - } - - if (!network_request_started_) { - DCHECK(!pending_before_send_headers_data_); - pending_before_send_headers_data_ = - std::make_unique<PendingBeforeSendHeadersData>(headers, - std::move(callback)); - return; - } - + DCHECK(uses_header_client_); request_.headers = headers; on_before_send_headers_callback_ = std::move(callback); ContinueToBeforeSendHeaders(net::OK); @@ -311,11 +278,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived( const std::string& headers, OnHeadersReceivedCallback callback) { - if (!current_request_uses_header_client_) { - std::move(callback).Run(net::OK, base::nullopt, GURL()); - return; - } - + DCHECK(uses_header_client_); on_headers_received_callback_ = std::move(callback); current_response_.headers = base::MakeRefCounted<net::HttpResponseHeaders>(headers); @@ -381,7 +344,7 @@ return; } - if (!current_request_uses_header_client_ && !redirect_url_.is_empty()) { + if (!uses_header_client_ && !redirect_url_.is_empty()) { HandleBeforeRequestRedirect(); return; } @@ -426,17 +389,12 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest:: ContinueToStartRequest(int error_code) { - // Move to a local to make sure this gets destroyed at the end of this - // function if it isn't used. - std::unique_ptr<PendingBeforeSendHeadersData> before_send_headers_data = - std::move(pending_before_send_headers_data_); - if (error_code != net::OK) { OnRequestError(network::URLLoaderCompletionStatus(error_code)); return; } - if (current_request_uses_header_client_ && !redirect_url_.is_empty()) { + if (uses_header_client_ && !redirect_url_.is_empty()) { HandleBeforeRequestRedirect(); return; } @@ -444,24 +402,18 @@ if (proxied_client_binding_.is_bound()) proxied_client_binding_.ResumeIncomingMethodCallProcessing(); - network_request_started_ = true; if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) { // No extensions have cancelled us up to this point, so it's now OK to // initiate the real network request. network::mojom::URLLoaderClientPtr proxied_client; proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client)); uint32_t options = options_; - // Even if this request does not use the header client, future redirects - // might, so we need to set the option on the loader. - if (has_any_extra_headers_listeners_) + if (uses_header_client_) options |= network::mojom::kURLLoadOptionUseHeaderClient; factory_->target_factory_->CreateLoaderAndStart( mojo::MakeRequest(&target_loader_), info_->routing_id, network_service_request_id_, options, request_, std::move(proxied_client), traffic_annotation_); - } else if (before_send_headers_data) { - OnBeforeSendHeaders(before_send_headers_data->headers, - std::move(before_send_headers_data->callback)); } // From here the lifecycle of this request is driven by subsequent events on @@ -476,7 +428,7 @@ return; } - if (current_request_uses_header_client_) { + if (uses_header_client_) { DCHECK(on_before_send_headers_callback_); std::move(on_before_send_headers_callback_) .Run(error_code, request_.headers); @@ -494,7 +446,7 @@ request_.headers); } - if (!current_request_uses_header_client_) + if (!uses_header_client_) ContinueToStartRequest(net::OK); } @@ -590,7 +542,7 @@ return; } - DCHECK(!current_request_uses_header_client_ || !override_headers_); + DCHECK(!uses_header_client_ || !override_headers_); if (override_headers_) current_response_.headers = override_headers_;
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h index 17cb6f6..1ca71ad 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -153,32 +153,15 @@ bool request_completed_ = false; - // If |has_any_extra_headers_listeners_| is set to true, the request will be - // sent with the network::mojom::kURLLoadOptionUseHeaderClient option, and - // we expect events to come through the - // network::mojom::TrustedURLLoaderHeaderClient binding on the factory. This - // is only set to true if there is a listener that needs to view or modify - // headers set in the network process. - bool has_any_extra_headers_listeners_ = false; - bool current_request_uses_header_client_ = false; + // If |uses_header_client_| is set to true, the request will be sent with + // the network::mojom::kURLLoadOptionUseHeaderClient option, and we expect + // events to come through the network::mojom::TrustedURLLoaderHeaderClient + // binding on the factory. This is only set to true if there is a listener + // that needs to view or modify headers set in the network process. + bool uses_header_client_ = false; OnBeforeSendHeadersCallback on_before_send_headers_callback_; OnHeadersReceivedCallback on_headers_received_callback_; - // If extraHeaders listeners are present, we may receive the - // OnBeforeSendHeaders event for a redirect before OnBeforeRequest has been - // run on that URL. In that case we need to queue up this event until - // OnBeforeRequest has been completed and we know the request will not be - // canceled/redirected there. - struct PendingBeforeSendHeadersData; - std::unique_ptr<PendingBeforeSendHeadersData> - pending_before_send_headers_data_; - - // Whether the request has gone to the network yet. If an - // OnBeforeSendHeaders event reaches us before the request has gone to the - // network, it should be saved for later in - // |pending_before_send_headers_data_|. - bool network_request_started_ = false; - base::WeakPtrFactory<InProgressRequest> weak_factory_; DISALLOW_COPY_AND_ASSIGN(InProgressRequest);
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc index 6d58a49..22f794c 100644 --- a/extensions/browser/extension_message_filter.cc +++ b/extensions/browser/extension_message_filter.cc
@@ -19,6 +19,7 @@ #include "extensions/browser/process_manager_factory.h" #include "extensions/browser/process_map.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/extension.h" #include "extensions/common/extension_messages.h" @@ -390,7 +391,7 @@ if (browser_context_) { MessageService::Get(browser_context_) ->OpenChannelToExtension(render_process_id_, routing_id, port_id, - info.source_id, info.target_id, + info.source_endpoint, info.target_id, info.source_url, channel_name); } }
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index 9f0adbd..593b8f9c 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -87,6 +87,8 @@ "api/declarative_net_request/utils.cc", "api/declarative_net_request/utils.h", "api/messaging/message.h", + "api/messaging/messaging_endpoint.cc", + "api/messaging/messaging_endpoint.h", "api/messaging/port_id.cc", "api/messaging/port_id.h", "api/printer_provider/usb_printer_manifest_data.cc",
diff --git a/extensions/common/api/messaging/messaging_endpoint.cc b/extensions/common/api/messaging/messaging_endpoint.cc new file mode 100644 index 0000000..6ae7dcc9 --- /dev/null +++ b/extensions/common/api/messaging/messaging_endpoint.cc
@@ -0,0 +1,53 @@ +// Copyright 2019 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 "extensions/common/api/messaging/messaging_endpoint.h" + +#include <utility> + +namespace extensions { + +// static +MessagingEndpoint MessagingEndpoint::ForExtension(ExtensionId extension_id) { + MessagingEndpoint messaging_endpoint; + messaging_endpoint.type = MessagingEndpoint::Type::kExtension; + messaging_endpoint.extension_id = std::move(extension_id); + return messaging_endpoint; +} + +// static +MessagingEndpoint MessagingEndpoint::ForContentScript( + ExtensionId extension_id) { + MessagingEndpoint messaging_endpoint; + messaging_endpoint.type = MessagingEndpoint::Type::kTab; + messaging_endpoint.extension_id = std::move(extension_id); + return messaging_endpoint; +} + +// static +MessagingEndpoint MessagingEndpoint::ForWebPage() { + MessagingEndpoint messaging_endpoint; + messaging_endpoint.type = MessagingEndpoint::Type::kTab; + return messaging_endpoint; +} + +// static +MessagingEndpoint MessagingEndpoint::ForNativeApp() { + MessagingEndpoint messaging_endpoint; + messaging_endpoint.type = MessagingEndpoint::Type::kNativeApp; + return messaging_endpoint; +} + +MessagingEndpoint::MessagingEndpoint() = default; + +MessagingEndpoint::MessagingEndpoint(const MessagingEndpoint&) = default; + +MessagingEndpoint::MessagingEndpoint(MessagingEndpoint&&) = default; + +MessagingEndpoint& MessagingEndpoint::operator=(const MessagingEndpoint&) = + default; + +MessagingEndpoint::~MessagingEndpoint() = default; + +} // namespace extensions
diff --git a/extensions/common/api/messaging/messaging_endpoint.h b/extensions/common/api/messaging/messaging_endpoint.h new file mode 100644 index 0000000..1a2bd67 --- /dev/null +++ b/extensions/common/api/messaging/messaging_endpoint.h
@@ -0,0 +1,51 @@ +// Copyright 2019 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 EXTENSIONS_COMMON_API_MESSAGING_MESSAGING_ENDPOINT_H_ +#define EXTENSIONS_COMMON_API_MESSAGING_MESSAGING_ENDPOINT_H_ + +#include "base/optional.h" +#include "extensions/common/extension_id.h" + +namespace extensions { + +struct MessagingEndpoint { + // Type of the messaging source or destination - i.e., the type of the + // component which talks to a messaging channel. + enum class Type { + // An extension. + kExtension = 0, + // A web page or a content script or a hosted app. + kTab = 1, + // A native application. + kNativeApp = 2, + + // This item must be equal to the last actual enum item. + kLast = kNativeApp, + }; + + static MessagingEndpoint ForExtension(ExtensionId extension_id); + static MessagingEndpoint ForContentScript(ExtensionId extension_id); + static MessagingEndpoint ForWebPage(); + static MessagingEndpoint ForNativeApp(); + + MessagingEndpoint(); + MessagingEndpoint(const MessagingEndpoint&); + MessagingEndpoint(MessagingEndpoint&&); + + MessagingEndpoint& operator=(const MessagingEndpoint&); + + ~MessagingEndpoint(); + + Type type = Type::kExtension; + + // Identifier of the extension (or the content script). It is required for + // |type| of kExtension. For |type| of kTab, it is set if the endpoint is a + // content script (otherwise, it's the web page). + base::Optional<ExtensionId> extension_id; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_API_MESSAGING_MESSAGING_ENDPOINT_H_
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h index 3efca1e..96649cc8 100644 --- a/extensions/common/extension_messages.h +++ b/extensions/common/extension_messages.h
@@ -18,6 +18,7 @@ #include "content/public/common/common_param_traits.h" #include "content/public/common/socket_permission_request.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/common_param_traits.h" #include "extensions/common/constants.h" @@ -51,6 +52,9 @@ IPC_ENUM_TRAITS_MAX_VALUE(extensions::UserScript::RunLocation, extensions::UserScript::RUN_LOCATION_LAST - 1) +IPC_ENUM_TRAITS_MAX_VALUE(extensions::MessagingEndpoint::Type, + extensions::MessagingEndpoint::Type::kLast) + IPC_ENUM_TRAITS_MAX_VALUE(HostID::HostType, HostID::HOST_TYPE_LAST) // Parameters structure for ExtensionHostMsg_AddAPIActionToActivityLog and @@ -211,15 +215,19 @@ IPC_STRUCT_MEMBER(int, frame_id) IPC_STRUCT_END() +IPC_STRUCT_TRAITS_BEGIN(extensions::MessagingEndpoint) + IPC_STRUCT_TRAITS_MEMBER(type) + IPC_STRUCT_TRAITS_MEMBER(extension_id) +IPC_STRUCT_TRAITS_END() + // Struct containing the data for external connections to extensions. Used to // handle the IPCs initiated by both connect() and onConnect(). IPC_STRUCT_BEGIN(ExtensionMsg_ExternalConnectionInfo) // The ID of the extension that is the target of the request. IPC_STRUCT_MEMBER(std::string, target_id) - // The ID of the extension that initiated the request. May be empty if it - // wasn't initiated by an extension. - IPC_STRUCT_MEMBER(std::string, source_id) + // Specifies the type and the ID of the endpoint that initiated the request. + IPC_STRUCT_MEMBER(extensions::MessagingEndpoint, source_endpoint) // The URL of the frame that initiated the request. IPC_STRUCT_MEMBER(GURL, source_url)
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc index 897fc721..a641db3 100644 --- a/extensions/renderer/ipc_message_sender.cc +++ b/extensions/renderer/ipc_message_sender.cc
@@ -11,6 +11,7 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/worker_thread.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/constants.h" #include "extensions/common/extension_messages.h" #include "extensions/common/features/feature.h" @@ -158,8 +159,14 @@ switch (target.type) { case MessageTarget::EXTENSION: { ExtensionMsg_ExternalConnectionInfo info; - if (extension && !extension->is_hosted_app()) - info.source_id = extension->id(); + if (extension && !extension->is_hosted_app()) { + info.source_endpoint = + script_context->context_type() == Feature::CONTENT_SCRIPT_CONTEXT + ? MessagingEndpoint::ForContentScript(extension->id()) + : MessagingEndpoint::ForExtension(extension->id()); + } else { + info.source_endpoint = MessagingEndpoint::ForWebPage(); + } info.target_id = *target.extension_id; info.source_url = script_context->url();
diff --git a/extensions/renderer/js_renderer_messaging_service.cc b/extensions/renderer/js_renderer_messaging_service.cc index 2c5ff504..8f75d16 100644 --- a/extensions/renderer/js_renderer_messaging_service.cc +++ b/extensions/renderer/js_renderer_messaging_service.cc
@@ -82,11 +82,15 @@ } v8::Local<v8::String> v8_channel_name; - v8::Local<v8::String> v8_source_id; + v8::Local<v8::String> v8_source_extension_id; v8::Local<v8::String> v8_target_extension_id; v8::Local<v8::String> v8_source_url_spec; if (!ToV8String(isolate, channel_name.c_str(), &v8_channel_name) || - !ToV8String(isolate, info.source_id.c_str(), &v8_source_id) || + !ToV8String(isolate, + info.source_endpoint.extension_id + ? *info.source_endpoint.extension_id + : ExtensionId(), + &v8_source_extension_id) || !ToV8String(isolate, target_extension_id.c_str(), &v8_target_extension_id) || !ToV8String(isolate, source_url_spec.c_str(), &v8_source_url_spec)) { @@ -108,7 +112,7 @@ // guestRenderFrameRoutingId guest_render_frame_routing_id, // sourceExtensionId - v8_source_id, + v8_source_extension_id, // targetExtensionId v8_target_extension_id, // sourceUrl
diff --git a/extensions/renderer/messaging_bindings.cc b/extensions/renderer/messaging_bindings.cc index d31c5ffa..1a38ac07 100644 --- a/extensions/renderer/messaging_bindings.cc +++ b/extensions/renderer/messaging_bindings.cc
@@ -18,6 +18,7 @@ #include "base/values.h" #include "content/public/renderer/render_frame.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/extension_messages.h" #include "extensions/renderer/extension_frame_helper.h" @@ -188,14 +189,22 @@ ports_[js_id] = std::make_unique<ExtensionPort>(context(), port_id, js_id); ExtensionMsg_ExternalConnectionInfo info; + // For messaging APIs, hosted apps should be considered a web page so hide // its extension ID. const Extension* extension = context()->extension(); - if (extension && !extension->is_hosted_app()) - info.source_id = extension->id(); + if (extension && !extension->is_hosted_app()) { + info.source_endpoint = + context()->context_type() == Feature::CONTENT_SCRIPT_CONTEXT + ? MessagingEndpoint::ForContentScript(extension->id()) + : MessagingEndpoint::ForExtension(extension->id()); + } else { + info.source_endpoint = MessagingEndpoint::ForWebPage(); + } v8::Isolate* isolate = args.GetIsolate(); info.target_id = *v8::String::Utf8Value(isolate, args[0]); + info.source_url = context()->url(); std::string channel_name = *v8::String::Utf8Value(isolate, args[1]);
diff --git a/extensions/renderer/native_renderer_messaging_service.cc b/extensions/renderer/native_renderer_messaging_service.cc index 44471d5..42dee4d5 100644 --- a/extensions/renderer/native_renderer_messaging_service.cc +++ b/extensions/renderer/native_renderer_messaging_service.cc
@@ -12,6 +12,7 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/v8_value_converter.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/extension_messages.h" #include "extensions/common/manifest_handlers/externally_connectable.h" @@ -185,14 +186,16 @@ const ExtensionMsg_TabConnectionInfo* source, const ExtensionMsg_ExternalConnectionInfo& info, const std::string& event_name) { + DCHECK_NE(info.source_endpoint.type, MessagingEndpoint::Type::kNativeApp); + v8::Isolate* isolate = script_context->isolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Context> v8_context = script_context->v8_context(); v8::Context::Scope context_scope(v8_context); gin::DataObjectBuilder sender_builder(isolate); - if (!info.source_id.empty()) - sender_builder.Set("id", info.source_id); + if (info.source_endpoint.extension_id) + sender_builder.Set("id", *info.source_endpoint.extension_id); if (!info.source_url.is_empty()) sender_builder.Set("url", info.source_url.spec()); if (source->frame_id >= 0) @@ -242,8 +245,8 @@ std::make_unique<base::Value>(base::Value::Type::LIST); auto& list = activity_logging_args->GetList(); list.reserve(2u); - if (!info.source_id.empty()) - list.emplace_back(info.source_id); + if (info.source_endpoint.extension_id) + list.emplace_back(*info.source_endpoint.extension_id); else list.emplace_back();
diff --git a/extensions/renderer/native_renderer_messaging_service_unittest.cc b/extensions/renderer/native_renderer_messaging_service_unittest.cc index d3e64ca..9129486 100644 --- a/extensions/renderer/native_renderer_messaging_service_unittest.cc +++ b/extensions/renderer/native_renderer_messaging_service_unittest.cc
@@ -10,6 +10,7 @@ #include "base/stl_util.h" #include "components/crx_file/id_util.h" #include "content/public/common/child_process_host.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_messages.h" @@ -112,7 +113,8 @@ DictionaryBuilder().Set("tabId", tab_id).Build().get()); ExtensionMsg_ExternalConnectionInfo external_connection_info; external_connection_info.target_id = extension()->id(); - external_connection_info.source_id = extension()->id(); + external_connection_info.source_endpoint = + MessagingEndpoint::ForExtension(extension()->id()); external_connection_info.source_url = source_url; external_connection_info.guest_process_id = content::ChildProcessHost::kInvalidUniqueID; @@ -406,7 +408,8 @@ DictionaryBuilder().Set("tabId", tab_id).Build().get()); ExtensionMsg_ExternalConnectionInfo external_connection_info; external_connection_info.target_id = extension()->id(); - external_connection_info.source_id = extension()->id(); + external_connection_info.source_endpoint = + MessagingEndpoint::ForExtension(extension()->id()); external_connection_info.source_url = source_url; external_connection_info.guest_process_id = content::ChildProcessHost::kInvalidUniqueID; @@ -474,7 +477,8 @@ ExtensionMsg_ExternalConnectionInfo external_connection_info; external_connection_info.target_id = extension()->id(); - external_connection_info.source_id = source_id; + external_connection_info.source_endpoint = + MessagingEndpoint::ForExtension(source_id); external_connection_info.source_url = source_url; external_connection_info.guest_process_id = content::ChildProcessHost::kInvalidUniqueID;
diff --git a/extensions/renderer/renderer_messaging_service.cc b/extensions/renderer/renderer_messaging_service.cc index c68a13b..c0b5137 100644 --- a/extensions/renderer/renderer_messaging_service.cc +++ b/extensions/renderer/renderer_messaging_service.cc
@@ -16,6 +16,7 @@ #include "content/public/renderer/render_thread.h" #include "content/public/renderer/v8_value_converter.h" #include "extensions/common/api/messaging/message.h" +#include "extensions/common/api/messaging/messaging_endpoint.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/extension_messages.h" #include "extensions/renderer/extension_bindings_system.h" @@ -140,7 +141,7 @@ // First, determine the event we'll use to connect. std::string target_extension_id = script_context->GetExtensionID(); - bool is_external = info.source_id != target_extension_id; + bool is_external = info.source_endpoint.extension_id != target_extension_id; std::string event_name; if (channel_name == messaging_util::kSendRequestChannel) { event_name = is_external ? messaging_util::kOnRequestExternalEvent
diff --git a/extensions/renderer/resources/messaging.js b/extensions/renderer/resources/messaging.js index f32ddadd..acdd8a4d 100644 --- a/extensions/renderer/resources/messaging.js +++ b/extensions/renderer/resources/messaging.js
@@ -171,7 +171,7 @@ 'Cannot send a response more than once per chrome.' + eventName + ' listener per document'; } - errorMsg += ' (message was sent by extension' + sourceExtensionId; + errorMsg += ' (message was sent by extension ' + sourceExtensionId; if (sourceExtensionId && sourceExtensionId !== targetExtensionId) errorMsg += ' for extension ' + targetExtensionId; if (sourceUrl)
diff --git a/fuchsia/http/http_service_unittest.cc b/fuchsia/http/http_service_unittest.cc index 56c24994..fcea6be4 100644 --- a/fuchsia/http/http_service_unittest.cc +++ b/fuchsia/http/http_service_unittest.cc
@@ -5,7 +5,6 @@ #include <fuchsia/net/oldhttp/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> -#include "base/fuchsia/component_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_directory.h" #include "base/run_loop.h"
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc index bf57eb58..db21715 100644 --- a/fuchsia/runners/cast/cast_runner_integration_test.cc +++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -42,17 +42,14 @@ CastRunnerIntegrationTest() : run_timeout_(TestTimeouts::action_timeout()), cast_channel_binding_(this) { - // Create a new test ServiceDirectory, and a test ComponentContext - // connected to it, for the test to use to drive the CastRunner. - zx::channel service_directory_request, service_directory_client; - zx_status_t status = zx::channel::create(0, &service_directory_client, - &service_directory_request); - ZX_CHECK(status == ZX_OK, status) << "zx_channel_create"; - - test_service_directory_ = std::make_unique<base::fuchsia::ServiceDirectory>( - std::move(service_directory_request)); - test_component_context_ = std::make_unique<base::fuchsia::ComponentContext>( - std::move(service_directory_client)); + // Create a new test ServiceDirectory, and ServiceDirectoryClient connected + // to it, for tests to use to drive the CastRunner. + fidl::InterfaceHandle<fuchsia::io::Directory> directory; + test_services_ = std::make_unique<base::fuchsia::ServiceDirectory>( + directory.NewRequest()); + test_services_client_ = + std::make_unique<base::fuchsia::ServiceDirectoryClient>( + std::move(directory)); // Create the AppConfigManager. app_config_binding_ = std::make_unique< @@ -61,16 +58,15 @@ chromium::cast::ApplicationConfigManagerPtr app_config_manager_interface; app_config_binding_->Bind(app_config_manager_interface.NewRequest()); - // Create the CastRunner, published into |test_service_directory_|. + // Create the CastRunner, published into |test_services_|. cast_runner_ = std::make_unique<CastRunner>( - test_service_directory_.get(), - WebContentRunner::CreateDefaultWebContext(), + test_services_.get(), WebContentRunner::CreateDefaultWebContext(), std::move(app_config_manager_interface), cast_runner_run_loop_.QuitClosure()); // Connect to the CastRunner's fuchsia.sys.Runner interface. cast_runner_ptr_ = - test_component_context_->ConnectToService<fuchsia::sys::Runner>(); + test_services_client_->ConnectToService<fuchsia::sys::Runner>(); cast_runner_ptr_.set_error_handler([this](zx_status_t status) { ZX_LOG(ERROR, status) << "CastRunner closed channel."; ADD_FAILURE(); @@ -131,8 +127,8 @@ fidl::Binding<chromium::cast::CastChannel> cast_channel_binding_; // ServiceDirectory into which the CastRunner will publish itself. - std::unique_ptr<base::fuchsia::ServiceDirectory> test_service_directory_; - std::unique_ptr<base::fuchsia::ComponentContext> test_component_context_; + std::unique_ptr<base::fuchsia::ServiceDirectory> test_services_; + std::unique_ptr<base::fuchsia::ComponentContext> test_services_client_; std::unique_ptr<CastRunner> cast_runner_; fuchsia::sys::RunnerPtr cast_runner_ptr_; @@ -151,7 +147,7 @@ // Launch the test-app component. fuchsia::sys::ComponentControllerPtr component_controller_ptr; - base::fuchsia::ComponentContext component_services(StartCastComponent( + base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent( base::StringPrintf("cast:%s", kBlankAppId), &cast_runner_ptr_, component_controller_ptr.NewRequest(), &cast_channel_binding_)); component_controller_ptr.set_error_handler(&ComponentErrorHandler); @@ -193,7 +189,7 @@ TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) { // Launch the test-app component. fuchsia::sys::ComponentControllerPtr component_controller_ptr; - base::fuchsia::ComponentContext component_services(StartCastComponent( + base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent( "cast:99999999", &cast_runner_ptr_, component_controller_ptr.NewRequest(), &cast_channel_binding_)); component_controller_ptr.set_error_handler(&ComponentErrorHandler); @@ -214,7 +210,7 @@ // Launch the test-app component. fuchsia::sys::ComponentControllerPtr component_controller_ptr; - base::fuchsia::ComponentContext component_services(StartCastComponent( + base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent( base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_, component_controller_ptr.NewRequest(), &cast_channel_binding_)); component_controller_ptr.set_error_handler(&ComponentErrorHandler); @@ -274,7 +270,7 @@ // Launch the test-app component. fuchsia::sys::ComponentControllerPtr component_controller_ptr; - base::fuchsia::ComponentContext component_services(StartCastComponent( + base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent( base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_, component_controller_ptr.NewRequest(), &cast_channel_binding_)); @@ -297,7 +293,7 @@ // Launch the test-app component. fuchsia::sys::ComponentControllerPtr component_controller_ptr; - base::fuchsia::ComponentContext component_services(StartCastComponent( + base::fuchsia::ServiceDirectoryClient services_client(StartCastComponent( base::StringPrintf("cast:%s", kCastChannelAppId), &cast_runner_ptr_, component_controller_ptr.NewRequest(), &cast_channel_binding_));
diff --git a/fuchsia/runners/cast/main.cc b/fuchsia/runners/cast/main.cc index e04adb878..1cefdf6 100644 --- a/fuchsia/runners/cast/main.cc +++ b/fuchsia/runners/cast/main.cc
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/fuchsia/component_context.h" #include "base/fuchsia/service_directory.h" +#include "base/fuchsia/service_directory_client.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "fuchsia/runners/cast/cast_runner.h" @@ -15,7 +15,7 @@ CastRunner runner( base::fuchsia::ServiceDirectory::GetDefault(), WebContentRunner::CreateDefaultWebContext(), - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<chromium::cast::ApplicationConfigManager>(), run_loop.QuitClosure());
diff --git a/fuchsia/runners/cast/test_common.cc b/fuchsia/runners/cast/test_common.cc index 3b8b0c0454..0e166d7 100644 --- a/fuchsia/runners/cast/test_common.cc +++ b/fuchsia/runners/cast/test_common.cc
@@ -13,7 +13,7 @@ #include "base/fuchsia/service_directory.h" #include "base/run_loop.h" -zx::channel StartCastComponent( +fidl::InterfaceHandle<fuchsia::io::Directory> StartCastComponent( const base::StringPiece& cast_url, fuchsia::sys::RunnerPtr* sys_runner, fidl::InterfaceRequest<fuchsia::sys::ComponentController> @@ -22,40 +22,30 @@ // Construct, bind, and populate a ServiceDirectory for publishing // the CastChannel service to the CastComponent. auto service_list = std::make_unique<fuchsia::sys::ServiceList>(); - zx::channel cast_channel_dir_request, cast_channel_dir_client; - zx_status_t status = zx::channel::create(0, &cast_channel_dir_request, - &cast_channel_dir_client); - ZX_CHECK(status == ZX_OK, status) << "zx_channel_create"; + fidl::InterfaceHandle<fuchsia::io::Directory> directory; base::fuchsia::ServiceDirectory cast_channel_directory( - std::move(cast_channel_dir_request)); + directory.NewRequest()); base::RunLoop service_connect_runloop; - cast_channel_directory.AddService( - chromium::cast::CastChannel::Name_, - base::BindRepeating( - [](base::RepeatingClosure on_connect_cb, - fidl::Binding<chromium::cast::CastChannel>* cast_channel_binding_, - zx::channel channel) { - cast_channel_binding_->Bind(std::move(channel)); - on_connect_cb.Run(); - }, - base::Passed(service_connect_runloop.QuitClosure()), - base::Unretained(cast_channel_binding))); + cast_channel_directory.AddService(base::BindRepeating( + [](base::RepeatingClosure on_connect_cb, + fidl::Binding<chromium::cast::CastChannel>* cast_channel_binding_, + fidl::InterfaceRequest<chromium::cast::CastChannel> request) { + cast_channel_binding_->Bind(std::move(request)); + on_connect_cb.Run(); + }, + base::Passed(service_connect_runloop.QuitClosure()), + base::Unretained(cast_channel_binding))); service_list->names.push_back(chromium::cast::CastChannel::Name_); - service_list->host_directory = std::move(cast_channel_dir_client); + service_list->host_directory = directory.TakeChannel(); - fuchsia::sys::LaunchInfo launch_info; - launch_info.url = cast_url.as_string(); - launch_info.additional_services = std::move(service_list); - - // Create a channel to pass to the Runner, through which to expose the new - // component's ServiceDirectory. - zx::channel service_directory_client; - status = zx::channel::create(0, &service_directory_client, - &launch_info.directory_request); - ZX_CHECK(status == ZX_OK, status) << "zx_channel_create"; - + // Configure the Runner, including a service directory channel to publish + // services to. fuchsia::sys::StartupInfo startup_info; - startup_info.launch_info = std::move(launch_info); + startup_info.launch_info.url = cast_url.as_string(); + startup_info.launch_info.additional_services = std::move(service_list); + fidl::InterfaceHandle<fuchsia::io::Directory> component_services; + startup_info.launch_info.directory_request = + component_services.NewRequest().TakeChannel(); // The FlatNamespace vectors must be non-null, but may be empty. startup_info.flat_namespace.paths.resize(0); @@ -73,5 +63,5 @@ // Prepare the service directory for clean teardown. cast_channel_directory.RemoveAllServices(); - return service_directory_client; + return component_services; }
diff --git a/fuchsia/runners/cast/test_common.h b/fuchsia/runners/cast/test_common.h index 98dbac32..ec1c20fd 100644 --- a/fuchsia/runners/cast/test_common.h +++ b/fuchsia/runners/cast/test_common.h
@@ -11,12 +11,18 @@ #include "base/strings/string_piece.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h" +namespace fuchsia { +namespace io { +class Directory; +} +} // namespace fuchsia + // Starts a Cast component from the runner |sys_runner| with the URL |cast_url| // and returns the outgoing service directory client channel. // The Cast component will connect to the CastChannel FIDL service bound at // |cast_channel_binding|. // Blocks until |cast_channel_binding| is bound. -zx::channel StartCastComponent( +fidl::InterfaceHandle<fuchsia::io::Directory> StartCastComponent( const base::StringPiece& cast_url, fuchsia::sys::RunnerPtr* sys_runner, fidl::InterfaceRequest<fuchsia::sys::ComponentController>
diff --git a/fuchsia/runners/common/web_component.cc b/fuchsia/runners/common/web_component.cc index c86a450..b7cd32c 100644 --- a/fuchsia/runners/common/web_component.cc +++ b/fuchsia/runners/common/web_component.cc
@@ -47,8 +47,9 @@ if (startup_info.launch_info.additional_services && startup_info.launch_info.additional_services->host_directory) { additional_services_ = - std::make_unique<base::fuchsia::ComponentContext>(std::move( - startup_info.launch_info.additional_services->host_directory)); + std::make_unique<base::fuchsia::ServiceDirectoryClient>( + fidl::InterfaceHandle<fuchsia::io::Directory>(std::move( + startup_info.launch_info.additional_services->host_directory))); additional_service_names_ = std::move(startup_info.launch_info.additional_services->names); } @@ -75,7 +76,8 @@ // message-loop, to ensure that it is available before the ServiceDirectory // starts processing requests. service_directory_ = std::make_unique<base::fuchsia::ServiceDirectory>( - std::move(startup_info.launch_info.directory_request)); + fidl::InterfaceRequest<fuchsia::io::Directory>( + std::move(startup_info.launch_info.directory_request))); view_provider_binding_ = std::make_unique< base::fuchsia::ScopedServiceBinding<fuchsia::ui::app::ViewProvider>>( service_directory_.get(), this);
diff --git a/fuchsia/runners/common/web_component.h b/fuchsia/runners/common/web_component.h index 6d493835..09874bd 100644 --- a/fuchsia/runners/common/web_component.h +++ b/fuchsia/runners/common/web_component.h
@@ -15,9 +15,9 @@ #include <utility> #include <vector> -#include "base/fuchsia/component_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_directory.h" +#include "base/fuchsia/service_directory_client.h" #include "base/logging.h" #include "fuchsia/fidl/chromium/web/cpp/fidl.h" #include "url/gurl.h" @@ -73,14 +73,14 @@ virtual void DestroyComponent(int termination_exit_code, fuchsia::sys::TerminationReason reason); - // Gets a directory of incoming services provided to the component, or returns + // Returns the directory of incoming services provided to the component, or // nullptr if none was provided. - base::fuchsia::ComponentContext* additional_services() const { + base::fuchsia::ServiceDirectoryClient* additional_services() const { return additional_services_.get(); } - // Gets the names of services available in additional_services(). - const std::vector<std::string> additional_service_names() { + // Returns the names of services available in additional_services(). + const std::vector<std::string>& additional_service_names() const { return additional_service_names_; } @@ -96,7 +96,7 @@ fidl::Binding<fuchsia::sys::ComponentController> controller_binding_; // Incoming services provided at component creation. - std::unique_ptr<base::fuchsia::ComponentContext> additional_services_; + std::unique_ptr<base::fuchsia::ServiceDirectoryClient> additional_services_; // The names of services provided at component creation. std::vector<std::string> additional_service_names_;
diff --git a/fuchsia/runners/common/web_content_runner.cc b/fuchsia/runners/common/web_content_runner.cc index 3eb9995..281bb07 100644 --- a/fuchsia/runners/common/web_content_runner.cc +++ b/fuchsia/runners/common/web_content_runner.cc
@@ -10,11 +10,11 @@ #include "base/bind.h" #include "base/files/file.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/file_utils.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_directory.h" +#include "base/fuchsia/service_directory_client.h" #include "base/logging.h" #include "fuchsia/runners/common/web_component.h" #include "url/gurl.h" @@ -22,7 +22,7 @@ // static chromium::web::ContextPtr WebContentRunner::CreateDefaultWebContext() { auto web_context_provider = - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<chromium::web::ContextProvider>(); chromium::web::CreateContextParams create_params;
diff --git a/fuchsia/runners/web/web_runner_smoke_test.cc b/fuchsia/runners/web/web_runner_smoke_test.cc index de5f04b..e014b33 100644 --- a/fuchsia/runners/web/web_runner_smoke_test.cc +++ b/fuchsia/runners/web/web_runner_smoke_test.cc
@@ -5,7 +5,7 @@ #include <fuchsia/sys/cpp/fidl.h> #include "base/bind.h" -#include "base/fuchsia/component_context.h" +#include "base/fuchsia/service_directory_client.h" #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -64,7 +64,7 @@ fuchsia::sys::LaunchInfo launch_info; launch_info.url = test_server_.GetURL("/test.html").spec(); - auto launcher = base::fuchsia::ComponentContext::GetDefault() + auto launcher = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToServiceSync<fuchsia::sys::Launcher>(); fuchsia::sys::ComponentControllerSyncPtr controller;
diff --git a/gpu/config/gpu_preferences.h b/gpu/config/gpu_preferences.h index 032d86a..b5d39972 100644 --- a/gpu/config/gpu_preferences.h +++ b/gpu/config/gpu_preferences.h
@@ -56,12 +56,6 @@ // =================================== // Settings from //content/public/common/content_switches.h - // Runs the renderer and plugins in the same process as the browser. - bool single_process = false; - - // Run the GPU process as a thread in the browser process. - bool in_process_gpu = false; - // Disables hardware acceleration of video decode, where available. bool disable_accelerated_video_decode = false;
diff --git a/gpu/config/gpu_preferences_unittest.cc b/gpu/config/gpu_preferences_unittest.cc index d31ea56..14708613b 100644 --- a/gpu/config/gpu_preferences_unittest.cc +++ b/gpu/config/gpu_preferences_unittest.cc
@@ -14,8 +14,6 @@ namespace { void CheckGpuPreferencesEqual(GpuPreferences left, GpuPreferences right) { - EXPECT_EQ(left.single_process, right.single_process); - EXPECT_EQ(left.in_process_gpu, right.in_process_gpu); EXPECT_EQ(left.disable_accelerated_video_decode, right.disable_accelerated_video_decode); EXPECT_EQ(left.disable_accelerated_video_encode, @@ -110,8 +108,6 @@ prefs_mojom.name = value; \ EXPECT_EQ(input_prefs.name, prefs_mojom.name); - GPU_PREFERENCES_FIELD(single_process, true) - GPU_PREFERENCES_FIELD(in_process_gpu, true) GPU_PREFERENCES_FIELD(disable_accelerated_video_decode, true) GPU_PREFERENCES_FIELD(disable_accelerated_video_encode, true) GPU_PREFERENCES_FIELD(gpu_startup_dialog, true)
diff --git a/gpu/ipc/common/gpu_preferences.mojom b/gpu/ipc/common/gpu_preferences.mojom index 37bdcc4..92b32cfb 100644 --- a/gpu/ipc/common/gpu_preferences.mojom +++ b/gpu/ipc/common/gpu_preferences.mojom
@@ -17,8 +17,6 @@ // gpu::GpuPreferences struct GpuPreferences { - bool single_process; - bool in_process_gpu; bool disable_accelerated_video_decode; bool disable_accelerated_video_encode; bool gpu_startup_dialog;
diff --git a/gpu/ipc/common/gpu_preferences_struct_traits.h b/gpu/ipc/common/gpu_preferences_struct_traits.h index 7bea28c..2487731f 100644 --- a/gpu/ipc/common/gpu_preferences_struct_traits.h +++ b/gpu/ipc/common/gpu_preferences_struct_traits.h
@@ -56,8 +56,6 @@ struct StructTraits<gpu::mojom::GpuPreferencesDataView, gpu::GpuPreferences> { static bool Read(gpu::mojom::GpuPreferencesDataView prefs, gpu::GpuPreferences* out) { - out->single_process = prefs.single_process(); - out->in_process_gpu = prefs.in_process_gpu(); out->disable_accelerated_video_decode = prefs.disable_accelerated_video_decode(); out->disable_accelerated_video_encode = @@ -132,12 +130,6 @@ return true; } - static bool single_process(const gpu::GpuPreferences& prefs) { - return prefs.single_process; - } - static bool in_process_gpu(const gpu::GpuPreferences& prefs) { - return prefs.in_process_gpu; - } static bool disable_accelerated_video_decode( const gpu::GpuPreferences& prefs) { return prefs.disable_accelerated_video_decode;
diff --git a/gpu/ipc/common/struct_traits_unittest.cc b/gpu/ipc/common/struct_traits_unittest.cc index 95e18dc3..2c51f08d 100644 --- a/gpu/ipc/common/struct_traits_unittest.cc +++ b/gpu/ipc/common/struct_traits_unittest.cc
@@ -418,8 +418,8 @@ TEST_F(StructTraitsTest, GpuPreferences) { GpuPreferences prefs; - prefs.single_process = true; - prefs.in_process_gpu = true; + prefs.gpu_startup_dialog = true; + prefs.disable_gpu_watchdog = true; #if defined(OS_WIN) const GpuPreferences::VpxDecodeVendors vendor = GpuPreferences::VPX_VENDOR_AMD; @@ -430,8 +430,8 @@ mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); GpuPreferences echo; proxy->EchoGpuPreferences(prefs, &echo); - EXPECT_TRUE(echo.single_process); - EXPECT_TRUE(echo.in_process_gpu); + EXPECT_TRUE(echo.gpu_startup_dialog); + EXPECT_TRUE(echo.disable_gpu_watchdog); EXPECT_TRUE(echo.enable_gpu_driver_debug_logging); #if defined(OS_WIN) EXPECT_EQ(vendor, echo.enable_accelerated_vpx_decode);
diff --git a/ios/chrome/browser/ntp/BUILD.gn b/ios/chrome/browser/ntp/BUILD.gn index 103ce3d6..769e0da 100644 --- a/ios/chrome/browser/ntp/BUILD.gn +++ b/ios/chrome/browser/ntp/BUILD.gn
@@ -10,6 +10,7 @@ ] configs += [ "//build/config/compiler:enable_arc" ] deps = [ + ":features", "//base:base", "//components/strings:components_strings_grit", "//ios/chrome/browser", @@ -19,6 +20,16 @@ ] } +source_set("features") { + sources = [ + "features.cc", + "features.h", + ] + deps = [ + "//base", + ] +} + source_set("unit_tests") { configs += [ "//build/config/compiler:enable_arc" ] testonly = true
diff --git a/ios/chrome/browser/ntp/features.cc b/ios/chrome/browser/ntp/features.cc new file mode 100644 index 0000000..5095e51 --- /dev/null +++ b/ios/chrome/browser/ntp/features.cc
@@ -0,0 +1,8 @@ +// Copyright 2019 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 "ios/chrome/browser/ntp/features.h" + +const base::Feature kBlockNewTabPagePendingLoad{ + "BlockNewTabPagePendingLoad", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ntp/features.h b/ios/chrome/browser/ntp/features.h new file mode 100644 index 0000000..0fbb1d3 --- /dev/null +++ b/ios/chrome/browser/ntp/features.h
@@ -0,0 +1,13 @@ +// Copyright 2019 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 IOS_CHROME_BROWSER_NTP_FEATURES_H_ +#define IOS_CHROME_BROWSER_NTP_FEATURES_H_ + +#include "base/feature_list.h" + +// Feature flag to enable NTP UI pending loader blocker. +extern const base::Feature kBlockNewTabPagePendingLoad; + +#endif // IOS_CHROME_BROWSER_NTP_FEATURES_H_
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h index e0d9a6d1..6493814 100644 --- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h +++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
@@ -6,8 +6,10 @@ #define IOS_CHROME_BROWSER_NTP_NEW_TAB_PAGE_TAB_HELPER_H_ #import <UIKit/UIKit.h> +#include <memory> #include "base/macros.h" +#include "base/timer/timer.h" #include "ios/web/public/web_state/web_state_observer.h" #import "ios/web/public/web_state/web_state_user_data.h" @@ -31,6 +33,13 @@ // WebStateObserver callback. void Deactivate(); + // Sometimes the underlying ios/web page used for the NTP (about://newtab) + // takes a long time to load. Loading any page before the newtab is committed + // will leave ios/web in a bad state. See: crbug.com/925304 for more context. + // Remove this when ios/web supports queueing multiple loads during this + // state. + bool IgnoreLoadRequests() const; + private: NewTabPageTabHelper(web::WebState* web_state, id<NewTabPageTabHelperDelegate> delegate); @@ -52,6 +61,14 @@ // Returns true if an |url| is either chrome://newtab or about://newtab. bool IsNTPURL(const GURL& url); + // Sets the |ignore_load_requests_| flag to YES and starts the ignore load + // timer. + void EnableIgnoreLoadRequests(); + + // Sets the |ignore_load_requests_| flag to NO and stops the ignore load + // timer. + void DisableIgnoreLoadRequests(); + // Used to present and dismiss the NTP. __weak id<NewTabPageTabHelperDelegate> delegate_ = nil; @@ -61,6 +78,13 @@ // |YES| if the current tab helper is active. BOOL active_; + // |YES| if the NTP's underlying ios/web page is still loading. + BOOL ignore_load_requests_ = NO; + + // Ensure the ignore_load_requests_ flag is never set to NO for more than + // |kMaximumIgnoreLoadRequestsTime| seconds. + std::unique_ptr<base::OneShotTimer> ignore_load_requests_timer_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(NewTabPageTabHelper); };
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm index 5411c84..2d4d6b3 100644 --- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm +++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.mm
@@ -12,6 +12,7 @@ #include "base/strings/sys_string_conversions.h" #include "components/strings/grit/components_strings.h" #include "ios/chrome/browser/chrome_url_constants.h" +#include "ios/chrome/browser/ntp/features.h" #include "ios/chrome/browser/ntp/new_tab_page_tab_helper_delegate.h" #include "ios/chrome/browser/ui/ui_feature_flags.h" #import "ios/web/public/navigation_item.h" @@ -29,6 +30,10 @@ // |url::kAboutScheme|, there's no host value, only a path. Use this value for // matching the NTP. const char kAboutNewTabPath[] = "//newtab/"; + +// Maximum number of seconds for |ignore_load_requests_| to be set to YES. +static const size_t kMaximumIgnoreLoadRequestsTime = 10; + } // namespace // static @@ -58,6 +63,12 @@ if (active_) { UpdatePendingItem(); [delegate_ newTabPageHelperDidChangeVisibility:this forWebState:web_state_]; + + // If about://newtab is currently loading but has not yet committed, block + // loads until it does commit. + if (!IsNTPURL(web_state->GetLastCommittedURL())) { + EnableIgnoreLoadRequests(); + } } } @@ -69,11 +80,39 @@ SetActive(false); } +bool NewTabPageTabHelper::IgnoreLoadRequests() const { + return ignore_load_requests_; +} + +void NewTabPageTabHelper::EnableIgnoreLoadRequests() { + if (!base::FeatureList::IsEnabled(kBlockNewTabPagePendingLoad)) + return; + + ignore_load_requests_ = YES; + + // |ignore_load_requests_timer_| is deleted when the tab helper is deleted, so + // it's safe to use Unretained here. + ignore_load_requests_timer_.reset(new base::OneShotTimer()); + ignore_load_requests_timer_->Start( + FROM_HERE, base::TimeDelta::FromSeconds(kMaximumIgnoreLoadRequestsTime), + base::BindOnce(&NewTabPageTabHelper::DisableIgnoreLoadRequests, + base::Unretained(this))); +} + +void NewTabPageTabHelper::DisableIgnoreLoadRequests() { + if (ignore_load_requests_timer_) { + ignore_load_requests_timer_->Stop(); + ignore_load_requests_timer_.reset(); + } + ignore_load_requests_ = NO; +} + #pragma mark - WebStateObserver void NewTabPageTabHelper::WebStateDestroyed(web::WebState* web_state) { web_state->RemoveObserver(this); SetActive(false); + DisableIgnoreLoadRequests(); } void NewTabPageTabHelper::DidStartNavigation( @@ -93,6 +132,7 @@ return; } + DisableIgnoreLoadRequests(); SetActive(IsNTPURL(web_state->GetLastCommittedURL())); }
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn index d54e3a8..c7b8227 100644 --- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -40,6 +40,7 @@ "//ios/chrome/browser/browser_state", "//ios/chrome/browser/favicon", "//ios/chrome/browser/metrics:metrics_internal", + "//ios/chrome/browser/ntp", "//ios/chrome/browser/ntp_snippets", "//ios/chrome/browser/ntp_tiles", "//ios/chrome/browser/reading_list",
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm index a04fd368..31070b6 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -14,6 +14,7 @@ #include "components/strings/grit/components_strings.h" #include "ios/chrome/browser/chrome_url_constants.h" #import "ios/chrome/browser/metrics/new_tab_page_uma.h" +#import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h" #import "ios/chrome/browser/search_engines/search_engine_observer_bridge.h" #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" #import "ios/chrome/browser/ui/commands/application_commands.h" @@ -201,6 +202,11 @@ } - (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath { + NewTabPageTabHelper* NTPHelper = + NewTabPageTabHelper::FromWebState(self.webState); + if (NTPHelper && NTPHelper->IgnoreLoadRequests()) { + return; + } CollectionViewItem* item = [self.suggestionsViewController.collectionViewModel itemAtIndexPath:indexPath]; ContentSuggestionsItem* suggestionItem = @@ -256,6 +262,11 @@ return; } + NewTabPageTabHelper* NTPHelper = + NewTabPageTabHelper::FromWebState(self.webState); + if (NTPHelper && NTPHelper->IgnoreLoadRequests()) + return; + ContentSuggestionsMostVisitedItem* mostVisitedItem = base::mac::ObjCCastStrict<ContentSuggestionsMostVisitedItem>(item); @@ -346,6 +357,10 @@ } - (void)handleLearnMoreTapped { + NewTabPageTabHelper* NTPHelper = + NewTabPageTabHelper::FromWebState(self.webState); + if (NTPHelper && NTPHelper->IgnoreLoadRequests()) + return; GURL URL(kNTPHelpURL); ChromeLoadParams params(URL); [self.dispatcher loadURLWithParams:params];
diff --git a/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/media/audio/fuchsia/audio_output_stream_fuchsia.cc index bdb614f..6fc9f38 100644 --- a/media/audio/fuchsia/audio_output_stream_fuchsia.cc +++ b/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -7,7 +7,7 @@ #include <zircon/syscalls.h> #include "base/bind.h" -#include "base/fuchsia/component_context.h" +#include "base/fuchsia/service_directory_client.h" #include "media/audio/fuchsia/audio_manager_fuchsia.h" #include "media/base/audio_sample_types.h" #include "media/base/audio_timestamp_helper.h" @@ -36,7 +36,7 @@ // Connect |audio_renderer_| to the audio service. fuchsia::media::AudioPtr audio_server = - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::media::Audio>(); audio_server->CreateAudioRenderer(audio_renderer_.NewRequest()); audio_renderer_.set_error_handler(
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc index 3cf45416..84c3f481 100644 --- a/media/filters/fuchsia/fuchsia_video_decoder.cc +++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" @@ -402,7 +402,7 @@ codec_params.require_hw = !enable_sw_decoding_; auto codec_factory = - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::mediacodec::CodecFactory>(); codec_factory->CreateDecoder(std::move(codec_params), codec_.NewRequest());
diff --git a/media/gpu/v4l2/v4l2_image_processor.cc b/media/gpu/v4l2/v4l2_image_processor.cc index bd013f9..a9b81c30 100644 --- a/media/gpu/v4l2/v4l2_image_processor.cc +++ b/media/gpu/v4l2/v4l2_image_processor.cc
@@ -84,7 +84,7 @@ error_cb_(error_cb) {} V4L2ImageProcessor::~V4L2ImageProcessor() { - DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_); + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); Destroy(); @@ -443,7 +443,7 @@ bool V4L2ImageProcessor::Reset() { VLOGF(2); - DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_); + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); DCHECK(device_thread_.IsRunning()); process_task_tracker_.TryCancelAll(); @@ -452,7 +452,7 @@ void V4L2ImageProcessor::Destroy() { VLOGF(2); - DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_); + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); // If the device thread is running, destroy using posted task. if (device_thread_.IsRunning()) {
diff --git a/media/gpu/v4l2/v4l2_image_processor.h b/media/gpu/v4l2/v4l2_image_processor.h index 99e266e..9d1b0306 100644 --- a/media/gpu/v4l2/v4l2_image_processor.h +++ b/media/gpu/v4l2/v4l2_image_processor.h
@@ -18,9 +18,9 @@ #include "base/files/scoped_file.h" #include "base/macros.h" #include "base/memory/scoped_refptr.h" +#include "base/sequence_checker.h" #include "base/task/cancelable_task_tracker.h" #include "base/threading/thread.h" -#include "base/threading/thread_checker.h" #include "media/base/video_frame.h" #include "media/base/video_frame_layout.h" #include "media/gpu/image_processor.h" @@ -196,8 +196,8 @@ // Error callback to the client. ErrorCB error_cb_; - // Checker for the thread that creates this V4L2ImageProcessor. - THREAD_CHECKER(client_thread_checker_); + // Checker for the sequence that creates this V4L2ImageProcessor. + SEQUENCE_CHECKER(client_sequence_checker_); DISALLOW_COPY_AND_ASSIGN(V4L2ImageProcessor); };
diff --git a/media/gpu/vaapi/vp8_encoder.cc b/media/gpu/vaapi/vp8_encoder.cc index 53c196b..b591fd143 100644 --- a/media/gpu/vaapi/vp8_encoder.cc +++ b/media/gpu/vaapi/vp8_encoder.cc
@@ -11,15 +11,15 @@ namespace { // Keyframe period. -const size_t kKFPeriod = 3000; +constexpr size_t kKFPeriod = 3000; // Arbitrarily chosen bitrate window size for rate control, in ms. -const int kCPBWindowSizeMs = 1500; +constexpr int kCPBWindowSizeMs = 1500; // Based on WebRTC's defaults. -const int kMinQP = 4; -const int kMaxQP = 112; -const int kDefaultQP = (3 * kMinQP + kMaxQP) / 4; +constexpr int kMinQP = 4; +constexpr int kMaxQP = 112; +constexpr int kDefaultQP = (3 * kMinQP + kMaxQP) / 4; } // namespace VP8Encoder::EncodeParams::EncodeParams()
diff --git a/media/gpu/vp8_reference_frame_vector.h b/media/gpu/vp8_reference_frame_vector.h index 63c7159..cdb7c564 100644 --- a/media/gpu/vp8_reference_frame_vector.h +++ b/media/gpu/vp8_reference_frame_vector.h
@@ -7,7 +7,7 @@ #include <array> -#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" #include "base/sequence_checker.h" #include "media/filters/vp8_parser.h"
diff --git a/net/base/network_change_notifier_fuchsia.cc b/net/base/network_change_notifier_fuchsia.cc index a8044df..bc108fe 100644 --- a/net/base/network_change_notifier_fuchsia.cc +++ b/net/base/network_change_notifier_fuchsia.cc
@@ -10,8 +10,8 @@ #include <vector> #include "base/bind.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "base/optional.h" #include "base/run_loop.h" #include "net/base/network_interfaces.h" @@ -41,7 +41,7 @@ NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia( uint32_t required_features) : NetworkChangeNotifierFuchsia( - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::netstack::Netstack>(), required_features) {}
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc index 30dcc5d..300966e 100644 --- a/net/base/network_interfaces_fuchsia.cc +++ b/net/base/network_interfaces_fuchsia.cc
@@ -12,8 +12,8 @@ #include <utility> #include "base/format_macros.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "base/strings/stringprintf.h" #include "net/base/ip_endpoint.h" #include "net/base/network_interfaces.h" @@ -103,7 +103,7 @@ DCHECK(networks); fuchsia::netstack::NetstackSyncPtr netstack = - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToServiceSync<fuchsia::netstack::Netstack>(); // TODO(kmarshall): Use NetworkChangeNotifier's cached interface list.
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index b00247df..301d4b71 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc
@@ -472,8 +472,8 @@ if (!is_https_proxy_ || !SanitizeProxyRedirect(&response_)) return ERR_TUNNEL_CONNECTION_FAILED; - transport_.reset(); http_stream_parser_.reset(); + transport_.reset(); return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; case 407: // Proxy Authentication Required
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc index 4f142a1..0ffb067 100644 --- a/net/http/http_proxy_client_socket_wrapper.cc +++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -670,7 +670,7 @@ } } else { // Create a session direct to the proxy itself - spdy_session = spdy_session_pool_->CreateAvailableSessionFromSocket( + spdy_session = spdy_session_pool_->CreateAvailableSessionFromSocketHandle( key, is_trusted_proxy_, std::move(transport_socket_handle_), net_log_); DCHECK(spdy_session); }
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc index 9b2e7989..861c5f2 100644 --- a/net/http/http_stream_factory_job.cc +++ b/net/http/http_stream_factory_job.cc
@@ -1279,7 +1279,7 @@ !spdy_session_direct_ && proxy_info_.proxy_server().is_trusted_proxy(); base::WeakPtr<SpdySession> spdy_session = - session_->spdy_session_pool()->CreateAvailableSessionFromSocket( + session_->spdy_session_pool()->CreateAvailableSessionFromSocketHandle( spdy_session_key_, is_trusted_proxy, std::move(connection_), net_log_);
diff --git a/net/socket/connect_job_test_util.cc b/net/socket/connect_job_test_util.cc index f72a946..a964986 100644 --- a/net/socket/connect_job_test_util.cc +++ b/net/socket/connect_job_test_util.cc
@@ -4,6 +4,8 @@ #include "net/socket/connect_job_test_util.h" +#include <utility> + #include "base/logging.h" #include "base/run_loop.h" #include "net/socket/stream_socket.h" @@ -49,4 +51,8 @@ } } +std::unique_ptr<StreamSocket> TestConnectJobDelegate::ReleaseSocket() { + return std::move(socket_); +} + } // namespace net
diff --git a/net/socket/connect_job_test_util.h b/net/socket/connect_job_test_util.h index aea7693f..73d7f06 100644 --- a/net/socket/connect_job_test_util.h +++ b/net/socket/connect_job_test_util.h
@@ -46,6 +46,8 @@ StreamSocket* socket() { return socket_.get(); } + std::unique_ptr<StreamSocket> ReleaseSocket(); + private: const SocketExpected socket_expected_; bool has_result_ = false;
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h index 315cba5b..60fb740a 100644 --- a/net/spdy/spdy_http_stream.h +++ b/net/spdy/spdy_http_stream.h
@@ -14,6 +14,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "net/base/completion_once_callback.h" +#include "net/base/load_timing_info.h" #include "net/base/net_export.h" #include "net/log/net_log_source.h" #include "net/spdy/multiplexed_http_stream.h"
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc index 0f31adc..b9ab32c 100644 --- a/net/spdy/spdy_proxy_client_socket_unittest.cc +++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -13,6 +13,7 @@ #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "net/base/address_list.h" +#include "net/base/load_timing_info.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/dns/mock_host_resolver.h" @@ -24,9 +25,15 @@ #include "net/log/test_net_log_entry.h" #include "net/log/test_net_log_util.h" #include "net/socket/client_socket_factory.h" +#include "net/socket/connect_job_test_util.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" +#include "net/socket/socks_connect_job.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_connect_job.h" +#include "net/socket/stream_socket.h" #include "net/socket/tcp_client_socket.h" +#include "net/socket/transport_connect_job.h" #include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_session_pool.h" @@ -46,6 +53,8 @@ //----------------------------------------------------------------------------- +namespace net { + namespace { static const char kRequestUrl[] = "https://www.google.com/"; @@ -72,9 +81,57 @@ static const char kRedirectUrl[] = "https://example.com/"; -} // anonymous namespace +// Creates a SpdySession with a StreamSocket, instead of a ClientSocketHandle. +base::WeakPtr<SpdySession> CreateSpdyProxySession( + HttpNetworkSession* http_session, + SpdySessionDependencies* session_deps, + const SpdySessionKey& key) { + EXPECT_FALSE(http_session->spdy_session_pool()->FindAvailableSession( + key, true /* enable_ip_based_pooling */, false /* is_websocket */, + NetLogWithSource())); -namespace net { + auto transport_params = base::MakeRefCounted<TransportSocketParams>( + key.host_port_pair(), false /* disable_resolver_cache */, + OnHostResolutionCallback()); + + SSLConfig ssl_config; + auto ssl_params = base::MakeRefCounted<SSLSocketParams>( + transport_params, nullptr, nullptr, key.host_port_pair(), ssl_config, + key.privacy_mode()); + TestConnectJobDelegate connect_job_delegate; + SSLConnectJob connect_job( + MEDIUM, + CommonConnectJobParams( + "group_name", SocketTag(), true /* respect_limits */, + session_deps->socket_factory.get(), session_deps->host_resolver.get(), + SSLClientSocketContext(session_deps->cert_verifier.get(), + session_deps->channel_id_service.get(), + session_deps->transport_security_state.get(), + session_deps->cert_transparency_verifier.get(), + session_deps->ct_policy_enforcer.get(), + nullptr /* ssl_client_session_cache_arg */, + std::string() /* ssl_session_cache_shard_arg */ + ), + nullptr /* socket_performance_watcher_factory */, + nullptr /* network_quality_estimator */, session_deps->net_log, + nullptr /* websocket_endpoint_lock_manager */), + ssl_params, nullptr /* socks_pool */, nullptr /* http_proxy_pool */, + &connect_job_delegate); + connect_job_delegate.StartJobExpectingResult(&connect_job, OK, + false /* expect_sync_result */); + + base::WeakPtr<SpdySession> spdy_session = + http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( + key, false /* is_trusted_proxy */, + connect_job_delegate.ReleaseSocket(), LoadTimingInfo::ConnectTiming(), + NetLogWithSource()); + // Failure is reported asynchronously. + EXPECT_TRUE(spdy_session); + EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key)); + return spdy_session; +} + +} // namespace class SpdyProxyClientSocketTest : public PlatformTest, public WithScopedTaskEnvironment, @@ -206,8 +263,9 @@ session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); // Creates the SPDY session and stream. - spdy_session_ = CreateSpdySession(session_.get(), endpoint_spdy_session_key_, - NetLogWithSource()); + spdy_session_ = CreateSpdyProxySession(session_.get(), &session_deps_, + endpoint_spdy_session_key_); + base::WeakPtr<SpdyStream> spdy_stream( CreateStreamSynchronously( SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url_, LOWEST, @@ -413,9 +471,8 @@ spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructBodyFrame( const char* data, int length) { - return spdy_util_.ConstructSpdyDataFrame(kStreamId, - base::StringPiece(data, length), - /*fin=*/false); + return spdy_util_.ConstructSpdyDataFrame( + kStreamId, base::StringPiece(data, length), false /* fin */); } // ----------- Connect
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 9c3605b..c98317a 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc
@@ -44,6 +44,7 @@ #include "net/log/net_log_with_source.h" #include "net/nqe/network_quality_estimator.h" #include "net/quic/quic_http_utils.h" +#include "net/socket/client_socket_handle.h" #include "net/socket/socket.h" #include "net/socket/ssl_client_socket.h" #include "net/spdy/header_coalescer.h" @@ -844,6 +845,7 @@ http_server_properties_(http_server_properties), transport_security_state_(transport_security_state), ssl_config_service_(ssl_config_service), + socket_(nullptr), stream_hi_water_mark_(kFirstStreamId), last_accepted_push_stream_id_(0), push_delegate_(push_delegate), @@ -920,11 +922,10 @@ CHECK(!in_io_loop_); DcheckDraining(); - // TODO(akalin): Check connection->is_initialized() instead. This - // requires re-working CreateFakeSpdySession(), though. - DCHECK(connection_->socket()); + // TODO(akalin): Check connection->is_initialized(). + DCHECK(socket_); // With SPDY we can't recycle sockets. - connection_->socket()->Disconnect(); + socket_->Disconnect(); RecordHistograms(); @@ -981,48 +982,40 @@ ResetStream(stream_id, ERR_ABORTED, "Cancelled push stream."); } -void SpdySession::InitializeWithSocket( - std::unique_ptr<ClientSocketHandle> connection, +void SpdySession::InitializeWithSocketHandle( + std::unique_ptr<ClientSocketHandle> client_socket_handle, SpdySessionPool* pool) { - CHECK(!in_io_loop_); - DCHECK_EQ(availability_state_, STATE_AVAILABLE); - DCHECK_EQ(read_state_, READ_STATE_DO_READ); - DCHECK_EQ(write_state_, WRITE_STATE_IDLE); - DCHECK(!connection_); + DCHECK(!client_socket_handle_); + DCHECK(!owned_stream_socket_); + DCHECK(!socket_); // TODO(akalin): Check connection->is_initialized() instead. This // requires re-working CreateFakeSpdySession(), though. - DCHECK(connection->socket()); + DCHECK(client_socket_handle->socket()); - connection_ = std::move(connection); + client_socket_handle_ = std::move(client_socket_handle); + socket_ = client_socket_handle_->socket(); + client_socket_handle_->AddHigherLayeredPool(this); - session_send_window_size_ = kDefaultInitialWindowSize; - session_recv_window_size_ = kDefaultInitialWindowSize; + InitializeInternal(pool); +} - auto it = initial_settings_.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE); - uint32_t spdy_max_header_list_size = - (it == initial_settings_.end()) ? kSpdyMaxHeaderListSize : it->second; - buffered_spdy_framer_ = std::make_unique<BufferedSpdyFramer>( - spdy_max_header_list_size, net_log_, time_func_); - buffered_spdy_framer_->set_visitor(this); - buffered_spdy_framer_->set_debug_visitor(this); - buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_); +void SpdySession::InitializeWithSocket( + std::unique_ptr<StreamSocket> stream_socket, + const LoadTimingInfo::ConnectTiming& connect_timing, + SpdySessionPool* pool) { + DCHECK(!client_socket_handle_); + DCHECK(!owned_stream_socket_); + DCHECK(!socket_); - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, - base::Bind(&NetLogSpdyInitializedCallback, - connection_->socket()->NetLog().source())); + DCHECK(stream_socket); - DCHECK_EQ(availability_state_, STATE_AVAILABLE); - connection_->AddHigherLayeredPool(this); - if (enable_sending_initial_data_) - SendInitialData(); - pool_ = pool; + owned_stream_socket_ = std::move(stream_socket); + socket_ = owned_stream_socket_.get(); + connect_timing_ = + std::make_unique<LoadTimingInfo::ConnectTiming>(connect_timing); - // Bootstrap the read loop. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), - READ_STATE_DO_READ, OK)); + InitializeInternal(pool); } bool SpdySession::VerifyDomainAuthentication(const std::string& domain) const { @@ -1283,15 +1276,15 @@ } bool SpdySession::GetSSLInfo(SSLInfo* ssl_info) const { - return connection_->socket()->GetSSLInfo(ssl_info); + return socket_->GetSSLInfo(ssl_info); } bool SpdySession::WasAlpnNegotiated() const { - return connection_->socket()->WasAlpnNegotiated(); + return socket_->WasAlpnNegotiated(); } NextProto SpdySession::GetNegotiatedProtocol() const { - return connection_->socket()->GetNegotiatedProtocol(); + return socket_->GetNegotiatedProtocol(); } void SpdySession::SendStreamWindowUpdate(spdy::SpdyStreamId stream_id, @@ -1388,9 +1381,8 @@ dict->SetInteger("unclaimed_pushed_streams", pool_->push_promise_index()->CountStreamsForSession(this)); - dict->SetString( - "negotiated_protocol", - NextProtoToString(connection_->socket()->GetNegotiatedProtocol())); + dict->SetString("negotiated_protocol", + NextProtoToString(socket_->GetNegotiatedProtocol())); dict->SetInteger("error", error_on_close_); dict->SetInteger("max_concurrent_streams", max_concurrent_streams_); @@ -1411,26 +1403,50 @@ } bool SpdySession::IsReused() const { - return buffered_spdy_framer_->frames_received() > 0 || - connection_->reuse_type() == ClientSocketHandle::UNUSED_IDLE; + if (buffered_spdy_framer_->frames_received() > 0) + return true; + + // If there's no socket pool in use (i.e., |owned_stream_socket_| is + // non-null), then the SpdySession could only have been created with freshly + // connected socket, since canceling the H2 session request would have + // destroyed the socket. + return owned_stream_socket_ || + client_socket_handle_->reuse_type() == ClientSocketHandle::UNUSED_IDLE; } bool SpdySession::GetLoadTimingInfo(spdy::SpdyStreamId stream_id, LoadTimingInfo* load_timing_info) const { - return connection_->GetLoadTimingInfo(stream_id != kFirstStreamId, - load_timing_info); + if (client_socket_handle_) { + DCHECK(!connect_timing_); + return client_socket_handle_->GetLoadTimingInfo(stream_id != kFirstStreamId, + load_timing_info); + } + + DCHECK(connect_timing_); + DCHECK(socket_); + + // The socket is considered "fresh" (not reused) only for the first stream on + // a SPDY session. All others consider it reused, and don't return connection + // establishment timing information. + load_timing_info->socket_reused = (stream_id != kFirstStreamId); + if (!load_timing_info->socket_reused) + load_timing_info->connect_timing = *connect_timing_; + + load_timing_info->socket_log_id = socket_->NetLog().source().id; + + return true; } int SpdySession::GetPeerAddress(IPEndPoint* address) const { - if (connection_->socket()) - return connection_->socket()->GetPeerAddress(address); + if (socket_) + return socket_->GetPeerAddress(address); return ERR_SOCKET_NOT_CONNECTED; } int SpdySession::GetLocalAddress(IPEndPoint* address) const { - if (connection_->socket()) - return connection_->socket()->GetLocalAddress(address); + if (socket_) + return socket_->GetLocalAddress(address); return ERR_SOCKET_NOT_CONNECTED; } @@ -1523,7 +1539,7 @@ // TODO(xunjieli): Include |pending_create_stream_queues_| when WeakPtr is // supported in memory_usage_estimator.h. *is_session_active = is_active(); - connection_->DumpMemoryStats(stats); + socket_->DumpMemoryStats(stats); // |connection_| is estimated in stats->total_size. |read_buffer_| is // estimated in |read_buffer_size|. TODO(xunjieli): Make them use EMU(). @@ -1542,7 +1558,7 @@ } bool SpdySession::ChangeSocketTag(const SocketTag& new_tag) { - if (!IsAvailable() || !connection_->socket()) + if (!IsAvailable() || !socket_) return false; // Changing the tag on the underlying socket will affect all streams, @@ -1550,7 +1566,7 @@ if (is_active()) return false; - connection_->socket()->ApplySocketTag(new_tag); + socket_->ApplySocketTag(new_tag); SpdySessionKey new_key(spdy_session_key_.host_port_pair(), spdy_session_key_.proxy_server(), @@ -1567,6 +1583,40 @@ UMA_HISTOGRAM_ENUMERATION("Net.SpdyPushedStreamFate", value); } +void SpdySession::InitializeInternal(SpdySessionPool* pool) { + CHECK(!in_io_loop_); + DCHECK_EQ(availability_state_, STATE_AVAILABLE); + DCHECK_EQ(read_state_, READ_STATE_DO_READ); + DCHECK_EQ(write_state_, WRITE_STATE_IDLE); + + session_send_window_size_ = kDefaultInitialWindowSize; + session_recv_window_size_ = kDefaultInitialWindowSize; + + auto it = initial_settings_.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE); + uint32_t spdy_max_header_list_size = + (it == initial_settings_.end()) ? kSpdyMaxHeaderListSize : it->second; + buffered_spdy_framer_ = std::make_unique<BufferedSpdyFramer>( + spdy_max_header_list_size, net_log_, time_func_); + buffered_spdy_framer_->set_visitor(this); + buffered_spdy_framer_->set_debug_visitor(this); + buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_); + + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, + base::BindRepeating(&NetLogSpdyInitializedCallback, + socket_->NetLog().source())); + + DCHECK_EQ(availability_state_, STATE_AVAILABLE); + if (enable_sending_initial_data_) + SendInitialData(); + pool_ = pool; + + // Bootstrap the read loop. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindRepeating(&SpdySession::PumpReadLoop, + weak_factory_.GetWeakPtr(), READ_STATE_DO_READ, OK)); +} + // {,Try}CreateStream() can be called with |in_io_loop_| set if a stream is // being created in response to another being closed due to received data. @@ -1615,10 +1665,10 @@ if (availability_state_ == STATE_DRAINING) return ERR_CONNECTION_CLOSED; - DCHECK(connection_->socket()); + DCHECK(socket_); UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected", - connection_->socket()->IsConnected()); - if (!connection_->socket()->IsConnected()) { + socket_->IsConnected()); + if (!socket_->IsConnected()) { DoDrainSession( ERR_CONNECTION_CLOSED, "Tried to create SPDY stream for a closed socket connection."); @@ -1962,10 +2012,11 @@ DeleteStream(std::move(owned_stream), status); - // If there are no active streams and the socket pool is stalled, close the - // session to free up a socket slot. - if (active_streams_.empty() && created_streams_.empty() && - connection_->IsPoolStalled()) { + // If the socket belongs to a socket pool, and there are no active streams, + // and the socket pool is stalled, then close the session to free up a socket + // slot. + if (client_socket_handle_ && active_streams_.empty() && + created_streams_.empty() && client_socket_handle_->IsPoolStalled()) { DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection."); } } @@ -2112,13 +2163,12 @@ DCHECK(!read_buffer_); CHECK(in_io_loop_); - CHECK(connection_); - CHECK(connection_->socket()); + CHECK(socket_); read_state_ = READ_STATE_DO_READ_COMPLETE; int rv = ERR_READ_IF_READY_NOT_IMPLEMENTED; read_buffer_ = base::MakeRefCounted<IOBuffer>(kReadBufferSize); if (base::FeatureList::IsEnabled(Socket::kReadIfReadyExperiment)) { - rv = connection_->socket()->ReadIfReady( + rv = socket_->ReadIfReady( read_buffer_.get(), kReadBufferSize, base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), READ_STATE_DO_READ)); @@ -2130,7 +2180,7 @@ } if (rv == ERR_READ_IF_READY_NOT_IMPLEMENTED) { // Fallback to regular Read(). - return connection_->socket()->Read( + return socket_->Read( read_buffer_.get(), kReadBufferSize, base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), READ_STATE_DO_READ_COMPLETE)); @@ -2297,7 +2347,7 @@ scoped_refptr<IOBuffer> write_io_buffer = in_flight_write_->GetIOBufferForRemainingData(); - return connection_->socket()->Write( + return socket_->Write( write_io_buffer.get(), in_flight_write_->GetRemainingSize(), base::Bind(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), WRITE_STATE_DO_WRITE_COMPLETE),
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 2f846ffd..2878cf28f 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h
@@ -27,11 +27,11 @@ #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_states.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_export.h" #include "net/base/request_priority.h" #include "net/log/net_log_source.h" -#include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool.h" #include "net/socket/next_proto.h" #include "net/socket/ssl_client_socket.h" @@ -86,7 +86,6 @@ const spdy::SpdyStreamId kFirstStreamId = 1; const spdy::SpdyStreamId kLastStreamId = 0x7fffffff; -struct LoadTimingInfo; class NetLog; class NetworkQualityEstimator; class SpdyStream; @@ -348,10 +347,19 @@ // |pool| is the SpdySessionPool that owns us. Its lifetime must // strictly be greater than |this|. // - // The session begins reading from |connection| on a subsequent event loop - // iteration, so the SpdySession may close immediately afterwards if the first - // read of |connection| fails. - void InitializeWithSocket(std::unique_ptr<ClientSocketHandle> connection, + // The session begins reading from |client_socket_handle| on a subsequent + // event loop iteration, so the SpdySession may close immediately afterwards + // if the first read of |client_socket_handle| fails. + void InitializeWithSocketHandle( + std::unique_ptr<ClientSocketHandle> client_socket_handle, + SpdySessionPool* pool); + + // Just like InitializeWithSocketHandle(), but for use when the session is not + // on top of a socket pool, but instead directly on top of a socket, which the + // session has sole ownership of, and is responsible for deleting directly + // itself. + void InitializeWithSocket(std::unique_ptr<StreamSocket> stream_socket, + const LoadTimingInfo::ConnectTiming& connect_timing, SpdySessionPool* pool); // Check to see if this SPDY session can support an additional domain. @@ -476,9 +484,7 @@ // Returns true if the underlying transport socket ever had any reads or // writes. - bool WasEverUsed() const { - return connection_->socket()->WasEverUsed(); - } + bool WasEverUsed() const { return socket_->WasEverUsed(); } // Returns the load timing information from the perspective of the given // stream. If it's not the first stream, the connection is considered reused @@ -595,6 +601,9 @@ WRITE_STATE_DO_WRITE_COMPLETE, }; + // Has the shared logic for the other two Initialize methods that call it. + void InitializeInternal(SpdySessionPool* pool); + // Called by SpdyStreamRequest to start a request to create a // stream. If OK is returned, then |stream| will be filled in with a // valid stream. If ERR_IO_PENDING is returned, then @@ -937,8 +946,18 @@ TransportSecurityState* transport_security_state_; SSLConfigService* ssl_config_service_; - // The socket handle for this session. - std::unique_ptr<ClientSocketHandle> connection_; + // One of these two owns the socket for this session, which is stored in + // |socket_|. If |client_socket_handle_| is non-null, this session is on top + // of a socket in a socket pool. If |owned_stream_socket_| is non-null, this + // session is directly on top of a socket, which is not in a socket pool. + std::unique_ptr<ClientSocketHandle> client_socket_handle_; + std::unique_ptr<StreamSocket> owned_stream_socket_; + + // This is non-null only if |owned_stream_socket_| is non-null. + std::unique_ptr<LoadTimingInfo::ConnectTiming> connect_timing_; + + // The socket for this session. + StreamSocket* socket_; // The read buffer used to read data from the socket. // Non-null if there is a Read() pending.
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc index 27e4f563..014d434 100644 --- a/net/spdy/spdy_session_pool.cc +++ b/net/spdy/spdy_session_pool.cc
@@ -98,48 +98,38 @@ CertDatabase::GetInstance()->RemoveObserver(this); } +base::WeakPtr<SpdySession> +SpdySessionPool::CreateAvailableSessionFromSocketHandle( + const SpdySessionKey& key, + bool is_trusted_proxy, + std::unique_ptr<ClientSocketHandle> client_socket_handle, + const NetLogWithSource& net_log) { + TRACE_EVENT0(NetTracingCategory(), + "SpdySessionPool::CreateAvailableSessionFromSocketHandle"); + + std::unique_ptr<SpdySession> new_session = + CreateSession(key, is_trusted_proxy, net_log.net_log()); + new_session->InitializeWithSocketHandle(std::move(client_socket_handle), + this); + return InsertSession(key, std::move(new_session), net_log); +} + base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket( const SpdySessionKey& key, bool is_trusted_proxy, - std::unique_ptr<ClientSocketHandle> connection, + std::unique_ptr<StreamSocket> socket_stream, + const LoadTimingInfo::ConnectTiming& connect_timing, const NetLogWithSource& net_log) { TRACE_EVENT0(NetTracingCategory(), "SpdySessionPool::CreateAvailableSessionFromSocket"); - UMA_HISTOGRAM_ENUMERATION( - "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX); + std::unique_ptr<SpdySession> new_session = + CreateSession(key, is_trusted_proxy, net_log.net_log()); - auto new_session = std::make_unique<SpdySession>( - key, http_server_properties_, transport_security_state_, - ssl_config_service_, quic_supported_versions_, - enable_sending_initial_data_, enable_ping_based_connection_checking_, - support_ietf_format_quic_altsvc_, is_trusted_proxy, - session_max_recv_window_size_, initial_settings_, greased_http2_frame_, - time_func_, push_delegate_, network_quality_estimator_, - net_log.net_log()); + new_session->InitializeWithSocket(std::move(socket_stream), connect_timing, + this); - new_session->InitializeWithSocket(std::move(connection), this); - - base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr(); - sessions_.insert(new_session.release()); - MapKeyToAvailableSession(key, available_session); - - net_log.AddEvent( - NetLogEventType::HTTP2_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, - available_session->net_log().source().ToEventParametersCallback()); - - // Look up the IP address for this session so that we can match - // future sessions (potentially to different domains) which can - // potentially be pooled with this one. Because GetPeerAddress() - // reports the proxy's address instead of the origin server, check - // to see if this is a direct connection. - if (key.proxy_server().is_direct()) { - IPEndPoint address; - if (available_session->GetPeerAddress(&address) == OK) - aliases_.insert(AliasMap::value_type(address, key)); - } - - return available_session; + return InsertSession(key, std::move(new_session), net_log); } base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession( @@ -602,4 +592,46 @@ } } +std::unique_ptr<SpdySession> SpdySessionPool::CreateSession( + const SpdySessionKey& key, + bool is_trusted_proxy, + NetLog* net_log) { + UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", IMPORTED_FROM_SOCKET, + SPDY_SESSION_GET_MAX); + + return std::make_unique<SpdySession>( + key, http_server_properties_, transport_security_state_, + ssl_config_service_, quic_supported_versions_, + enable_sending_initial_data_, enable_ping_based_connection_checking_, + support_ietf_format_quic_altsvc_, is_trusted_proxy, + session_max_recv_window_size_, initial_settings_, greased_http2_frame_, + time_func_, push_delegate_, network_quality_estimator_, net_log); +} + +base::WeakPtr<SpdySession> SpdySessionPool::InsertSession( + const SpdySessionKey& key, + std::unique_ptr<SpdySession> new_session, + const NetLogWithSource& source_net_log) { + base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr(); + sessions_.insert(new_session.release()); + MapKeyToAvailableSession(key, available_session); + + source_net_log.AddEvent( + NetLogEventType::HTTP2_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, + available_session->net_log().source().ToEventParametersCallback()); + + // Look up the IP address for this session so that we can match + // future sessions (potentially to different domains) which can + // potentially be pooled with this one. Because GetPeerAddress() + // reports the proxy's address instead of the origin server, check + // to see if this is a direct connection. + if (key.proxy_server().is_direct()) { + IPEndPoint address; + if (available_session->GetPeerAddress(&address) == OK) + aliases_.insert(AliasMap::value_type(address, key)); + } + + return available_session; +} + } // namespace net
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index 4ff38a3f..755d0cd 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h
@@ -20,6 +20,7 @@ #include "base/optional.h" #include "net/base/host_port_pair.h" #include "net/base/ip_endpoint.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_export.h" #include "net/base/network_change_notifier.h" @@ -48,6 +49,7 @@ class NetLogWithSource; class NetworkQualityEstimator; class SpdySession; +class StreamSocket; class TransportSecurityState; // This is a very simple pool for open SpdySessions. @@ -95,12 +97,28 @@ // not already be a session for the given key. // // Returns the new SpdySession. Note that the SpdySession begins reading from - // |connection| on a subsequent event loop iteration, so it may be closed - // immediately afterwards if the first read of |connection| fails. + // |client_socket_handle| on a subsequent event loop iteration, so it may be + // closed immediately afterwards if the first read of |client_socket_handle| + // fails. + base::WeakPtr<SpdySession> CreateAvailableSessionFromSocketHandle( + const SpdySessionKey& key, + bool is_trusted_proxy, + std::unique_ptr<ClientSocketHandle> client_socket_handle, + const NetLogWithSource& net_log); + + // Just like the above method, except it takes a SocketStream instead of a + // ClientSocketHandle, and separate connect timing information. When this + // constructor is used, there is no socket pool beneath the SpdySession. + // Instead, the session takes exclusive ownership of the underting socket, and + // destroying the session will directly destroy the socket, as opposed to + // disconnected it and then returning it to the socket pool. This is intended + // for use with H2 proxies, which are layered beneath the socket pools and + // can have sockets above them for tunnels, which are put in a socket pool. base::WeakPtr<SpdySession> CreateAvailableSessionFromSocket( const SpdySessionKey& key, bool is_trusted_proxy, - std::unique_ptr<ClientSocketHandle> connection, + std::unique_ptr<StreamSocket> socket_stream, + const LoadTimingInfo::ConnectTiming& connect_timing, const NetLogWithSource& net_log); // If there is an available session for |key|, return it. @@ -254,6 +272,18 @@ const std::string& description, bool idle_only); + // Creates a new session. The session must be initialized before + // InsertSession() is invoked. + std::unique_ptr<SpdySession> CreateSession(const SpdySessionKey& key, + bool is_trusted_proxy, + NetLog* net_log); + // Adds a new session previously created with CreateSession to the pool. + // |source_net_log| is the NetLog for the object that created the session. + base::WeakPtr<SpdySession> InsertSession( + const SpdySessionKey& key, + std::unique_ptr<SpdySession> new_session, + const NetLogWithSource& source_net_log); + HttpServerProperties* http_server_properties_; TransportSecurityState* transport_security_state_;
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc index 804ee2c..29fc187 100644 --- a/net/spdy/spdy_stream.cc +++ b/net/spdy/spdy_stream.cc
@@ -20,6 +20,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_usage_estimator.h" #include "base/values.h" +#include "net/base/load_timing_info.h" #include "net/log/net_log.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h"
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index b3007f34..050a059 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc
@@ -509,7 +509,7 @@ EXPECT_THAT(rv, IsOk()); base::WeakPtr<SpdySession> spdy_session = - http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( + http_session->spdy_session_pool()->CreateAvailableSessionFromSocketHandle( key, is_trusted_proxy, std::move(connection), net_log); // Failure is reported asynchronously. EXPECT_TRUE(spdy_session); @@ -612,7 +612,7 @@ handle->SetSocket(std::make_unique<FakeSpdySessionClientSocket>( expected_status == OK ? ERR_IO_PENDING : expected_status)); base::WeakPtr<SpdySession> spdy_session = - pool->CreateAvailableSessionFromSocket( + pool->CreateAvailableSessionFromSocketHandle( key, /*is_trusted_proxy=*/false, std::move(handle), NetLogWithSource()); // Failure is reported asynchronously.
diff --git a/net/third_party/quic/platform/impl/quic_flag_utils_impl.h b/net/third_party/quic/platform/impl/quic_flag_utils_impl.h index 417a980..3fa531e 100644 --- a/net/third_party/quic/platform/impl/quic_flag_utils_impl.h +++ b/net/third_party/quic/platform/impl/quic_flag_utils_impl.h
@@ -8,12 +8,12 @@ #include "base/logging.h" #define QUIC_RELOADABLE_FLAG_COUNT_IMPL(flag) \ - DVLOG(1) << "FLAG_" #flag ": " << FLAGS_quic_reloadable_flag_##flag + DVLOG(2) << "FLAG_" #flag ": " << FLAGS_quic_reloadable_flag_##flag #define QUIC_RELOADABLE_FLAG_COUNT_N_IMPL(flag, instance, total) \ QUIC_RELOADABLE_FLAG_COUNT_IMPL(flag) #define QUIC_RESTART_FLAG_COUNT_IMPL(flag) \ - DVLOG(1) << "FLAG_" #flag ": " << FLAGS_quic_restart_flag_##flag + DVLOG(2) << "FLAG_" #flag ": " << FLAGS_quic_restart_flag_##flag #define QUIC_RESTART_FLAG_COUNT_N_IMPL(flag, instance, total) \ QUIC_RESTART_FLAG_COUNT_IMPL(flag)
diff --git a/printing/pwg_raster_settings.h b/printing/pwg_raster_settings.h index fe7ae4c..b1c3709 100644 --- a/printing/pwg_raster_settings.h +++ b/printing/pwg_raster_settings.h
@@ -5,6 +5,8 @@ #ifndef PRINTING_PWG_RASTER_SETTINGS_H_ #define PRINTING_PWG_RASTER_SETTINGS_H_ +#include "printing/print_job_constants.h" + namespace printing { enum PwgRasterTransformType { @@ -16,6 +18,7 @@ }; struct PwgRasterSettings { + DuplexMode duplex_mode; // How to transform odd-numbered pages. PwgRasterTransformType odd_page_transform; // Rotate all pages (on top of odd-numbered page transform).
diff --git a/remoting/host/file_transfer/BUILD.gn b/remoting/host/file_transfer/BUILD.gn index 5a530c65..d898537 100644 --- a/remoting/host/file_transfer/BUILD.gn +++ b/remoting/host/file_transfer/BUILD.gn
@@ -46,6 +46,7 @@ "ensure_user.h", "file_chooser.h", "file_chooser_common_win.h", + "file_chooser_mac.mm", "file_chooser_main_win.cc", "file_chooser_win.cc", "file_transfer_message_handler.cc",
diff --git a/remoting/host/file_transfer/file_chooser_mac.mm b/remoting/host/file_transfer/file_chooser_mac.mm new file mode 100644 index 0000000..45f7389e --- /dev/null +++ b/remoting/host/file_transfer/file_chooser_mac.mm
@@ -0,0 +1,168 @@ +// Copyright 2019 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 "remoting/host/file_transfer/file_chooser.h" + +#import <Cocoa/Cocoa.h> + +#include <utility> + +#include "base/bind.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_nsobject.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/sequence_bound.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "remoting/base/string_resources.h" +#include "ui/base/l10n/l10n_util_mac.h" + +@interface FileTransferOpenPanelDelegate : NSObject <NSOpenSavePanelDelegate> { +} +- (BOOL)panel:(id)sender shouldEnableURL:(NSURL*)url; +- (BOOL)panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError; +@end + +@implementation FileTransferOpenPanelDelegate +- (BOOL)panel:(id)sender shouldEnableURL:(NSURL*)url { + return [url isFileURL]; +} + +- (BOOL)panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError { + // Refuse to accept users closing the dialog with a key repeat, since the key + // may have been first pressed while the user was looking at something else. + if ([[NSApp currentEvent] type] == NSKeyDown && + [[NSApp currentEvent] isARepeat]) { + return NO; + } + + return YES; +} +@end + +namespace remoting { + +namespace { + +class FileChooserMac; + +class MacFileChooserOnUiThread { + public: + MacFileChooserOnUiThread( + scoped_refptr<base::SequencedTaskRunner> caller_task_runner, + base::WeakPtr<FileChooserMac> file_chooser_mac); + + ~MacFileChooserOnUiThread(); + + void Show(); + + private: + void RunCallback(FileChooser::Result result); + + base::scoped_nsobject<FileTransferOpenPanelDelegate> delegate_; + base::scoped_nsobject<NSOpenPanel> open_panel_; + scoped_refptr<base::SequencedTaskRunner> caller_task_runner_; + base::WeakPtr<FileChooserMac> file_chooser_mac_; + + DISALLOW_COPY_AND_ASSIGN(MacFileChooserOnUiThread); +}; + +class FileChooserMac : public FileChooser { + public: + FileChooserMac(scoped_refptr<base::SequencedTaskRunner> ui_task_runner, + ResultCallback callback); + + ~FileChooserMac() override; + + // FileChooser implementation. + void Show() override; + + void RunCallback(FileChooser::Result result); + + private: + FileChooser::ResultCallback callback_; + base::SequenceBound<MacFileChooserOnUiThread> mac_file_chooser_on_ui_thread_; + base::WeakPtrFactory<FileChooserMac> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FileChooserMac); +}; + +MacFileChooserOnUiThread::MacFileChooserOnUiThread( + scoped_refptr<base::SequencedTaskRunner> caller_task_runner, + base::WeakPtr<FileChooserMac> file_chooser_mac) + : delegate_([FileTransferOpenPanelDelegate new]), + caller_task_runner_(std::move(caller_task_runner)), + file_chooser_mac_(std::move(file_chooser_mac)) {} + +MacFileChooserOnUiThread::~MacFileChooserOnUiThread() { + if (open_panel_) { + // Will synchronously invoke completion handler. + [open_panel_ cancel:open_panel_]; + } +} + +void MacFileChooserOnUiThread::Show() { + DCHECK(!open_panel_); + open_panel_.reset([NSOpenPanel openPanel], base::scoped_policy::RETAIN); + [open_panel_ + setMessage:l10n_util::GetNSString(IDS_DOWNLOAD_FILE_DIALOG_TITLE)]; + [open_panel_ setAllowsMultipleSelection:NO]; + [open_panel_ setCanChooseFiles:YES]; + [open_panel_ setCanChooseDirectories:NO]; + [open_panel_ setDelegate:delegate_]; + [open_panel_ beginWithCompletionHandler:^(NSModalResponse result) { + if (result == NSFileHandlingPanelOKButton) { + NSURL* url = [[open_panel_ URLs] objectAtIndex:0]; + if (![url isFileURL]) { + // Delegate should prevent this. + RunCallback(protocol::MakeFileTransferError( + FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR)); + } + RunCallback(base::mac::NSStringToFilePath([url path])); + } else { + RunCallback(protocol::MakeFileTransferError( + FROM_HERE, protocol::FileTransfer_Error_Type_CANCELED)); + } + open_panel_.reset(); + }]; + // Bring to front. + [NSApp activateIgnoringOtherApps:YES]; +} + +void MacFileChooserOnUiThread::RunCallback(FileChooser::Result result) { + caller_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&FileChooserMac::RunCallback, file_chooser_mac_, + std::move(result))); +} + +FileChooserMac::FileChooserMac( + scoped_refptr<base::SequencedTaskRunner> ui_task_runner, + ResultCallback callback) + : callback_(std::move(callback)), weak_ptr_factory_(this) { + mac_file_chooser_on_ui_thread_ = + base::SequenceBound<MacFileChooserOnUiThread>( + ui_task_runner, base::SequencedTaskRunnerHandle::Get(), + weak_ptr_factory_.GetWeakPtr()); +} + +void FileChooserMac::Show() { + mac_file_chooser_on_ui_thread_.Post(FROM_HERE, + &MacFileChooserOnUiThread::Show); +} + +void FileChooserMac::RunCallback(FileChooser::Result result) { + std::move(callback_).Run(std::move(result)); +} + +FileChooserMac::~FileChooserMac() = default; + +} // namespace + +std::unique_ptr<FileChooser> FileChooser::Create( + scoped_refptr<base::SequencedTaskRunner> ui_task_runner, + ResultCallback callback) { + return std::make_unique<FileChooserMac>(std::move(ui_task_runner), + std::move(callback)); +} + +} // namespace remoting
diff --git a/services/device/public/cpp/hid/hid_collection.cc b/services/device/public/cpp/hid/hid_collection.cc index 473c5cf65..60c7b34 100644 --- a/services/device/public/cpp/hid/hid_collection.cc +++ b/services/device/public/cpp/hid/hid_collection.cc
@@ -5,6 +5,7 @@ #include "services/device/public/cpp/hid/hid_collection.h" #include <algorithm> +#include <limits> #include <utility> #include "base/memory/ptr_util.h" @@ -13,6 +14,20 @@ namespace device { +namespace { +// The maximum value of the report size for a single item in a HID report is 32 +// bits. From the Device Class Definition for HID v1.11, sec. 8.2: "An item +// field cannot span more than 4 bytes in a report. For example, a 32-bit item +// must start on a byte boundary to satisfy this condition." +static constexpr uint32_t kMaxItemReportSizeBits = 32; + +// On Windows, HID report length is reported (in bytes) as a USHORT which +// imposes a practical limit of 2^16-1 bytes. Apply the same upper limit when +// computing the maximum report size. +static constexpr uint64_t kMaxReasonableReportLengthBits = + std::numeric_limits<uint16_t>::max() * 8; +} // namespace + HidCollection::HidCollection(HidCollection* parent, uint32_t usage_page, uint32_t usage, @@ -203,10 +218,30 @@ for (const auto& entry : report_lists) { entry.max_report_bits = 0; for (const auto& report : entry.reports) { - size_t report_bits = 0; - for (const auto& item : report.second) - report_bits += item->GetReportSize() * item->GetReportCount(); - entry.max_report_bits = std::max(entry.max_report_bits, report_bits); + uint64_t report_bits = 0; + for (const auto& item : report.second) { + uint64_t report_size = item->GetReportSize(); + // Skip reports with items that have invalid report sizes. + if (report_size > kMaxItemReportSizeBits) { + report_bits = 0; + break; + } + // Report size and report count are both 32-bit values. A 64-bit integer + // type is needed to avoid overflow when computing the product. + uint64_t report_count = item->GetReportCount(); + uint64_t item_bits = report_size * report_count; + // Ignore this report if adding the size of this item would extend the + // total report length beyond the reasonable maximum. + if (item_bits > kMaxReasonableReportLengthBits || + report_bits > kMaxReasonableReportLengthBits - item_bits) { + report_bits = 0; + break; + } + report_bits += item_bits; + } + DCHECK_LE(report_bits, kMaxReasonableReportLengthBits); + entry.max_report_bits = + std::max(entry.max_report_bits, size_t{report_bits}); } } return collection_info;
diff --git a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc index 3f63b6a..ff83d0b 100644 --- a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc +++ b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc
@@ -1596,4 +1596,86 @@ kBelkinNostromoMouseAndExtraSize); } +TEST_F(HidReportDescriptorTest, InvalidReportSizeIgnored) { + // Report size can be at most 32 bits. Make sure a report item with invalid + // size does not affect the maximum report size. The descriptor below + // describes a report with one 64-bit constant field. + static const uint8_t kInvalidReportSizeDescriptor[] = { + 0xA0, // Collection + 0x95, 0x01, // Report Count (1) + 0x75, 0x40, // Report Size (64) + 0x90 // Output + }; + static const size_t kInvalidReportSizeDescriptorSize = + base::size(kInvalidReportSizeDescriptor); + auto info = HidCollectionInfo::New(); + info->usage = HidUsageAndPage::New(0, 0); + AddTopCollectionInfo(std::move(info)); + // Maximum report sizes should not be affected by the invalid report item. + ValidateDetails(false, 0, 0, 0, kInvalidReportSizeDescriptor, + kInvalidReportSizeDescriptorSize); + + // The report item with invalid size should still be included in the + // collection info. + auto* top = AddTopCollection(0, kCollectionTypePhysical); + SetReportSizeAndCount(64, 1); + AddReportConstant(top, kOutput, kNonNullableArray); + ValidateCollections(kInvalidReportSizeDescriptor, + kInvalidReportSizeDescriptorSize); +} + +TEST_F(HidReportDescriptorTest, ReasonablyHugeReportNotIgnored) { + // The descriptor below defines a 2^16-1 byte output report. Larger reports + // are considered unreasonable and are ignored in the max report size + // calculation. + static const uint8_t kReasonablyHugeReportDescriptor[] = { + 0xA0, // Collection + 0x96, 0xff, 0xff, // Report Count (65535) + 0x75, 0x08, // Report Size (8) + 0x90 // Output + }; + static const size_t kReasonablyHugeReportDescriptorSize = + base::size(kReasonablyHugeReportDescriptor); + auto info = HidCollectionInfo::New(); + info->usage = HidUsageAndPage::New(0, 0); + AddTopCollectionInfo(std::move(info)); + // Maximum report sizes should include the huge report. + ValidateDetails(false, 0, 65535, 0, kReasonablyHugeReportDescriptor, + kReasonablyHugeReportDescriptorSize); + + auto* top = AddTopCollection(0, kCollectionTypePhysical); + SetReportSizeAndCount(8, 65535); + AddReportConstant(top, kOutput, kNonNullableArray); + ValidateCollections(kReasonablyHugeReportDescriptor, + kReasonablyHugeReportDescriptorSize); +} + +TEST_F(HidReportDescriptorTest, UnreasonablyHugeReportIgnored) { + // The descriptor below defines a 2^16 byte output report. The report is + // larger than the maximum report size considered reasonable and will be + // ignored when computing the max report size. + static const uint8_t kUnreasonablyHugeReportDescriptor[] = { + 0xA0, // Collection + 0x97, 0x00, 0x00, 0x01, 0x00, // Report Count (65536) + 0x75, 0x08, // Report Size (8) + 0x90 // Output + }; + static const size_t kUnreasonablyHugeReportDescriptorSize = + base::size(kUnreasonablyHugeReportDescriptor); + auto info = HidCollectionInfo::New(); + info->usage = HidUsageAndPage::New(0, 0); + AddTopCollectionInfo(std::move(info)); + // Maximum report sizes should not be affected by the huge report. + ValidateDetails(false, 0, 0, 0, kUnreasonablyHugeReportDescriptor, + kUnreasonablyHugeReportDescriptorSize); + + // The unreasonably huge report item should still be included in the + // collection info. + auto* top = AddTopCollection(0, kCollectionTypePhysical); + SetReportSizeAndCount(8, 65536); + AddReportConstant(top, kOutput, kNonNullableArray); + ValidateCollections(kUnreasonablyHugeReportDescriptor, + kUnreasonablyHugeReportDescriptorSize); +} + } // namespace device
diff --git a/services/device/public/cpp/hid/hid_report_item.h b/services/device/public/cpp/hid/hid_report_item.h index eae86f8..44d1d9dd 100644 --- a/services/device/public/cpp/hid/hid_report_item.h +++ b/services/device/public/cpp/hid/hid_report_item.h
@@ -81,10 +81,10 @@ bool HasNull() const { return report_info_.null; } // Returns the width of each field defined by this item, in bits. - uint16_t GetReportSize() const { return global_.report_size; } + uint32_t GetReportSize() const { return global_.report_size; } // Returns the number of fields defined by this item. - uint16_t GetReportCount() const { return global_.report_count; } + uint32_t GetReportCount() const { return global_.report_count; } // Returns a 32-bit value representing a unit definition for the current item, // or 0 if the item is not assigned a unit.
diff --git a/services/service_manager/public/cpp/connector.h b/services/service_manager/public/cpp/connector.h index faba682..3fc1ebd 100644 --- a/services/service_manager/public/cpp/connector.h +++ b/services/service_manager/public/cpp/connector.h
@@ -169,11 +169,11 @@ } template <typename Interface> - void BindInterface(const std::string& service_name, + void BindInterface(const ServiceFilter& filter, mojo::InterfaceRequest<Interface> request, mojom::BindInterfacePriority priority) { - return BindInterface(ServiceFilter::ByName(service_name), Interface::Name_, - request.PassMessagePipe(), priority, nullptr); + return BindInterface(filter, Interface::Name_, request.PassMessagePipe(), + priority, {}); } void BindInterface(const ServiceFilter& filter,
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc index c50862b3..6d5525c6 100644 --- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -130,8 +130,10 @@ public: ThreadLocalEventSink( std::unique_ptr<perfetto::StartupTraceWriter> trace_writer, + perfetto::BufferID target_buffer, bool thread_will_flush) : trace_writer_(std::move(trace_writer)), + target_buffer_(target_buffer), thread_will_flush_(thread_will_flush) { #if DCHECK_IS_ON() static std::atomic<int32_t> id_counter(1); @@ -432,8 +434,11 @@ trace_writer_->Flush(); } + perfetto::BufferID target_buffer() const { return target_buffer_; } + private: std::unique_ptr<perfetto::StartupTraceWriter> trace_writer_; + perfetto::BufferID target_buffer_; const bool thread_will_flush_; ChromeEventBundleHandle event_bundle_; perfetto::TraceWriter::TracePacketHandle trace_packet_handle_; @@ -504,7 +509,8 @@ DCHECK(!producer_client_); producer_client_ = producer_client; - target_buffer_ = data_source_config.target_buffer(); + target_buffer_.store(data_source_config.target_buffer(), + std::memory_order_relaxed); // Reduce lock contention by binding the registry without holding the lock. unbound_writer_registry = std::move(startup_writer_registry_); } @@ -552,7 +558,7 @@ base::AutoLock lock(lock_); DCHECK(producer_client_); producer_client_ = nullptr; - target_buffer_ = 0; + target_buffer_.store(0, std::memory_order_relaxed); } if (was_enabled) { @@ -629,12 +635,13 @@ if (startup_writer_registry_) { return new ThreadLocalEventSink( startup_writer_registry_->CreateUnboundTraceWriter(), - thread_will_flush); + target_buffer_.load(std::memory_order_relaxed), thread_will_flush); } else if (producer_client_) { return new ThreadLocalEventSink( std::make_unique<perfetto::StartupTraceWriter>( - producer_client_->CreateTraceWriter(target_buffer_)), - thread_will_flush); + producer_client_->CreateTraceWriter( + target_buffer_.load(std::memory_order_relaxed))), + target_buffer_.load(std::memory_order_relaxed), thread_will_flush); } else { return nullptr; } @@ -648,6 +655,24 @@ auto* thread_local_event_sink = static_cast<ThreadLocalEventSink*>(ThreadLocalEventSinkSlot()->Get()); + // Make sure the sink was reset since the last tracing session. Normally, it + // is reset on Flush after the session is disabled. However, it may not have + // been reset if the current thread doesn't support flushing. In that case, we + // need to check here that it writes to the right buffer. + // + // Because we want to avoid locking for each event, we access |target_buffer_| + // racily. It's OK if we don't see it change to the new buffer immediately. In + // that case, the first few trace events may get lost, but we will eventually + // notice that we are writing to the wrong buffer once the change to + // |target_buffer_| has propagated, and reset the sink. Note we will still + // acquire the |lock_| to safely recreate the sink in + // CreateThreadLocalEventSink(). + if (!thread_will_flush && thread_local_event_sink && + GetInstance()->target_buffer_.load(std::memory_order_relaxed) != + thread_local_event_sink->target_buffer()) { + thread_local_event_sink = nullptr; + } + if (!thread_local_event_sink) { thread_local_event_sink = GetInstance()->CreateThreadLocalEventSink(thread_will_flush);
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h index a1f9eb7..b627427 100644 --- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h +++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -121,7 +121,11 @@ base::OnceClosure stop_complete_callback_; base::Lock lock_; // Protects subsequent members. - uint32_t target_buffer_ = 0; + // Regular accesses to |target_buffer_| in conjunction with other fields are + // protected by |lock_|, thus no memory order guarantees are required when + // writing or reading it in those places. Note that OnAddTraceEvent() + // accesses its atomic value racily without holding |lock_|. + std::atomic<uint32_t> target_buffer_{0}; ProducerClient* producer_client_ = nullptr; // We own the registry during startup, but transfer its ownership to the // ProducerClient once the perfetto service is available. Only set if
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc index 23c4f31..86f0de0 100644 --- a/services/tracing/tracing_service.cc +++ b/services/tracing/tracing_service.cc
@@ -46,7 +46,8 @@ mojom::TracedProcessPtr traced_process; connector_->BindInterface( service_manager::ServiceFilter::ForExactIdentity(identity), - &traced_process); + mojo::MakeRequest(&traced_process), + service_manager::mojom::BindInterfacePriority::kBestEffort); auto new_connection_request = mojom::ConnectToTracingRequest::New();
diff --git a/skia/ext/fontmgr_default_fuchsia.cc b/skia/ext/fontmgr_default_fuchsia.cc index 2e6811d..4c09946 100644 --- a/skia/ext/fontmgr_default_fuchsia.cc +++ b/skia/ext/fontmgr_default_fuchsia.cc
@@ -6,7 +6,7 @@ #include <fuchsia/fonts/cpp/fidl.h> -#include "base/fuchsia/component_context.h" +#include "base/fuchsia/service_directory_client.h" #include "third_party/skia/include/core/SkFontMgr.h" #include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" @@ -14,7 +14,7 @@ SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr() { return SkFontMgr_New_Fuchsia( - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToServiceSync<fuchsia::fonts::Provider>()); }
diff --git a/skia/ext/fontmgr_fuchsia_unittest.cc b/skia/ext/fontmgr_fuchsia_unittest.cc index a5381b8..f49be123 100644 --- a/skia/ext/fontmgr_fuchsia_unittest.cc +++ b/skia/ext/fontmgr_fuchsia_unittest.cc
@@ -5,7 +5,7 @@ #include <fuchsia/fonts/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> -#include "base/fuchsia/component_context.h" +#include "base/fuchsia/service_directory_client.h" #include "base/path_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkFontMgr.h" @@ -19,7 +19,7 @@ public: FuchsiaFontManagerTest() { font_manager_ = SkFontMgr_New_Fuchsia( - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToServiceSync<fuchsia::fonts::Provider>()); }
diff --git a/sql/database.cc b/sql/database.cc index e820382..3586bef 100644 --- a/sql/database.cc +++ b/sql/database.cc
@@ -179,8 +179,6 @@ } void Database::ReportDiagnosticInfo(int extended_error, Statement* stmt) { - AssertIOAllowed(); - std::string debug_info = GetDiagnosticInfo(extended_error, stmt); if (!debug_info.empty() && RegisterIntentToUpload()) { DEBUG_ALIAS_FOR_CSTR(debug_buf, debug_info.c_str(), 2000); @@ -231,14 +229,15 @@ void Database::StatementRef::Close(bool forced) { if (stmt_) { - // Call to AssertIOAllowed() cannot go at the beginning of the function - // because Close() is called unconditionally from destructor to clean - // database_. And if this is inactive statement this won't cause any - // disk access and destructor most probably will be called on thread - // not allowing disk access. + // Call to InitScopedBlockingCall() cannot go at the beginning of the + // function because Close() is called unconditionally from destructor to + // clean database_. And if this is inactive statement this won't cause any + // disk access and destructor most probably will be called on thread not + // allowing disk access. // TODO(paivanof@gmail.com): This should move to the beginning // of the function. http://crbug.com/136655. - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); sqlite3_finalize(stmt_); stmt_ = nullptr; } @@ -385,13 +384,14 @@ open_statements_.clear(); if (db_) { - // Call to AssertIOAllowed() cannot go at the beginning of the function - // because Close() must be called from destructor to clean + // Call to InitScopedBlockingCall() cannot go at the beginning of the + // function because Close() must be called from destructor to clean // statement_cache_, it won't cause any disk access and it most probably // will happen on thread not allowing disk access. // TODO(paivanof@gmail.com): This should move to the beginning // of the function. http://crbug.com/136655. - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); // Reseting acquires a lock to ensure no dump is happening on the database // at the same time. Unregister takes ownership of provider and it is safe @@ -426,7 +426,8 @@ } void Database::Preload() { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); if (!db_) { DCHECK(poisoned_) << "Cannot preload null db"; @@ -557,8 +558,6 @@ static const char* kDiagnosticDumpsKey = "DiagnosticDumps"; static int kVersion = 1; - AssertIOAllowed(); - if (histogram_tag_.empty()) return false; @@ -740,8 +739,6 @@ // TODO(shess): Since this is only called in an error situation, it might be // prudent to rewrite in terms of SQLite API calls, and mark the function const. std::string Database::CollectCorruptionInfo() { - AssertIOAllowed(); - // If the file cannot be accessed it is unlikely that an integrity check will // turn up actionable information. const base::FilePath db_path = DbPath(); @@ -825,7 +822,8 @@ } size_t Database::GetAppropriateMmapSize() { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); // How much to map if no errors are found. 50MB encompasses the 99th // percentile of Chrome databases in the wild, so this should be good. @@ -968,7 +966,8 @@ // Create an in-memory database with the existing database's page // size, then backup that database over the existing database. bool Database::Raze() { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); if (!db_) { DCHECK(poisoned_) << "Cannot raze null db"; @@ -1269,7 +1268,9 @@ // caller wishes to execute multiple statements, that should be explicit, and // perhaps tucked into an explicit transaction with rollback in case of error. int Database::ExecuteAndReturnErrorCode(const char* sql) { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); + if (!db_) { DCHECK(poisoned_) << "Illegal use of Database without a db"; return SQLITE_ERROR; @@ -1396,7 +1397,8 @@ scoped_refptr<Database::StatementRef> Database::GetStatementImpl( sql::Database* tracking_db, const char* sql) const { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); DCHECK(sql); DCHECK(!tracking_db || tracking_db == this); @@ -1446,7 +1448,8 @@ } bool Database::IsSQLValid(const char* sql) { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); if (!db_) { DCHECK(poisoned_) << "Illegal use of Database without a db"; return false; @@ -1542,7 +1545,8 @@ bool Database::OpenInternal(const std::string& file_name, Database::Retry retry_flag) { - AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + InitScopedBlockingCall(&scoped_blocking_call); if (db_) { DLOG(DCHECK) << "sql::Database is already open.";
diff --git a/sql/database.h b/sql/database.h index f4b6b29..4ea5d77 100644 --- a/sql/database.h +++ b/sql/database.h
@@ -20,9 +20,9 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "base/sequence_checker.h" #include "base/threading/scoped_blocking_call.h" -#include "base/threading/thread_restrictions.h" #include "base/time/tick_clock.h" #include "sql/internal_api_token.h" #include "sql/statement_id.h" @@ -554,12 +554,12 @@ // |forced| indicates that orderly-shutdown checks should not apply. void CloseInternal(bool forced); - // Check whether the current thread is allowed to make IO calls, but only - // if database wasn't open in memory. Function is inlined to be a no-op in - // official build. - void AssertIOAllowed() const { + // Construct a ScopedBlockingCall to annotate IO calls, but only if + // database wasn't open in memory. + void InitScopedBlockingCall( + base::Optional<base::ScopedBlockingCall>* scoped_blocking_call) const { if (!in_memory_) - base::AssertBlockingAllowedDeprecated(); + scoped_blocking_call->emplace(base::BlockingType::MAY_BLOCK); } // Internal helper for Does*Exist() functions. @@ -622,11 +622,12 @@ // orderly-shutdown checks should apply (see Database::RazeAndClose()). void Close(bool forced); - // Check whether the current thread is allowed to make IO calls, but only - // if database wasn't open in memory. - void AssertIOAllowed() const { + // Construct a ScopedBlockingCall to annotate IO calls, but only if + // database wasn't open in memory. + void InitScopedBlockingCall( + base::Optional<base::ScopedBlockingCall>* scoped_blocking_call) const { if (database_) - database_->AssertIOAllowed(); + database_->InitScopedBlockingCall(scoped_blocking_call); } private:
diff --git a/sql/statement.cc b/sql/statement.cc index e1f8509..1ae42a1 100644 --- a/sql/statement.cc +++ b/sql/statement.cc
@@ -54,7 +54,8 @@ } int Statement::StepInternal(bool timer_flag) { - ref_->AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + ref_->InitScopedBlockingCall(&scoped_blocking_call); if (!CheckValid()) return SQLITE_ERROR; @@ -98,7 +99,8 @@ } void Statement::Reset(bool clear_bound_vars) { - ref_->AssertIOAllowed(); + base::Optional<base::ScopedBlockingCall> scoped_blocking_call; + ref_->InitScopedBlockingCall(&scoped_blocking_call); if (is_valid()) { if (clear_bound_vars) sqlite3_clear_bindings(ref_->stmt());
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 224d3f5c..7e9331e 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -6701,6 +6701,14 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { "args": [ "--num-retries=3" ], @@ -10860,6 +10868,14 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { "args": [ "--num-retries=3" ],
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json index d450cf9..ace672ef 100644 --- a/testing/buildbot/chromium.linux.json +++ b/testing/buildbot/chromium.linux.json
@@ -1735,6 +1735,14 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { "args": [ "--num-retries=3" ], @@ -2456,6 +2464,14 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { "args": [ "--num-retries=3", "--debug" @@ -3133,6 +3149,14 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { "isolate_name": "webkit_python_tests", "name": "webkit_python_tests", "swarming": { @@ -4399,6 +4423,19 @@ } }, { + "isolate_name": "webdriver_wpt_tests", + "name": "webdriver_tests_suite", + "results_handler": "layout tests", + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-16.04" + } + ] + } + }, + { "args": [ "--num-retries=3" ],
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json index 4b7f49e..784671b0 100644 --- a/testing/buildbot/chromium.perf.fyi.json +++ b/testing/buildbot/chromium.perf.fyi.json
@@ -272,7 +272,7 @@ "--benchmarks=loading.desktop.network_service", "-v", "--upload-results", - "--output-format=histograms", + "--output-format=chartjson", "--browser=release" ], "isolate_name": "performance_test_suite",
diff --git a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter index 308818c..3257471 100644 --- a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter +++ b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter
@@ -43,6 +43,7 @@ CrElementsInputTest.All CrElementsProfileAvatarSelectorFocusTest.All CrElementsToggleTest.All +CrExtensionsOptionsPageTest.All CrSettingsAnimatedPagesTest.All CrSettingsFocusRowBehavior.FocusTest CrSettingsSyncPageTest.All
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 25e996a..cf813ff 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -578,6 +578,17 @@ "script": "//testing/xvfb.py", "type": "script", }, + "webdriver_wpt_tests": { + "label": "//:webdriver_wpt_tests", + "type": "script", + "script": "//testing/xvfb.py", + "args": [ + "../../chrome/test/chromedriver/test/run_webdriver_tests.py", + "--chromedriver=chromedriver", + "--isolated-script-test-output=${ISOLATED_OUTDIR}/results.json", + "--test-path=../../third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py", + ], + }, "chromedriver_replay_unittests": { "label": "//chrome/test/chromedriver:chromedriver_replay_unittests", "script": "//chrome/test/chromedriver/log_replay/client_replay_unittest.py",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 7d7faa4c..27c8800 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -3705,6 +3705,13 @@ 'shards': 10, }, }, + 'webdriver_tests_suite': { + 'isolate_name': 'webdriver_wpt_tests', + 'results_handler': 'layout tests', + 'swarming': { + 'shards': 1, + }, + }, }, 'mac_specific_chromium_gtests': {
diff --git a/third_party/blink/public/mojom/frame/lifecycle.mojom b/third_party/blink/public/mojom/frame/lifecycle.mojom index 4a97cc5..c29efbc 100644 --- a/third_party/blink/public/mojom/frame/lifecycle.mojom +++ b/third_party/blink/public/mojom/frame/lifecycle.mojom
@@ -13,3 +13,17 @@ // Not visible, no layout object created. ie. display: none kNotRendered, }; + +// The frame lifecycle state. +enum FrameLifecycleState { + // State is normal running. + kRunning, + // Paused state is used for nested event loops. Does not fire + // frozen, resumed events on the document. + kPaused, + // State is frozen, pause all media. Do not resume media + // when moving to kRunning. + kFrozen, + // State is frozen, auto resume media when moving to kRunning. + kFrozenAutoResumeMedia, +};
diff --git a/third_party/blink/public/platform/web_data.h b/third_party/blink/public/platform/web_data.h index 2780763..597188fc 100644 --- a/third_party/blink/public/platform/web_data.h +++ b/third_party/blink/public/platform/web_data.h
@@ -70,6 +70,7 @@ void Reset(); void Assign(const WebData&); void Assign(const char* data, size_t size); + void Append(const char* data, size_t size); size_t size() const;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h index 0c72c57..00c5a555 100644 --- a/third_party/blink/public/web/web_local_frame.h +++ b/third_party/blink/public/web/web_local_frame.h
@@ -14,6 +14,7 @@ #include "third_party/blink/public/common/frame/sandbox_flags.h" #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-shared.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" +#include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/web_focus_type.h" #include "third_party/blink/public/platform/web_size.h" @@ -731,6 +732,8 @@ virtual void PerformMediaPlayerAction(const WebPoint&, const WebMediaPlayerAction&) = 0; + virtual void SetLifecycleState(mojom::FrameLifecycleState state) = 0; + protected: explicit WebLocalFrame(WebTreeScopeType scope) : WebFrame(scope) {}
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h index 45cc9374..66c8584 100644 --- a/third_party/blink/public/web/web_navigation_params.h +++ b/third_party/blink/public/web/web_navigation_params.h
@@ -163,6 +163,7 @@ // Fills |body_loader| based on the provided static data. static void FillBodyLoader(WebNavigationParams*, base::span<const char> data); + static void FillBodyLoader(WebNavigationParams*, WebData data); // Fills |response| and |body_loader| based on the provided static data. // |url| must be set already.
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h index 01ff6cb..79f633d 100644 --- a/third_party/blink/public/web/web_view.h +++ b/third_party/blink/public/web/web_view.h
@@ -443,9 +443,6 @@ // Suspend and resume --------------------------------------------------- - // Pausing and unpausing current scheduled tasks. - virtual void PausePageScheduledTasks(bool paused) = 0; - // TODO(lfg): Remove this once the refactor of WebView/WebWidget is // completed. virtual WebWidget* MainFrameWidget() = 0;
diff --git a/third_party/blink/renderer/bindings/scripts/v8_interface.py b/third_party/blink/renderer/bindings/scripts/v8_interface.py index 5dcbc51f..7ea0940c 100644 --- a/third_party/blink/renderer/bindings/scripts/v8_interface.py +++ b/third_party/blink/renderer/bindings/scripts/v8_interface.py
@@ -120,7 +120,7 @@ KEY = 'origin_trial_feature_name' # pylint: disable=invalid-name def member_filter(members): - return sorted([member for member in members if member.get(KEY) and not member.get('exposed_test')]) + return sorted([member for member in members if member.get(KEY)]) def member_filter_by_name(members, name): return [member for member in members if member[KEY] == name] @@ -147,10 +147,12 @@ # TODO(chasej): Need to handle method overloads? e.g. # (method['overloads']['secure_context_test_all'] if 'overloads' in method else method['secure_context_test']) feature['needs_secure_context'] = any(member.get('secure_context_test', False) for member in members) + feature['needs_context'] = feature['needs_secure_context'] or any(member.get('exposed_test', False) for member in members) if features: includes.add('platform/bindings/script_state.h') includes.add('core/origin_trials/origin_trials.h') + return features
diff --git a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl index e49940b..91e2184 100644 --- a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl +++ b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
@@ -869,8 +869,10 @@ v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interface_template); ALLOW_UNUSED_LOCAL(signature); {% endif %} - {% if feature.needs_secure_context %} + {% if feature.needs_context %} ExecutionContext* execution_context = ToExecutionContext(isolate->GetCurrentContext()); + {% endif %}{# needs context #} + {% if feature.needs_secure_context %} bool is_secure_context = (execution_context && execution_context->IsSecureContext()); {% endif %}{# needs secure context #} {# Origin-Trial-enabled attributes #} @@ -905,6 +907,7 @@ {% endfor %} {# Origin-Trial-enabled methods (no overloads) #} {% for method in feature.methods %} + {% filter exposed(method.exposed_test) %} {% filter secure_context(method.secure_context_test) %} static constexpr V8DOMConfiguration::MethodConfiguration k{{method.camel_case_name}}Configurations[] = { @@ -916,6 +919,7 @@ interface, signature, config); } {% endfilter %}{# secure_context #} + {% endfilter %}{# exposed_test #} {% endfor %} }
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl index ef4a5e46..92ed409 100644 --- a/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl +++ b/third_party/blink/renderer/bindings/tests/idls/core/test_interface.idl
@@ -91,6 +91,7 @@ void alwaysExposedMethod(); [Exposed=Worker] void workerExposedMethod(); [Exposed=Window] void windowExposedMethod(); + [Exposed=Window,OriginTrialEnabled=TestFeature] void originTrialWindowExposedMethod(); static void alwaysExposedStaticMethod(); [Exposed=Worker] static void workerExposedStaticMethod();
diff --git a/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc b/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc index 377daa6..33e78fb 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/origin_trial_features_for_core.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/bindings/core/v8/origin_trial_features_for_core.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_test_interface.h" #include "third_party/blink/renderer/bindings/core/v8/v8_test_object.h" #include "third_party/blink/renderer/bindings/core/v8/v8_window.h" #include "third_party/blink/renderer/core/context_features/context_feature_settings.h" @@ -57,6 +58,12 @@ } // TODO(iclelland): Extract this common code out of OriginTrialFeaturesForCore // and OriginTrialFeaturesForModules into a block. + if (wrapper_type_info == V8TestInterface::GetWrapperTypeInfo()) { + if (origin_trials::TestFeatureEnabled(execution_context)) { + V8TestInterface::InstallTestFeature( + isolate, world, v8::Local<v8::Object>(), prototype_object, interface_object); + } + } if (wrapper_type_info == V8TestObject::GetWrapperTypeInfo()) { if (origin_trials::FeatureNameEnabled(execution_context)) { V8TestObject::InstallFeatureName( @@ -83,6 +90,13 @@ isolate, world, v8::Local<v8::Object>(), prototype_object, interface_object); } } + if (feature == origin_trials::kTestFeatureTrialName) { + if (context_data->GetExistingConstructorAndPrototypeForType( + V8TestInterface::GetWrapperTypeInfo(), &prototype_object, &interface_object)) { + V8TestInterface::InstallTestFeature( + isolate, world, v8::Local<v8::Object>(), prototype_object, interface_object); + } + } } } // namespace
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc index 872638ab..c0ffad6 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -32,11 +32,13 @@ #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/inspector/console_message.h" +#include "third_party/blink/renderer/core/origin_trials/origin_trials.h" #include "third_party/blink/renderer/platform/bindings/exception_messages.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/get_ptr.h" @@ -1743,6 +1745,12 @@ impl->windowExposedMethod(); } +static void OriginTrialWindowExposedMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + TestInterfaceImplementation* impl = V8TestInterface::ToImpl(info.Holder()); + + impl->originTrialWindowExposedMethod(); +} + static void AlwaysExposedStaticMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { TestInterfaceImplementation::alwaysExposedStaticMethod(); } @@ -3482,6 +3490,12 @@ test_interface_implementation_v8_internal::WindowExposedMethodMethod(info); } +void V8TestInterface::OriginTrialWindowExposedMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { + RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestInterfaceImplementation_originTrialWindowExposedMethod"); + + test_interface_implementation_v8_internal::OriginTrialWindowExposedMethodMethod(info); +} + void V8TestInterface::AlwaysExposedStaticMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestInterfaceImplementation_alwaysExposedStaticMethod"); @@ -4175,6 +4189,45 @@ } } +void V8TestInterface::InstallTestFeature( + v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local<v8::Object> instance, + v8::Local<v8::Object> prototype, + v8::Local<v8::Function> interface) { + v8::Local<v8::FunctionTemplate> interface_template = + V8TestInterface::GetWrapperTypeInfo()->DomTemplate(isolate, world); + v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interface_template); + ALLOW_UNUSED_LOCAL(signature); + ExecutionContext* execution_context = ToExecutionContext(isolate->GetCurrentContext()); + if (execution_context && (execution_context->IsDocument())) { + static constexpr V8DOMConfiguration::MethodConfiguration + kOriginTrialWindowExposedMethodConfigurations[] = { + {"originTrialWindowExposedMethod", V8TestInterface::OriginTrialWindowExposedMethodMethodCallback, 0, v8::None, V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kDoNotCheckAccess, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAllWorlds} + }; + for (const auto& config : kOriginTrialWindowExposedMethodConfigurations) { + V8DOMConfiguration::InstallMethod( + isolate, world, instance, prototype, + interface, signature, config); + } + } +} + +void V8TestInterface::InstallTestFeature( + ScriptState* script_state, v8::Local<v8::Object> instance) { + V8PerContextData* per_context_data = script_state->PerContextData(); + v8::Local<v8::Object> prototype = per_context_data->PrototypeForType( + V8TestInterface::GetWrapperTypeInfo()); + v8::Local<v8::Function> interface = per_context_data->ConstructorForType( + V8TestInterface::GetWrapperTypeInfo()); + ALLOW_UNUSED_LOCAL(interface); + InstallTestFeature(script_state->GetIsolate(), script_state->World(), instance, prototype, interface); +} + +void V8TestInterface::InstallTestFeature(ScriptState* script_state) { + InstallTestFeature(script_state, v8::Local<v8::Object>()); +} + v8::Local<v8::FunctionTemplate> V8TestInterface::DomTemplate( v8::Isolate* isolate, const DOMWrapperWorld& world) { return V8DOMConfiguration::DomClassTemplate(
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.h index c859d00..09775f03 100644 --- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.h +++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.h
@@ -27,6 +27,8 @@ namespace blink { +class ScriptState; + CORE_EXPORT extern WrapperTypeInfo v8_test_interface_wrapper_type_info; class V8TestInterface { @@ -69,6 +71,10 @@ CORE_EXPORT static void RegisterPartial2VoidMethodMethodForPartialInterface(void (*)(const v8::FunctionCallbackInfo<v8::Value>&)); CORE_EXPORT static void RegisterPartial2StaticVoidMethodMethodForPartialInterface(void (*)(const v8::FunctionCallbackInfo<v8::Value>&)); + static void InstallTestFeature(v8::Isolate*, const DOMWrapperWorld&, v8::Local<v8::Object> instance, v8::Local<v8::Object> prototype, v8::Local<v8::Function> interface); + static void InstallTestFeature(ScriptState*, v8::Local<v8::Object> instance); + static void InstallTestFeature(ScriptState*); + // Callback functions CORE_EXPORT static void TestInterfaceAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); @@ -193,6 +199,7 @@ CORE_EXPORT static void AlwaysExposedMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void WorkerExposedMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void WindowExposedMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); + CORE_EXPORT static void OriginTrialWindowExposedMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void AlwaysExposedStaticMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void WorkerExposedStaticMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&); CORE_EXPORT static void WindowExposedStaticMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>&);
diff --git a/third_party/blink/renderer/core/css/style_change_reason.cc b/third_party/blink/renderer/core/css/style_change_reason.cc index ccc91f3..0dc147bf 100644 --- a/third_party/blink/renderer/core/css/style_change_reason.cc +++ b/third_party/blink/renderer/core/css/style_change_reason.cc
@@ -20,6 +20,7 @@ const char kDeclarativeContent[] = "Extension declarativeContent.css"; const char kDesignMode[] = "DesignMode"; const char kFindInvisible[] = "FindInvisible"; +const char kFlatTreeChange[] = "FlatTreeChange"; const char kFontSizeChange[] = "FontSizeChange"; const char kFonts[] = "Fonts"; const char kFrame[] = "Frame";
diff --git a/third_party/blink/renderer/core/css/style_change_reason.h b/third_party/blink/renderer/core/css/style_change_reason.h index 6e0f0fe..a712e1c3 100644 --- a/third_party/blink/renderer/core/css/style_change_reason.h +++ b/third_party/blink/renderer/core/css/style_change_reason.h
@@ -23,6 +23,7 @@ extern const char kDeclarativeContent[]; extern const char kDesignMode[]; extern const char kFrame[]; +extern const char kFlatTreeChange[]; extern const char kFontSizeChange[]; extern const char kFonts[]; extern const char kFullscreen[];
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc index e840bb7..4e9ef78 100644 --- a/third_party/blink/renderer/core/dom/node.cc +++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1448,6 +1448,21 @@ StyleChangeReasonForTracing::Create(style_change_reason::kLazyReattach)); } +void Node::SetForceReattachLayoutTree() { + DCHECK(!GetDocument().GetStyleEngine().InRebuildLayoutTree()); + if (GetForceReattachLayoutTree()) + return; + if (!InActiveDocument()) + return; + if (!IsContainerNode() && !IsTextNode()) + return; + SetFlag(kForceReattachLayoutTree); + if (!NeedsStyleRecalc()) { + // Make sure we traverse down to this node during style recalc. + MarkAncestorsWithChildNeedsStyleRecalc(); + } +} + // FIXME: Shouldn't these functions be in the editing code? Code that asks // questions about HTML in the core DOM class is obviously misplaced. bool Node::CanStartSelection() const { @@ -3049,6 +3064,18 @@ return false; } +void Node::FlatTreeParentChanged() { + // The node changed the flat tree position by being slotted to a new slot or + // slotted for the first time. We need to recalc style since the inheritance + // parent may have changed. + SetNeedsStyleRecalc(kLocalStyleChange, + StyleChangeReasonForTracing::Create( + style_change_reason::kFlatTreeChange)); + // We also need to force a layout tree re-attach since the layout tree parent + // box may have changed. + SetForceReattachLayoutTree(); +} + void Node::Trace(Visitor* visitor) { visitor->Trace(parent_or_shadow_host_node_); visitor->Trace(previous_);
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h index e602421..20dc728 100644 --- a/third_party/blink/renderer/core/dom/node.h +++ b/third_party/blink/renderer/core/dom/node.h
@@ -482,6 +482,11 @@ void MarkAncestorsWithChildNeedsReattachLayoutTree(); + // Mark node for forced layout tree re-attach during next lifecycle update. + // This is to trigger layout tree re-attachment when we cannot detect that we + // need to re-attach based on the computed style changes. This can happen when + // re-slotting shadow host children, for instance. + void SetForceReattachLayoutTree(); bool GetForceReattachLayoutTree() const { return GetFlag(kForceReattachLayoutTree); } @@ -684,6 +689,10 @@ ReattachLayoutTree(context); } void ReattachLayoutTree(AttachContext&); + + // TODO(futhark): Get rid of this method by replacing it with + // SetForceReattachLayoutTree + SetNeedsStyleRecalc, or DetachLayoutTree as + // appropriate. void LazyReattachIfAttached(); // --------------------------------------------------------------------------- @@ -855,6 +864,13 @@ CheckSlotChange(SlotChangeType::kSignalSlotChangeEvent); } + void FlatTreeParentChanged(); + void RemovedFromFlatTree() { + // This node was previously part of the flat tree, but due to slot re- + // assignment it no longer is. We need to detach the layout tree. + DetachLayoutTree(); + } + void SetHasDuplicateAttributes() { SetFlag(kHasDuplicateAttributes); } bool HasDuplicateAttribute() const { return GetFlag(kHasDuplicateAttributes);
diff --git a/third_party/blink/renderer/core/dom/slot_assignment.cc b/third_party/blink/renderer/core/dom/slot_assignment.cc index 50900696..aa8ae72 100644 --- a/third_party/blink/renderer/core/dom/slot_assignment.cc +++ b/third_party/blink/renderer/core/dom/slot_assignment.cc
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/dom/element_traversal.h" #include "third_party/blink/renderer/core/dom/flat_tree_traversal_forbidden_scope.h" #include "third_party/blink/renderer/core/dom/node.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/dom/node_traversal.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/dom/slot_assignment_engine.h" @@ -289,7 +290,7 @@ } else { if (RuntimeEnabledFeatures::FastFlatTreeTraversalEnabled()) child.ClearFlatTreeNodeData(); - child.LazyReattachIfAttached(); + child.RemovedFromFlatTree(); } }
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc index e070814b..3e143fd 100644 --- a/third_party/blink/renderer/core/editing/editing_utilities.cc +++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -1140,10 +1140,10 @@ return nullptr; ContainerNode* root = HighestEditableRoot(p); - Element* ancestor = p.AnchorNode()->IsElementNode() - ? ToElement(p.AnchorNode()) - : p.AnchorNode()->parentElement(); - for (; ancestor; ancestor = ancestor->parentElement()) { + for (Node& runner : NodeTraversal::InclusiveAncestorsOf(*p.AnchorNode())) { + if (!runner.IsElementNode()) + continue; + Element* ancestor = ToElement(&runner); if (root && !HasEditableStyle(*ancestor)) continue; if (ancestor->HasTagName(tag_name))
diff --git a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc index 58e387e..f4f6724e 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc
@@ -139,6 +139,7 @@ void TextSearcherICU::SetPattern(const StringView& pattern, FindOptions options) { + DCHECK_GT(pattern.length(), 0u); options_ = options; SetCaseSensitivity(!(options & kCaseInsensitive)); SetPattern(pattern.Characters16(), pattern.length()); @@ -186,6 +187,12 @@ result.start = static_cast<wtf_size_t>(match_start); result.length = usearch_getMatchedLength(searcher_); + // Might be possible to get zero-length result with some Unicode characters + // that shouldn't actually match but is matched by ICU such as \u0080. + if (result.length == 0u) { + result.start = 0; + return false; + } return true; }
diff --git a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu_test.cc b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu_test.cc index 336507c..c0c01cf 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu_test.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu_test.cc
@@ -99,4 +99,18 @@ EXPECT_EQ(offset_result.length, first_result.length); } +TEST(TextSearcherICUTest, FindControlCharacter) { + TextSearcherICU searcher; + const String& pattern = MakeUTF16(u8"\u0080"); + searcher.SetPattern(pattern, 0); + + const String& text = MakeUTF16("some text"); + searcher.SetText(text.Characters16(), text.length()); + + MatchResultICU result; + EXPECT_FALSE(searcher.NextMatchResult(result)); + EXPECT_EQ(0u, result.start); + EXPECT_EQ(0u, result.length); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc index de693791..8b968dd 100644 --- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc +++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -269,11 +269,14 @@ // https://w3c.github.io/DOM-Parsing/#dfn-generating-a-prefix AtomicString MarkupAccumulator::GeneratePrefix( const AtomicString& new_namespace) { - // 1. Let generated prefix be the concatenation of the string "ns" and the - // current numerical value of prefix index. - AtomicString generated_prefix = "ns" + String::Number(prefix_index_); - // 2. Let the value of prefix index be incremented by one. - ++prefix_index_; + AtomicString generated_prefix; + do { + // 1. Let generated prefix be the concatenation of the string "ns" and the + // current numerical value of prefix index. + generated_prefix = "ns" + String::Number(prefix_index_); + // 2. Let the value of prefix index be incremented by one. + ++prefix_index_; + } while (LookupNamespaceURI(generated_prefix)); // 3. Add to map the generated prefix given the new namespace namespace. AddPrefix(generated_prefix, new_namespace); // 4. Return the value of generated prefix.
diff --git a/third_party/blink/renderer/core/execution_context/pause_state.h b/third_party/blink/renderer/core/execution_context/pause_state.h index 4ec7b3f5..938938f 100644 --- a/third_party/blink/renderer/core/execution_context/pause_state.h +++ b/third_party/blink/renderer/core/execution_context/pause_state.h
@@ -9,6 +9,7 @@ // This enum represents the pausing state of the ExecutionContext. +// TODO(dtapuska): Remove this enum and use FrameLifecycleState instead. enum class PauseState { // Pause tasks only. Used for nested event loops (alert, print). kPaused,
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc index a0036615..b58f14a 100644 --- a/third_party/blink/renderer/core/exported/web_navigation_params.cc +++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -90,6 +90,19 @@ } // static +void WebNavigationParams::FillBodyLoader(WebNavigationParams* params, + WebData data) { + params->response.SetExpectedContentLength(data.size()); + auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>(); + scoped_refptr<SharedBuffer> buffer = data; + if (buffer) + body_loader->Write(*buffer); + body_loader->Finish(); + params->body_loader = std::move(body_loader); + params->is_static_data = true; +} + +// static void WebNavigationParams::FillStaticResponse(WebNavigationParams* params, WebString mime_type, WebString text_encoding,
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc index 03b5aa0a..77be24a 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -726,10 +726,6 @@ GetPage()->AcceptLanguagesChanged(); } -void WebViewImpl::PausePageScheduledTasks(bool paused) { - GetPage()->SetPaused(paused); -} - WebInputEventResult WebViewImpl::HandleKeyEvent(const WebKeyboardEvent& event) { DCHECK((event.GetType() == WebInputEvent::kRawKeyDown) || (event.GetType() == WebInputEvent::kKeyDown) ||
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h index 83f02c7..e77ab1e 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.h +++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -172,7 +172,6 @@ void DisableAutoResizeMode() override; void PerformPluginAction(const WebPluginAction&, const gfx::Point&) override; void AudioStateChanged(bool is_audio_playing) override; - void PausePageScheduledTasks(bool paused) override; WebHitTestResult HitTestResultAt(const gfx::Point&) override; WebHitTestResult HitTestResultForTap(const gfx::Point&, const WebSize&) override;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc index 82c416ad..c27cd18 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1753,4 +1753,54 @@ Loader().StopAllLoaders(); } +void LocalFrame::PauseContext() { + // TODO(dtapuska): Eventually get rid of PauseState. + PauseState pause_state = + lifecycle_state_ == mojom::FrameLifecycleState::kFrozenAutoResumeMedia + ? PauseState::kFrozen + : PauseState::kPaused; + if (Document* document = GetDocument()) { + document->Fetcher()->SetDefersLoading(true); + document->PauseScheduledTasks(pause_state); + } + Loader().SetDefersLoading(true); + GetFrameScheduler()->SetPaused(true); +} + +void LocalFrame::UnpauseContext() { + if (Document* document = GetDocument()) { + document->Fetcher()->SetDefersLoading(false); + document->UnpauseScheduledTasks(); + } + Loader().SetDefersLoading(false); + GetFrameScheduler()->SetPaused(false); +} + +void LocalFrame::SetLifecycleState(mojom::FrameLifecycleState state) { + if (state == lifecycle_state_) + return; + bool is_frozen = lifecycle_state_ != mojom::FrameLifecycleState::kRunning; + bool freeze = state != mojom::FrameLifecycleState::kRunning; + + // TODO(dtapuska): Determine if we should dispatch events if we are + // transitioning across frozen states. ie. kPaused->kFrozen should + // pause media. + + // If we are transitioning from one frozen state to another just return. + if (is_frozen == freeze) + return; + mojom::FrameLifecycleState old_state = lifecycle_state_; + lifecycle_state_ = state; + + if (freeze) { + if (lifecycle_state_ != mojom::FrameLifecycleState::kPaused) + DidFreeze(); + PauseContext(); + } else { + UnpauseContext(); + if (old_state != mojom::FrameLifecycleState::kPaused) + DidResume(); + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h index 92ea373..44c3bfe7 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -34,6 +34,7 @@ #include "base/macros.h" #include "mojo/public/cpp/bindings/strong_binding_set.h" #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-blink.h" +#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h" #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-blink.h" #include "third_party/blink/public/mojom/loader/previews_resource_loading_hints.mojom-blink.h" #include "third_party/blink/public/platform/reporting.mojom-blink.h" @@ -43,6 +44,7 @@ #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h" #include "third_party/blink/renderer/core/dom/weak_identifier_map.h" #include "third_party/blink/renderer/core/editing/forward.h" +#include "third_party/blink/renderer/core/execution_context/pause_state.h" #include "third_party/blink/renderer/core/frame/frame.h" #include "third_party/blink/renderer/core/frame/frame_types.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" @@ -433,6 +435,8 @@ // To be called from OomInterventionImpl. void ForciblyPurgeV8Memory(); + void SetLifecycleState(mojom::FrameLifecycleState state); + private: friend class FrameNavigationDisabler; @@ -478,6 +482,9 @@ void SetFrameColorOverlay(SkColor color); + void PauseContext(); + void UnpauseContext(); + std::unique_ptr<FrameScheduler> frame_scheduler_; // Holds all PauseSubresourceLoadingHandles allowing either |this| to delete @@ -563,6 +570,9 @@ const bool is_save_data_enabled_; std::unique_ptr<FrameOverlay> frame_color_overlay_; + + mojom::FrameLifecycleState lifecycle_state_ = + mojom::FrameLifecycleState::kRunning; }; inline FrameLoader& LocalFrame::Loader() const {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index 2a21139c..27e1529c 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -285,7 +285,6 @@ void LocalFrameView::Trace(blink::Visitor* visitor) { visitor->Trace(frame_); - visitor->Trace(parent_); visitor->Trace(fragment_anchor_); visitor->Trace(scrollable_areas_); visitor->Trace(animating_scrollable_areas_); @@ -1970,8 +1969,10 @@ auto* detached_frame_view = this; while (detached_frame_view->is_attached_ && - detached_frame_view != local_frame_view_root) - detached_frame_view = detached_frame_view->parent_.Get(); + detached_frame_view != local_frame_view_root) { + detached_frame_view = detached_frame_view->ParentFrameView(); + CHECK(detached_frame_view); + } if (detached_frame_view == local_frame_view_root) return; @@ -3305,23 +3306,16 @@ } void LocalFrameView::AttachToLayout() { - // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes. CHECK(!is_attached_); if (frame_->GetDocument()) CHECK_NE(Lifecycle().GetState(), DocumentLifecycle::kStopping); is_attached_ = true; - parent_ = ParentFrameView(); - if (!parent_) { - Frame* parent_frame = frame_->Tree().Parent(); - CHECK(parent_frame); - CHECK(parent_frame->IsLocalFrame()); - CHECK(parent_frame->View()); - } - CHECK(parent_); - if (parent_->IsVisible()) + LocalFrameView* parent_view = ParentFrameView(); + CHECK(parent_view); + if (parent_view->IsVisible()) SetParentVisible(true); SetupRenderThrottling(); - subtree_throttled_ = ParentFrameView()->CanThrottleRendering(); + subtree_throttled_ = parent_view->CanThrottleRendering(); // We may have updated paint properties in detached frame subtree for // printing (see UpdateLifecyclePhasesForPrinting()). The paint properties @@ -3333,16 +3327,7 @@ } void LocalFrameView::DetachFromLayout() { - // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes. CHECK(is_attached_); - LocalFrameView* parent = ParentFrameView(); - if (!parent) { - Frame* parent_frame = frame_->Tree().Parent(); - CHECK(parent_frame); - CHECK(parent_frame->IsLocalFrame()); - CHECK(parent_frame->View()); - } - CHECK(parent == parent_); SetParentVisible(false); is_attached_ = false;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h index 752a0b7..778f3ef 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.h +++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -871,7 +871,6 @@ EmbeddedObjectSet part_update_set_; Member<LocalFrame> frame_; - Member<LocalFrameView> parent_; IntRect frame_rect_; bool is_attached_;
diff --git a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc index 65dd524..85fcd95 100644 --- a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc +++ b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
@@ -32,7 +32,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url.h" -#include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/public/web/web_view.h" #include "third_party/blink/renderer/core/dom/class_collection.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -44,6 +43,7 @@ #include "third_party/blink/renderer/core/frame/location.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h" #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" @@ -62,22 +62,22 @@ protected: void SetUp() override { helper_.Initialize(); } - void TearDown() override { - platform_->GetURLLoaderMockFactory() - ->UnregisterAllURLsAndClearMemoryCache(); - } - - void RegisterMockedURLLoad(const std::string& url, - const std::string& file_name) { - url_test_helpers::RegisterMockedURLLoad( - ToKURL(url), - test::CoreTestDataPath(WebString::FromUTF8("mhtml/" + file_name)), - WebString::FromUTF8("multipart/related")); - } - - void LoadURLInTopFrame(const WebURL& url) { - frame_test_helpers::LoadFrame(helper_.GetWebView()->MainFrameImpl(), - url.GetString().Utf8().data()); + void LoadURLInTopFrame(const WebURL& url, const std::string& file_name) { + scoped_refptr<SharedBuffer> buffer = test::ReadFromFile( + test::CoreTestDataPath(WebString::FromUTF8("mhtml/" + file_name))); + WebLocalFrameImpl* frame = helper_.GetWebView()->MainFrameImpl(); + auto params = std::make_unique<WebNavigationParams>(); + params->url = url; + params->response = WebURLResponse(url); + params->response.SetMIMEType("multipart/related"); + params->response.SetHTTPStatusCode(200); + params->response.SetExpectedContentLength(buffer->size()); + auto body_loader = std::make_unique<StaticDataNavigationBodyLoader>(); + body_loader->Write(*buffer); + body_loader->Finish(); + params->body_loader = std::move(body_loader); + frame->CommitNavigation(std::move(params), nullptr /* extra_data */); + frame_test_helpers::PumpPendingRequestsForFrameToLoad(frame); } Page* GetPage() const { return helper_.GetWebView()->GetPage(); } @@ -92,10 +92,7 @@ TEST_F(MHTMLLoadingTest, CheckDomain) { const char kFileURL[] = "file:///simple_test.mht"; - // Register the mocked frame and load it. - WebURL url = ToKURL(kFileURL); - RegisterMockedURLLoad(kFileURL, "simple_test.mht"); - LoadURLInTopFrame(url); + LoadURLInTopFrame(ToKURL(kFileURL), "simple_test.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame); @@ -113,9 +110,7 @@ TEST_F(MHTMLLoadingTest, EnforceSandboxFlags) { const char kURL[] = "http://www.example.com"; - // Register the mocked frame and load it. - RegisterMockedURLLoad(kURL, "page_with_javascript.mht"); - LoadURLInTopFrame(ToKURL(kURL)); + LoadURLInTopFrame(ToKURL(kURL), "page_with_javascript.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame); @@ -159,9 +154,7 @@ TEST_F(MHTMLLoadingTest, EnforceSandboxFlagsInXSLT) { const char kURL[] = "http://www.example.com"; - // Register the mocked frame and load it. - RegisterMockedURLLoad(kURL, "xslt.mht"); - LoadURLInTopFrame(ToKURL(kURL)); + LoadURLInTopFrame(ToKURL(kURL), "xslt.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame); @@ -183,9 +176,7 @@ TEST_F(MHTMLLoadingTest, ShadowDom) { const char kURL[] = "http://www.example.com"; - // Register the mocked frame and load it. - RegisterMockedURLLoad(kURL, "shadow.mht"); - LoadURLInTopFrame(ToKURL(kURL)); + LoadURLInTopFrame(ToKURL(kURL), "shadow.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame); @@ -211,9 +202,7 @@ TEST_F(MHTMLLoadingTest, FormControlElements) { const char kURL[] = "http://www.example.com"; - // Register the mocked frame and load it. - RegisterMockedURLLoad(kURL, "form.mht"); - LoadURLInTopFrame(ToKURL(kURL)); + LoadURLInTopFrame(ToKURL(kURL), "form.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame); @@ -232,9 +221,7 @@ TEST_F(MHTMLLoadingTest, LoadMHTMLContainingSoftLineBreaks) { const char kURL[] = "http://www.example.com"; - // Register the mocked frame and load it. - RegisterMockedURLLoad(kURL, "soft_line_break.mht"); - LoadURLInTopFrame(ToKURL(kURL)); + LoadURLInTopFrame(ToKURL(kURL), "soft_line_break.mht"); ASSERT_TRUE(GetPage()); LocalFrame* frame = ToLocalFrame(GetPage()->MainFrame()); ASSERT_TRUE(frame);
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc index 03e4a1dd..a605fe6c 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2577,4 +2577,9 @@ std::move(devtools_agent_request))); } +void WebLocalFrameImpl::SetLifecycleState(mojom::FrameLifecycleState state) { + DCHECK(GetFrame()); + GetFrame()->SetLifecycleState(state); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h index 53f569c1..985cdbb 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -39,6 +39,7 @@ #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-blink.h" #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h" #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" +#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h" #include "third_party/blink/public/mojom/portal/portal.mojom-blink.h" #include "third_party/blink/public/platform/web_file_system_type.h" #include "third_party/blink/public/web/web_history_commit_type.h" @@ -323,6 +324,8 @@ const WebNavigationInfo&, std::unique_ptr<WebDocumentLoader::ExtraData>) override; + void SetLifecycleState(mojom::FrameLifecycleState state) override; + void InitializeCoreFrame(Page&, FrameOwner*, const AtomicString& name); LocalFrame* GetFrame() const { return frame_.Get(); }
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc index 1b2f27996..c9a77a8 100644 --- a/third_party/blink/renderer/core/html/html_slot_element.cc +++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -232,9 +232,16 @@ flat_tree_children_.push_back(child); } else { flat_tree_children_ = assigned_nodes_; + for (auto& node : old_flat_tree_children) { + // Detach fallback nodes. Host children which are no longer slotted are + // detached in SlotAssignment::RecalcAssignment(). + if (node->parentNode() == this) + node->RemovedFromFlatTree(); + } } - LazyReattachNodesIfNeeded(old_flat_tree_children, flat_tree_children_); + NotifySlottedNodesOfFlatTreeChange(old_flat_tree_children, + flat_tree_children_); } void HTMLSlotElement::DispatchSlotChangeEvent() { @@ -414,9 +421,9 @@ } } -void HTMLSlotElement::LazyReattachNodesByDynamicProgramming( - const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2) { +void HTMLSlotElement::NotifySlottedNodesOfFlatTreeChangeByDynamicProgramming( + const HeapVector<Member<Node>>& old_slotted, + const HeapVector<Member<Node>>& new_slotted) { // Use dynamic programming to minimize the number of nodes being reattached. using LCSTable = std::array<std::array<wtf_size_t, kLCSTableSizeLimit>, kLCSTableSizeLimit>; @@ -428,45 +435,40 @@ DEFINE_STATIC_LOCAL(BacktrackTable*, backtrack_table, (new BacktrackTable)); FillLongestCommonSubsequenceDynamicProgrammingTable( - nodes1, nodes2, *lcs_table, *backtrack_table); + old_slotted, new_slotted, *lcs_table, *backtrack_table); - wtf_size_t r = nodes1.size(); - wtf_size_t c = nodes2.size(); + wtf_size_t r = old_slotted.size(); + wtf_size_t c = new_slotted.size(); while (r > 0 && c > 0) { Backtrack backtrack = (*backtrack_table)[r][c]; if (backtrack == std::make_pair(r - 1, c - 1)) { - DCHECK_EQ(nodes1[r - 1], nodes2[c - 1]); - } else if (backtrack == std::make_pair(r - 1, c)) { - nodes1[r - 1]->LazyReattachIfAttached(); - } else { - DCHECK(backtrack == std::make_pair(r, c - 1)); - nodes2[c - 1]->LazyReattachIfAttached(); + DCHECK_EQ(old_slotted[r - 1], new_slotted[c - 1]); + } else if (backtrack == std::make_pair(r, c - 1)) { + new_slotted[c - 1]->FlatTreeParentChanged(); } std::tie(r, c) = backtrack; } - if (r > 0) { - for (wtf_size_t i = 0; i < r; ++i) - nodes1[i]->LazyReattachIfAttached(); - } else if (c > 0) { + if (c > 0) { for (wtf_size_t i = 0; i < c; ++i) - nodes2[i]->LazyReattachIfAttached(); + new_slotted[i]->FlatTreeParentChanged(); } } -void HTMLSlotElement::LazyReattachNodesIfNeeded( - const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2) { - if (nodes1 == nodes2) +void HTMLSlotElement::NotifySlottedNodesOfFlatTreeChange( + const HeapVector<Member<Node>>& old_slotted, + const HeapVector<Member<Node>>& new_slotted) { + if (old_slotted == new_slotted) return; probe::didPerformSlotDistribution(this); - if (nodes1.size() + 1 > kLCSTableSizeLimit || - nodes2.size() + 1 > kLCSTableSizeLimit) { + if (old_slotted.size() + 1 > kLCSTableSizeLimit || + new_slotted.size() + 1 > kLCSTableSizeLimit) { // Since DP takes O(N^2), we don't use DP if the size is larger than the // pre-defined limit. - LazyReattachNodesNaive(nodes1, nodes2); + NotifySlottedNodesOfFlatTreeChangeNaive(new_slotted); } else { - LazyReattachNodesByDynamicProgramming(nodes1, nodes2); + NotifySlottedNodesOfFlatTreeChangeByDynamicProgramming(old_slotted, + new_slotted); } } @@ -483,14 +485,11 @@ CheckSlotChange(SlotChangeType::kSuppressSlotChangeEvent); } -void HTMLSlotElement::LazyReattachNodesNaive( - const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2) { +void HTMLSlotElement::NotifySlottedNodesOfFlatTreeChangeNaive( + const HeapVector<Member<Node>>& new_slotted) { // TODO(hayato): Use some heuristic to avoid reattaching all nodes - for (auto& node : nodes1) - node->LazyReattachIfAttached(); - for (auto& node : nodes2) - node->LazyReattachIfAttached(); + for (auto& node : new_slotted) + node->FlatTreeParentChanged(); } void HTMLSlotElement::
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h index 674dc52..e5dcacca 100644 --- a/third_party/blink/renderer/core/html/html_slot_element.h +++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -128,13 +128,14 @@ bool HasSlotableChild() const; - void LazyReattachNodesIfNeeded(const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2); - static void LazyReattachNodesNaive(const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2); - static void LazyReattachNodesByDynamicProgramming( - const HeapVector<Member<Node>>& nodes1, - const HeapVector<Member<Node>>& nodes2); + void NotifySlottedNodesOfFlatTreeChange( + const HeapVector<Member<Node>>& old_slotted, + const HeapVector<Member<Node>>& new_slotted); + static void NotifySlottedNodesOfFlatTreeChangeNaive( + const HeapVector<Member<Node>>& new_slotted); + static void NotifySlottedNodesOfFlatTreeChangeByDynamicProgramming( + const HeapVector<Member<Node>>& old_slotted, + const HeapVector<Member<Node>>& new_slotted); void SetNeedsDistributionRecalcWillBeSetNeedsAssignmentRecalc();
diff --git a/third_party/blink/renderer/core/html/html_summary_element.cc b/third_party/blink/renderer/core/html/html_summary_element.cc index 68c1f20..d900ae6 100644 --- a/third_party/blink/renderer/core/html/html_summary_element.cc +++ b/third_party/blink/renderer/core/html/html_summary_element.cc
@@ -20,6 +20,7 @@ #include "third_party/blink/renderer/core/html/html_summary_element.h" +#include "third_party/blink/renderer/core/css/style_change_reason.h" #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/events/keyboard_event.h" @@ -44,7 +45,9 @@ } HTMLSummaryElement::HTMLSummaryElement(Document& document) - : HTMLElement(kSummaryTag, document) {} + : HTMLElement(kSummaryTag, document) { + SetHasCustomStyleCallbacks(); +} LayoutObject* HTMLSummaryElement::CreateLayoutObject( const ComputedStyle& style) { @@ -152,4 +155,14 @@ return IsMainSummary() || HTMLElement::WillRespondToMouseClickEvents(); } +void HTMLSummaryElement::WillRecalcStyle(const StyleRecalcChange) { + if (GetForceReattachLayoutTree() && IsMainSummary()) { + if (Element* marker = MarkerControl()) { + marker->SetNeedsStyleRecalc( + kLocalStyleChange, + StyleChangeReasonForTracing::Create(style_change_reason::kControl)); + } + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_summary_element.h b/third_party/blink/renderer/core/html/html_summary_element.h index 8d13bdb2..4d8974a 100644 --- a/third_party/blink/renderer/core/html/html_summary_element.h +++ b/third_party/blink/renderer/core/html/html_summary_element.h
@@ -43,6 +43,7 @@ void DefaultEventHandler(Event&) override; bool HasActivationBehavior() const override; void DidAddUserAgentShadowRoot(ShadowRoot&) override; + void WillRecalcStyle(const StyleRecalcChange) override; HTMLDetailsElement* DetailsElement() const; bool SupportsFocus() const override;
diff --git a/third_party/blink/renderer/core/html/list_item_ordinal_test.cc b/third_party/blink/renderer/core/html/list_item_ordinal_test.cc index cb64c79..fbbf6da 100644 --- a/third_party/blink/renderer/core/html/list_item_ordinal_test.cc +++ b/third_party/blink/renderer/core/html/list_item_ordinal_test.cc
@@ -16,7 +16,7 @@ TEST_F(ListItemOrdinalTest, ItemInsertedOrRemoved_ListItemsInSlot) { // Note: We should have more than |kLCSTableSizeLimit|(16) child nodes in - // host to invoke |LazyReattachNodesNaive()|. + // host to invoke |NotifySlottedNodesOfFlatTreeChangeNaive()|. SetBodyContent( "<div id=host>" "<li id=item1>1</li>"
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 255d21c5..4c71429 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -568,6 +568,26 @@ CommitData(span.data(), span.size()); } + if (loading_mhtml_archive_) { + ArchiveResource* main_resource = + fetcher_->CreateArchive(url_, data_buffer_); + data_buffer_ = nullptr; + if (main_resource) { + // The origin is the MHTML file, we need to set the base URL to the + // document encoded in the MHTML so relative URLs are resolved properly. + CommitNavigation(main_resource->MimeType(), main_resource->Url()); + // CommitNavigation runs unload listeners which can detach the frame. + if (!frame_) + return; + scoped_refptr<SharedBuffer> data(main_resource->Data()); + for (const auto& span : *data) + CommitData(span.data(), span.size()); + } else { + // Cannot parse mhtml archive - load empty document instead. + CommitNavigation(response_.MimeType()); + } + } + TimeTicks response_end_time = finish_time; if (response_end_time.is_null()) response_end_time = time_of_last_data_received_; @@ -575,12 +595,10 @@ response_end_time = CurrentTimeTicks(); GetTiming().SetResponseEnd(response_end_time); - if (!MaybeCreateArchive()) { - // If this is an empty document, it might not have actually been - // committed yet. Force a commit so that the Document actually gets created. - if (state_ == kProvisional) - CommitNavigation(response_.MimeType()); - } + // If this is an empty document, it might not have actually been + // committed yet. Force a commit so that the Document actually gets created. + if (state_ == kProvisional) + CommitNavigation(response_.MimeType()); DCHECK_GE(state_, kCommitted); if (!frame_) @@ -775,12 +793,6 @@ } } - // The MHTML mime type should be same as the one we check in the browser - // process's IsDownload (navigation_url_loader_network_service.cc). - loading_mhtml_archive_ = - DeprecatedEqualIgnoringCase("multipart/related", response_.MimeType()) || - DeprecatedEqualIgnoringCase("message/rfc822", response_.MimeType()); - if (!ShouldContinueForResponse()) { StopLoading(); return false; @@ -970,28 +982,6 @@ frame_ = nullptr; } -bool DocumentLoader::MaybeCreateArchive() { - // Give the archive machinery a crack at this document. - if (!loading_mhtml_archive_) - return false; - - ArchiveResource* main_resource = fetcher_->CreateArchive(Url(), data_buffer_); - if (!main_resource) - return false; - - data_buffer_ = nullptr; - // The origin is the MHTML file, we need to set the base URL to the document - // encoded in the MHTML so relative URLs are resolved properly. - CommitNavigation(main_resource->MimeType(), main_resource->Url()); - if (!frame_) - return false; - - scoped_refptr<SharedBuffer> data(main_resource->Data()); - for (const auto& span : *data) - CommitData(span.data(), span.size()); - return true; -} - const KURL& DocumentLoader::UnreachableURL() const { return unreachable_url_; } @@ -1160,6 +1150,22 @@ if (!HandleResponse(response)) return; + loading_mhtml_archive_ = + DeprecatedEqualIgnoringCase("multipart/related", response_.MimeType()) || + DeprecatedEqualIgnoringCase("message/rfc822", response_.MimeType()); + if (loading_mhtml_archive_) { + // To commit an mhtml archive synchronously we have to load the whole body + // synchronously and parse it, and it's already loaded in a buffer usually. + // This means we should not defer, and we'll finish loading synchronously + // from StartLoadingBody(). + body_loader_->StartLoadingBody(this, false /* use_isolated_code_cache */); + if (body_loader_) { + // If we did not finish synchronously, load empty document instead. + FinishedLoading(CurrentTimeTicks()); + } + return; + } + if (defers_loading_) body_loader_->SetDefersLoading(true);
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h index bb7228f..8960d67 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -291,8 +291,6 @@ void CommitData(const char* bytes, size_t length); - bool MaybeCreateArchive(); - void StartLoadingInternal(); void FinishedLoading(TimeTicks finish_time); void CancelLoadAfterCSPDenied(const ResourceResponse&);
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index c1c5ae40e3..fdb10ea9 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -240,7 +240,7 @@ // generate start notifications. document_loader_->SetSentDidFinishLoad(); if (frame_->GetPage()->Paused()) - SetDefersLoading(true); + frame_->SetLifecycleState(mojom::FrameLifecycleState::kPaused); TakeObjectSnapshot(); } @@ -250,19 +250,10 @@ } void FrameLoader::SetDefersLoading(bool defers) { - if (Document* document = frame_->GetDocument()) { - document->Fetcher()->SetDefersLoading(defers); - if (defers) - document->PauseScheduledTasks(PauseState::kPaused); - else - document->UnpauseScheduledTasks(); - } - if (document_loader_) document_loader_->SetDefersLoading(defers); if (provisional_document_loader_) provisional_document_loader_->SetDefersLoading(defers); - if (!defers) frame_->GetNavigationScheduler().StartTimer(); }
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc index 7058be4..0157616 100644 --- a/third_party/blink/renderer/core/page/page.cc +++ b/third_party/blink/renderer/core/page/page.cc
@@ -363,13 +363,14 @@ return; paused_ = paused; + mojom::FrameLifecycleState state = paused + ? mojom::FrameLifecycleState::kPaused + : mojom::FrameLifecycleState::kRunning; for (Frame* frame = MainFrame(); frame; frame = frame->Tree().TraverseNext()) { if (!frame->IsLocalFrame()) continue; - LocalFrame* local_frame = ToLocalFrame(frame); - local_frame->Loader().SetDefersLoading(paused); - local_frame->GetFrameScheduler()->SetPaused(paused); + ToLocalFrame(frame)->SetLifecycleState(state); } }
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h index f696f06..47bea74 100644 --- a/third_party/blink/renderer/core/page/page.h +++ b/third_party/blink/renderer/core/page/page.h
@@ -230,9 +230,6 @@ // are allowed to start/continue in this state, and all background processing // is also paused. bool Paused() const { return paused_; } - // This function is public to be used for suspending/resuming Page's tasks. - // Refer to |WebContentImpl::PausePageScheduledTasks| and - // http://crbug.com/822564 for more details. void SetPaused(bool); void SetPageScaleFactor(float);
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h index 168fff1..c1a1f74 100644 --- a/third_party/blink/renderer/core/paint/object_paint_properties.h +++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -142,8 +142,8 @@ // follows: // [ effect ] // | Isolated group to apply various CSS effects, including opacity, - // | mix-blend-mode, and for isolation if a mask needs to be applied or - // | backdrop-dependent children are present. + // | mix-blend-mode, backdrop-filter, and for isolation if a mask needs + // | to be applied or backdrop-dependent children are present. // +-[ filter ] // | Isolated group for CSS filter. // +-[ vertical/horizontal scrollbar effect ]
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index a197d63e..47822f3 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -3262,6 +3262,7 @@ const auto& style = GetLayoutObject().StyleRef(); if (style.BackdropFilter().IsEmpty()) { operations.Clear(); + *backdrop_filter_bounds = gfx::RRectF(); return; } FloatRect reference_box = BackdropFilterReferenceBox();
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index e0b3d16..3158aa38 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -801,6 +801,9 @@ if (blend_mode != SkBlendMode::kSrcOver) return true; + if (!style.BackdropFilter().IsEmpty()) + return true; + if (style.Opacity() != 1.0f || style.HasWillChangeOpacityHint()) return true; @@ -928,6 +931,25 @@ state.blend_mode = WebCoreCompositeToSkiaComposite( kCompositeSourceOver, style.GetBlendMode()); } + if (object_.IsBoxModelObject()) { + if (auto* layer = ToLayoutBoxModelObject(object_).Layer()) { + // Try to use the cached effect for backdrop-filter. + if (properties_->Effect()) { + state.backdrop_filter = properties_->Effect()->BackdropFilter(); + state.backdrop_filter_bounds = + properties_->Effect()->BackdropFilterBounds(); + } + // With BGPT disabled, UpdateFilterReferenceBox gets called from + // CompositedLayerMapping::UpdateGraphicsLayerGeometry, but only + // for composited layers. + if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled() || + layer->GetCompositingState() != kPaintsIntoOwnBacking) { + layer->UpdateFilterReferenceBox(); + } + layer->UpdateCompositorFilterOperationsForBackdropFilter( + state.backdrop_filter, &state.backdrop_filter_bounds); + } + } if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() || RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) { // We may begin to composite our subtree prior to an animation starts, @@ -1025,8 +1047,7 @@ return false; // TODO(trchen): SVG caches filters in SVGResources. Implement it. - if (object.StyleRef().HasFilter() || object.HasReflection() || - object.HasBackdropFilter()) + if (object.StyleRef().HasFilter() || object.HasReflection()) return true; // TODO(flackr): Check for nodes for each KeyframeModel target @@ -1056,9 +1077,6 @@ // Try to use the cached filter. if (properties_->Filter()) { state.filter = properties_->Filter()->Filter(); - state.backdrop_filter = properties_->Filter()->BackdropFilter(); - state.backdrop_filter_bounds = - properties_->Filter()->BackdropFilterBounds(); } // With BGPT disabled, UpdateFilterReferenceBox gets called from @@ -1069,8 +1087,6 @@ layer->UpdateFilterReferenceBox(); } layer->UpdateCompositorFilterOperationsForFilter(state.filter); - layer->UpdateCompositorFilterOperationsForBackdropFilter( - state.backdrop_filter, &state.backdrop_filter_bounds); layer->ClearFilterOnEffectNodeDirty(); }
diff --git a/third_party/blink/renderer/devtools/front_end/emulated_devices/module.json b/third_party/blink/renderer/devtools/front_end/emulated_devices/module.json index f43a66b..7fa5317 100644 --- a/third_party/blink/renderer/devtools/front_end/emulated_devices/module.json +++ b/third_party/blink/renderer/devtools/front_end/emulated_devices/module.json
@@ -821,6 +821,43 @@ }, { "type": "emulated-device", + "order": 1, + "device": { + "show-by-default": false, + "title": "JioPhone 2", + "screen": { + "horizontal": { + "width": 320, + "height": 240 + }, + "device-pixel-ratio": 1, + "vertical": { + "width": 240, + "height": 320 + } + }, + "capabilities": [ + "touch", + "mobile" + ], + "user-agent": "Mozilla/5.0 (Mobile; LYF/F300B/LYF-F300B-001-01-15-130718-i;Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5", + "type": "phone", + "modes": [ + { + "title": "default", + "orientation": "vertical", + "insets": { "left": 0, "top": 0, "right": 0, "bottom": 0 } + }, + { + "title": "default", + "orientation": "horizontal", + "insets": { "left": 0, "top": 0, "right": 0, "bottom": 0 } + } + ] + } + }, + { + "type": "emulated-device", "device": { "show-by-default": false, "title": "Kindle Fire HDX",
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineController.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineController.js index a8d853a..548b1bb 100644 --- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineController.js +++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineController.js
@@ -65,7 +65,7 @@ if (Runtime.experiments.isEnabled('timelineV8RuntimeCallStats') && options.enableJSSampling) categoriesArray.push(disabledByDefault('v8.runtime_stats_sampling')); - if (Runtime.queryParam('timelineTracingJSProfile') && options.enableJSSampling) { + if (!Runtime.queryParam('timelineTracingJSProfileDisabled') && options.enableJSSampling) { categoriesArray.push(disabledByDefault('v8.cpu_profiler')); if (Common.moduleSetting('highResolutionCpuProfiling').get()) categoriesArray.push(disabledByDefault('v8.cpu_profiler.hires')); @@ -175,7 +175,7 @@ */ async _startRecordingWithCategories(categories, enableJSSampling) { SDK.targetManager.suspendAllTargets(); - if (enableJSSampling && !Runtime.queryParam('timelineTracingJSProfile')) + if (enableJSSampling && Runtime.queryParam('timelineTracingJSProfileDisabled')) await this._startProfilingOnAllModels(); if (!this._tracingManager) return;
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js index 1bba2d8..747eb1f 100644 --- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js +++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js
@@ -1912,6 +1912,11 @@ case warnings.LongRecurringHandler: span.textContent = Common.UIString('Recurring handler took %s', Number.millisToString(event.duration, true)); break; + case warnings.LongTask: + span.appendChild( + UI.createDocumentationLink('../../fundamentals/performance/rail#goals-and-guidelines', ls`Long task`)); + span.createTextChild(ls` took ${Number.millisToString(event.duration, true)}.`); + break; case warnings.V8Deopt: span.appendChild(UI.XLink.create( 'https://github.com/GoogleChrome/devtools-docs/issues/53', Common.UIString('Not optimized')));
diff --git a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js index 2b440bc3c..cbf8f54 100644 --- a/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js +++ b/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js
@@ -744,6 +744,11 @@ break; } + case recordTypes.Task: + if (event.duration > TimelineModel.TimelineModel.Thresholds.LongTask) + timelineData.warning = TimelineModel.TimelineModel.WarningType.LongTask; + break; + case recordTypes.EventDispatch: if (event.duration > TimelineModel.TimelineModel.Thresholds.RecurringHandler) timelineData.warning = TimelineModel.TimelineModel.WarningType.LongHandler; @@ -1314,6 +1319,7 @@ * @enum {string} */ TimelineModel.TimelineModel.WarningType = { + LongTask: 'LongTask', ForcedStyle: 'ForcedStyle', ForcedLayout: 'ForcedLayout', IdleDeadlineExceeded: 'IdleDeadlineExceeded', @@ -1337,6 +1343,7 @@ }; TimelineModel.TimelineModel.Thresholds = { + LongTask: 200, Handler: 150, RecurringHandler: 50, ForcedLayout: 30,
diff --git a/third_party/blink/renderer/platform/exported/web_data.cc b/third_party/blink/renderer/platform/exported/web_data.cc index c09b94b..86a304e 100644 --- a/third_party/blink/renderer/platform/exported/web_data.cc +++ b/third_party/blink/renderer/platform/exported/web_data.cc
@@ -48,6 +48,13 @@ private_ = SharedBuffer::Create(data, size); } +void WebData::Append(const char* data, size_t size) { + if (private_.IsNull()) + private_ = SharedBuffer::Create(data, size); + else + private_->Append(data, size); +} + size_t WebData::size() const { if (private_.IsNull()) return 0;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index 392ca60..b59a2d9 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -970,13 +970,17 @@ HashSet<int> pending_render_surfaces; auto& effect_tree = host.property_trees()->effect_tree; for (const auto& layer : layers) { + bool found_backdrop_filter = false; for (auto* effect = effect_tree.Node(layer->effect_tree_index()); - !effect->has_render_surface; + !effect->has_render_surface || !effect->backdrop_filters.IsEmpty(); effect = effect_tree.Node(effect->parent_id)) { + found_backdrop_filter |= !effect->backdrop_filters.IsEmpty(); if (effect->opacity != 1.f && - !pending_render_surfaces.insert(effect->id).is_new_entry) { - // The opacity-only effect is seen the second time, which means that it - // has more than one compositing child and needs a render surface. + (!pending_render_surfaces.insert(effect->id).is_new_entry || + found_backdrop_filter)) { + // The opacity-only effect is seen a second time, which means that it + // has more than one compositing child and needs a render surface. Or + // the opacity effect has a backdrop-filter child. effect->has_render_surface = true; break; }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index c5ea197..f5a6823 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -3346,6 +3346,32 @@ kHasRenderSurface); } +TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfacesWithBackdropChildren) { + // Opacity effect with a single compositing backdrop-filter child. Normally + // the opacity effect would not get a render surface. However, because + // backdrop-filter needs to only filter up to the backdrop root, it always + // gets a render surface. + auto e = CreateOpacityEffect(e0(), 0.4f); + auto a = CreateOpacityEffect(*e, 0.5f); + CompositorFilterOperations blur_filter; + blur_filter.AppendBlurFilter(5); + auto bd = CreateBackdropFilterEffect( + *a, blur_filter, FloatPoint(), + CompositingReason::kActiveBackdropFilterAnimation); + + TestPaintArtifact artifact; + FloatRect r(150, 150, 100, 100); + artifact.Chunk(t0(), c0(), *a).RectDrawing(r, Color::kWhite); + artifact.Chunk(t0(), c0(), *bd).RectDrawing(r, Color::kWhite); + Update(artifact.Build()); + ASSERT_EQ(2u, ContentLayerCount()); + + EXPECT_OPACITY(ContentLayerAt(0)->effect_tree_index(), 0.5, + kHasRenderSurface); + EXPECT_OPACITY(ContentLayerAt(1)->effect_tree_index(), 1.0, + kHasRenderSurface); +} + TEST_P(PaintArtifactCompositorTest, OpacityIndirectlyAffectingTwoLayers) { auto opacity = CreateOpacityEffect(e0(), 0.5f); auto child_composited_effect = CreateOpacityEffect(
diff --git a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h index 12a40b2..9ad4051 100644 --- a/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h +++ b/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
@@ -72,6 +72,33 @@ compositing_reasons); } +inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect( + const EffectPaintPropertyNode& parent, + const TransformPaintPropertyNode& local_transform_space, + const ClipPaintPropertyNode* output_clip, + CompositorFilterOperations backdrop_filter, + const FloatPoint& filters_origin = FloatPoint(), + CompositingReasons compositing_reasons = CompositingReason::kNone) { + EffectPaintPropertyNode::State state; + state.local_transform_space = &local_transform_space; + state.output_clip = output_clip; + state.backdrop_filter = std::move(backdrop_filter); + state.filters_origin = filters_origin; + state.direct_compositing_reasons = compositing_reasons; + return EffectPaintPropertyNode::Create(parent, std::move(state)); +} + +inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect( + const EffectPaintPropertyNode& parent, + CompositorFilterOperations backdrop_filter, + const FloatPoint& paint_offset = FloatPoint(), + CompositingReasons compositing_reasons = CompositingReason::kNone) { + return CreateBackdropFilterEffect( + parent, parent.Unalias().LocalTransformSpace(), + parent.Unalias().OutputClip(), backdrop_filter, paint_offset, + compositing_reasons); +} + inline scoped_refptr<ClipPaintPropertyNode> CreateClip( const ClipPaintPropertyNode& parent, const TransformPaintPropertyNode& local_transform_space,
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint index d6e3678..d0d1a1e 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -425,11 +425,15 @@ crbug.com/923429 compositing/layer-creation/fixed-position-out-of-view-with-backdrop-filter.html [ Failure ] crbug.com/923429 compositing/overflow/scroll-parent-absolute-with-backdrop-filter.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Failure ] +crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ] +crbug.com/923429 css3/filters/backdrop-filter-rendering.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-rendering-no-background.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ] +crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ] + Bug(none) css3/filters/blur-filter-page-scroll-self.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index ce40db9..15a0fc59 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1918,13 +1918,10 @@ crbug.com/497522 css3/filters/backdrop-filter-svg.html [ Failure ] #crbug.com/497522 css3/filters/backdrop-filter-plus-filter.html [ Failure ] # This fails, but only in CAP mode: -crbug.com/497522 css3/filters/backdrop-filter-rendering.html [ Failure ] crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ] -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-border-radius.html [ Failure ] crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-paint-order.html [ Failure ] crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ] crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html [ Pass Failure ] -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ] # Until backdrop filter is enabled by default, backdrop tests will not pass in virtual/stable. Bug(none) virtual/stable/compositing/layer-creation/fixed-position-out-of-view-with-backdrop-filter.html [ Skip ] Bug(none) virtual/stable/compositing/overflow/scroll-parent-absolute-with-backdrop-filter.html [ Skip ] @@ -6023,6 +6020,11 @@ crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.html [ Pass Failure ] crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.worker.html [ Pass Failure ] +crbug.com/927296 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Pass Failure ] + +# Disabled until FreezeFramesOnVisiblity feature enabled by default. +crbug.com/907125 external/wpt/lifecycle/child-display-none.tentative.html [ Skip ] + # Sheriff 2019-02-01 # These are crashy on Win10. The first is also flaky on other platforms, but apparently # this file doesn't support having two lines referring to the same test with nested specificity. @@ -6039,4 +6041,5 @@ # Sheriff 2019-02-06 crbug.com/929122 [ Linux ] external/wpt/html/dom/interfaces.worker.html [ Timeout Failure ] -crbug.com/929355 [ Linux ] external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Failure ] +crbug.com/929355 [ Linux ] external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Pass Failure ] +crbug.com/929435 [ Mac ] external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 8024702..02e81358 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1040,6 +1040,11 @@ "args": ["--enable-blink-features=BidiCaretAffinity,EditingNG"] }, { + "prefix": "freeze-frames", + "base": "external/wpt/lifecycle", + "args": ["--enable-features=FreezeFramesOnVisibility"] + }, + { "prefix": "feature-policy-for-sandbox", "base": "http/tests/navigation", "args": ["--enable-blink-features=FeaturePolicyForSandbox"]
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius-expected.png new file mode 100644 index 0000000..1ead16785 --- /dev/null +++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius.html new file mode 100644 index 0000000..2981af3 --- /dev/null +++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-border-radius.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<div style="opacity: 0.9999;"> + <div class="circle filter"></div> + +</div> + + +<style> +div { + position: absolute; + width: 100px; + height: 100px; + top: 10px; + left: 10px; + background: green; +} +.circle { + top: 30px; + left: 30px; + border-radius: 50px; + background: #ffff0060; +} +.filter { + backdrop-filter: invert(1); +} +</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html new file mode 100644 index 0000000..6914cba3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Long scrolling should work properly</title> +<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> + + + +<style> +.post { + height: 1000px; + width: 300px; + border: 1px solid black; + +} +.before { + height: 213px; + border-top: 0; +} +.scroller { + overflow-y: scroll; + width: 500px; + height: 500px; + will-change: transform; +} +::-webkit-scrollbar { + display: none; +} +</style> + +<p>The number 7 should be visible in the scrolled window below.</p> + +<div id="scroller" class="scroller"> + <div style="position: relative;"> + <div style="position: relative;"> + <div class="post before"></div> + <div class="post">7</div> + </div> + </div> +</div> + +
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html new file mode 100644 index 0000000..aa91023 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Long scrolling should work properly</title> +<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/cssom-view/#scrolling"> +<link rel="match" href="long_scroll_composited-ref.html"> + +<style> +.post { + height: 1000px; + width: 300px; + border: 1px solid black; + +} +.scroller { + overflow-y: scroll; + width: 500px; + height: 500px; + will-change: transform; +} +::-webkit-scrollbar { + display: none; +} +</style> + +<p>The number 7 should be visible in the scrolled window below.</p> + +<div id="scroller" class="scroller"> + <div style="position: relative;"> + <div style="position: relative;"> + <div class="post">0</div> + <div class="post">1</div> + <div class="post">2</div> + <div class="post">3</div> + <div class="post">4</div> + <div class="post">5</div> + <div class="post">6</div> + <div class="post">7</div> + <div class="post">8</div> + <div class="post">9</div> + </div> + </div> +</div> + +<script> +onload = function() { + scroller=document.getElementById("scroller"); + scroller.scrollTop = 6800; +}; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius-ref.html deleted file mode 100644 index e5712a23..0000000 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius-ref.html +++ /dev/null
@@ -1,36 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>backdrop-filter: Should clip using border radius.</title> -<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> - - - -<div> - <div class="circle outside"></div> - <div class="circle inside"></div> -</div> - - -<style> -div { - position: absolute; - width: 100px; - height: 100px; - top: 10px; - left: 10px; - background: green; -} -.circle { - top: 30px; - left: 30px; - border-radius: 50px; - background: #ffff0060; -} -.inside { - background: #ffaf9f; - clip-path: inset(0px 30px 30px 0px); -} -.outside { - background: #ffff9f; -} -</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius.html deleted file mode 100644 index ec93de6..0000000 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-border-radius.html +++ /dev/null
@@ -1,32 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>backdrop-filter: Should clip using border radius.</title> -<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> -<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty"> -<link rel="match" href="backdrop-filter-border-radius-ref.html"> - -<div style="opacity: 0.9999;"> - <div class="circle filter"></div> - -</div> - - -<style> -div { - position: absolute; - width: 100px; - height: 100px; - top: 10px; - left: 10px; - background: green; -} -.circle { - top: 30px; - left: 30px; - border-radius: 50px; - background: #ffff0060; -} -.filter { - backdrop-filter: invert(1); -} -</style>
diff --git a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt new file mode 100644 index 0000000..8729df1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString-expected.txt
@@ -0,0 +1,9 @@ +This is a testharness.js-based test. +PASS check XMLSerializer.serializeToString method could parsing xmldoc to string +PASS Check if the default namespace is correctly reset. +PASS Check if there is no redundant empty namespace declaration. +PASS check XMLSerializer.serializeToString escapes attribute values for roundtripping +PASS Check if generated prefixes match to "ns${index}". +FAIL Check if "ns1" is generated even if the element already has xmlns:ns1. assert_equals: expected "<root xmlns:ns2=\"uri2\"><child xmlns:ns1=\"uri1\" xmlns:ns1=\"uri3\" ns1:attr1=\"value1\"/></root>" but got "<root xmlns:ns2=\"uri2\"><child xmlns:ns1=\"uri1\" xmlns:ns3=\"uri3\" ns3:attr1=\"value1\"/></root>" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html index 7c084e78..d71da49 100644 --- a/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html +++ b/third_party/blink/web_tests/external/wpt/domparsing/XMLSerializer-serializeToString.html
@@ -65,6 +65,16 @@ assert_equals(xmlString, '<root><child1 xmlns:ns1="uri1" ns1:attr1="value1" xmlns:ns2="uri2" ns2:attr2="value2"/><child2 xmlns:ns3="uri3" ns3:attr3="value3"/></root>'); }, 'Check if generated prefixes match to "ns${index}".'); +test(function() { + const input = '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1"/></root>'; + const root = (new DOMParser()).parseFromString(input, 'text/xml').documentElement; + root.firstChild.setAttributeNS('uri3', 'attr1', 'value1'); + const xmlString = (new XMLSerializer()).serializeToString(root); + // According to 'DOM Parsing and Serialization' draft as of 2018-12-11, + // 'generate a prefix' result can conflict with an existing xmlns:ns* declaration. + assert_equals(xmlString, '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1" xmlns:ns1="uri3" ns1:attr1="value1"/></root>'); +}, 'Check if "ns1" is generated even if the element already has xmlns:ns1.'); + </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/lifecycle/child-display-none.tentative.html b/third_party/blink/web_tests/external/wpt/lifecycle/child-display-none.tentative.html new file mode 100644 index 0000000..f76b4e7b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/lifecycle/child-display-none.tentative.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Child frame marked as frozen</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +async_test((t) => { + + var child = document.createElement('iframe'); + + var loaded = false; + var frozen = false; + + window.addEventListener('message', t.step_func((e) => { + if (e.data == "load") { + loaded = true; + } else if (e.data == "freeze") { + assert_true(loaded); + frozen = true; + child.style = "display: block"; + } else if (e.data == "resume") { + assert_true(loaded); + assert_true(frozen); + t.done(); + } + })); + + child.src = "resources/subframe.html"; + document.body.appendChild(child); + child.style = "display: none"; +}, "Child frame frozen"); + +</script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html b/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html new file mode 100644 index 0000000..2f1d70a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/lifecycle/resources/subframe.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script> +window.addEventListener('load', () => { + window.parent.postMessage('load'); +}); + +document.addEventListener('freeze', () => { + window.parent.postMessage('freeze'); +}); + +document.addEventListener('resume', () => { + window.parent.postMessage('resume'); +}); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/META.yml b/third_party/blink/web_tests/external/wpt/webdriver/META.yml new file mode 100644 index 0000000..a397b49 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/META.yml
@@ -0,0 +1,7 @@ +spec: https://w3c.github.io/webdriver/ +suggested_reviewers: + - AutomatedTester + - andreastt + - mjzffr + - shs96c + - whimboo
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/README.md b/third_party/blink/web_tests/external/wpt/webdriver/README.md new file mode 100644 index 0000000..78d9aba --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/README.md
@@ -0,0 +1,17 @@ +# WebDriver specification tests + +Herein lies a set of conformance tests +for the W3C web browser automation specification +known as [WebDriver](http://w3c.github.io/webdriver/webdriver-spec.html). +The purpose of these tests is determine implementation compliance +so that different driver implementations can determine +whether they meet the recognized standard. + +## Chapters of the Spec that still need tests + +We are using a [tracking spreadsheet](https://docs.google.com/spreadsheets/d/1GUK_sdY2cv59VAJNDxZQIfypnOpapSQhMjfcJ9Wc42U/edit#gid=0) +to coordinate work on these tests. Please look there to see who +is working on what, and which areas are currently under-tested. + +The spec contributors and editors can frequently be found on the W3C +#webdriver IRC channel.
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/setup.py b/third_party/blink/web_tests/external/wpt/webdriver/setup.py new file mode 100644 index 0000000..c473961 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/setup.py
@@ -0,0 +1,14 @@ +from setuptools import setup, find_packages + +setup(name="webdriver", + version="1.0", + description="WebDriver client compatible with " + "the W3C browser automation specification.", + author="Mozilla Engineering Productivity", + author_email="tools@lists.mozilla.org", + license="BSD", + packages=find_packages(), + classifiers=["Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", + "Operating System :: OS Independent"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/__init__.py new file mode 100644 index 0000000..0ba172ff --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/__init__.py
@@ -0,0 +1,4 @@ +import pytest + +# Enable pytest assert introspection for assertion helper +pytest.register_assert_rewrite('tests.support.asserts')
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/conftest.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/conftest.py new file mode 100644 index 0000000..42b82c9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/conftest.py
@@ -0,0 +1 @@ +pytest_plugins = "tests.support.fixtures"
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py new file mode 100644 index 0000000..e478e10b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/navigate_to/navigate.py
@@ -0,0 +1,27 @@ +from webdriver.transport import Response + +from tests.support.asserts import assert_error, assert_success +from tests.support.inline import inline + + +def navigate_to(session, url): + return session.transport.send( + "POST", "session/{session_id}/url".format(**vars(session)), + {"url": url}) + + +def test_null_parameter_value(session, http): + path = "/session/{session_id}/url".format(**vars(session)) + with http.post(path, None) as response: + assert_error(Response.from_http(response), "invalid argument") + + +def test_null_response_value(session): + response = navigate_to(session, inline("<div/>")) + value = assert_success(response) + assert value is None + + +def test_no_browsing_context(session, closed_window): + response = navigate_to(session, "foo") + assert_error(response, "no such window")
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/__init__.py new file mode 100644 index 0000000..e5e43c4e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/__init__.py
@@ -0,0 +1,10 @@ +import sys + +from merge_dictionaries import merge_dictionaries + +platform_name = { + "linux2": "linux", + "win32": "windows", + "cygwin": "windows", + "darwin": "mac" +}.get(sys.platform)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/asserts.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/asserts.py new file mode 100644 index 0000000..6771198 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/asserts.py
@@ -0,0 +1,202 @@ +import base64 +import imghdr +import struct + +from webdriver import Element, NoSuchAlertException, WebDriverException + + +# WebDriver specification ID: dfn-error-response-data +errors = { + "element click intercepted": 400, + "element not selectable": 400, + "element not interactable": 400, + "insecure certificate": 400, + "invalid argument": 400, + "invalid cookie domain": 400, + "invalid coordinates": 400, + "invalid element state": 400, + "invalid selector": 400, + "invalid session id": 404, + "javascript error": 500, + "move target out of bounds": 500, + "no such alert": 404, + "no such cookie": 404, + "no such element": 404, + "no such frame": 404, + "no such window": 404, + "script timeout": 500, + "session not created": 500, + "stale element reference": 404, + "timeout": 500, + "unable to set cookie": 500, + "unable to capture screen": 500, + "unexpected alert open": 500, + "unknown command": 404, + "unknown error": 500, + "unknown method": 405, + "unsupported operation": 500, +} + + +def assert_error(response, error_code): + """ + Verify that the provided webdriver.Response instance described + a valid error response as defined by `dfn-send-an-error` and + the provided error code. + + :param response: ``webdriver.Response`` instance. + :param error_code: String value of the expected error code + """ + assert response.status == errors[error_code] + assert "value" in response.body + assert response.body["value"]["error"] == error_code + assert isinstance(response.body["value"]["message"], basestring) + assert isinstance(response.body["value"]["stacktrace"], basestring) + + +def assert_success(response, value=None): + """ + Verify that the provided webdriver.Response instance described + a valid error response as defined by `dfn-send-an-error` and + the provided error code. + + :param response: ``webdriver.Response`` instance. + :param value: Expected value of the response body, if any. + """ + assert response.status == 200, str(response.error) + + if value is not None: + assert response.body["value"] == value + return response.body.get("value") + + +def assert_dialog_handled(session, expected_text, expected_retval): + # If there were any existing dialogs prior to the creation of this + # fixture's dialog, then the "Get Alert Text" command will return + # successfully. In that case, the text must be different than that + # of this fixture's dialog. + try: + assert session.alert.text != expected_text, ( + "User prompt with text '{}' was not handled.".format(expected_text)) + + except NoSuchAlertException: + # If dialog has been closed and no other one is open, check its return value + prompt_retval = session.execute_script(" return window.dialog_return_value;") + assert prompt_retval == expected_retval + + +def assert_files_uploaded(session, element, files): + + def get_file_contents(file_index): + return session.execute_async_script(""" + let files = arguments[0].files; + let index = arguments[1]; + let resolve = arguments[2]; + + var reader = new FileReader(); + reader.onload = function(event) { + resolve(reader.result); + }; + reader.readAsText(files[index]); + """, (element, file_index)) + + def get_uploaded_file_names(): + return session.execute_script(""" + let fileList = arguments[0].files; + let files = []; + + for (var i = 0; i < fileList.length; i++) { + files.push(fileList[i].name); + } + + return files; + """, args=(element,)) + + expected_file_names = [str(f.basename) for f in files] + assert get_uploaded_file_names() == expected_file_names + + for index, f in enumerate(files): + assert get_file_contents(index) == f.read() + + +def assert_is_active_element(session, element): + """Verify that element reference is the active element.""" + from_js = session.execute_script("return document.activeElement") + + if element is None: + assert from_js is None + else: + assert_same_element(session, element, from_js) + + +def assert_same_element(session, a, b): + """Verify that two element references describe the same element.""" + if isinstance(a, dict): + assert Element.identifier in a, "Actual value does not describe an element" + a_id = a[Element.identifier] + elif isinstance(a, Element): + a_id = a.id + else: + raise AssertionError("Actual value is not a dictionary or web element") + + if isinstance(b, dict): + assert Element.identifier in b, "Expected value does not describe an element" + b_id = b[Element.identifier] + elif isinstance(b, Element): + b_id = b.id + else: + raise AssertionError("Expected value is not a dictionary or web element") + + if a_id == b_id: + return + + message = ("Expected element references to describe the same element, " + + "but they did not.") + + # Attempt to provide more information, accounting for possible errors such + # as stale element references or not visible elements. + try: + a_markup = session.execute_script("return arguments[0].outerHTML;", args=(a,)) + b_markup = session.execute_script("return arguments[0].outerHTML;", args=(b,)) + message += " Actual: `%s`. Expected: `%s`." % (a_markup, b_markup) + except WebDriverException: + pass + + raise AssertionError(message) + + +def assert_in_events(session, expected_events): + actual_events = session.execute_script("return window.events") + for expected_event in expected_events: + assert expected_event in actual_events + + +def assert_events_equal(session, expected_events): + actual_events = session.execute_script("return window.events") + assert actual_events == expected_events + + +def assert_element_has_focus(target_element): + session = target_element.session + + active_element = session.execute_script("return document.activeElement") + active_tag = active_element.property("localName") + target_tag = target_element.property("localName") + + assert active_element == target_element, ( + "Focussed element is <%s>, not <%s>" % (active_tag, target_tag)) + + +def assert_move_to_coordinates(point, target, events): + for e in events: + if e["type"] != "mousemove": + assert e["pageX"] == point["x"] + assert e["pageY"] == point["y"] + assert e["target"] == target + + +def assert_png(screenshot): + """Test that screenshot is a Base64 encoded PNG file.""" + image = base64.decodestring(screenshot) + mime_type = imghdr.what("", image) + assert mime_type == "png", "Expected image to be PNG, but it was {}".format(mime_type)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/defaults.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/defaults.py new file mode 100644 index 0000000..c2020527 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/defaults.py
@@ -0,0 +1,8 @@ +SCRIPT_TIMEOUT = 30 +PAGE_LOAD_TIMEOUT = 300 +IMPLICIT_WAIT_TIMEOUT = 0 + +WINDOW_SIZE = (800, 600) + +DRIVER_HOST = '127.0.0.1' +DRIVER_PORT = 4444
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py new file mode 100644 index 0000000..149350c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py
@@ -0,0 +1,227 @@ +import copy +import json +import os +import urlparse + +import pytest +import webdriver + +from tests.support import defaults +from tests.support.helpers import cleanup_session +from tests.support.http_request import HTTPRequest +from tests.support.sync import Poll + + +_current_session = None +_custom_session = False + + +def pytest_configure(config): + # register the capabilities marker + config.addinivalue_line("markers", + "capabilities: mark test to use capabilities") + + +@pytest.fixture +def capabilities(): + """Default capabilities to use for a new WebDriver session.""" + return {} + + + +@pytest.fixture +def add_event_listeners(session): + """Register listeners for tracked events on element.""" + def add_event_listeners(element, tracked_events): + element.session.execute_script(""" + let element = arguments[0]; + let trackedEvents = arguments[1]; + + if (!("events" in window)) { + window.events = []; + } + + for (var i = 0; i < trackedEvents.length; i++) { + element.addEventListener(trackedEvents[i], function (event) { + window.events.push(event.type); + }); + } + """, args=(element, tracked_events)) + return add_event_listeners + + +@pytest.fixture +def create_cookie(session, url): + """Create a cookie""" + def create_cookie(name, value, **kwargs): + if kwargs.get("path", None) is not None: + session.url = url(kwargs["path"]) + + session.set_cookie(name, value, **kwargs) + return session.cookies(name) + + return create_cookie + + +@pytest.fixture +def create_frame(session): + """Create an `iframe` element in the current browsing context and insert it + into the document. Return a reference to the newly-created element.""" + def create_frame(): + append = """ + var frame = document.createElement('iframe'); + document.body.appendChild(frame); + return frame; + """ + return session.execute_script(append) + + return create_frame + + +@pytest.fixture +def create_window(session): + """Open new window and return the window handle.""" + def create_window(): + windows_before = session.handles + name = session.execute_script("window.open()") + assert len(session.handles) == len(windows_before) + 1 + new_windows = list(set(session.handles) - set(windows_before)) + return new_windows.pop() + return create_window + + +@pytest.fixture +def http(configuration): + return HTTPRequest(configuration["host"], configuration["port"]) + + +@pytest.fixture +def server_config(): + return json.loads(os.environ.get("WD_SERVER_CONFIG")) + + +@pytest.fixture(scope="session") +def configuration(): + host = os.environ.get("WD_HOST", defaults.DRIVER_HOST) + port = int(os.environ.get("WD_PORT", str(defaults.DRIVER_PORT))) + capabilities = json.loads(os.environ.get("WD_CAPABILITIES", "{}")) + + return { + "host": host, + "port": port, + "capabilities": capabilities + } + + +@pytest.fixture(scope="function") +def session(capabilities, configuration, request): + """Create and start a session for a test that does not itself test session creation. + + By default the session will stay open after each test, but we always try to start a + new one and assume that if that fails there is already a valid session. This makes it + possible to recover from some errors that might leave the session in a bad state, but + does not demand that we start a new session per test.""" + global _current_session + + # Update configuration capabilities with custom ones from the + # capabilities fixture, which can be set by tests + caps = copy.deepcopy(configuration["capabilities"]) + caps.update(capabilities) + caps = {"alwaysMatch": caps} + + # If there is a session with different capabilities active, end it now + if _current_session is not None and ( + caps != _current_session.requested_capabilities): + _current_session.end() + _current_session = None + + if _current_session is None: + _current_session = webdriver.Session( + configuration["host"], + configuration["port"], + capabilities=caps) + try: + _current_session.start() + except webdriver.error.SessionNotCreatedException: + if not _current_session.session_id: + raise + + # Enforce a fixed default window size + _current_session.window.size = defaults.WINDOW_SIZE + + yield _current_session + + cleanup_session(_current_session) + + +@pytest.fixture(scope="function") +def current_session(): + return _current_session + + +@pytest.fixture +def url(server_config): + def inner(path, protocol="http", query="", fragment=""): + port = server_config["ports"][protocol][0] + host = "%s:%s" % (server_config["browser_host"], port) + return urlparse.urlunsplit((protocol, host, path, query, fragment)) + + inner.__name__ = "url" + return inner + + +@pytest.fixture +def create_dialog(session): + """Create a dialog (one of "alert", "prompt", or "confirm") and provide a + function to validate that the dialog has been "handled" (either accepted or + dismissed) by returning some value.""" + + def create_dialog(dialog_type, text=None): + assert dialog_type in ("alert", "confirm", "prompt"), ( + "Invalid dialog type: '%s'" % dialog_type) + + if text is None: + text = "" + + assert isinstance(text, basestring), "`text` parameter must be a string" + + # Script completes itself when the user prompt has been opened. + # For prompt() dialogs, add a value for the 'default' argument, + # as some user agents (IE, for example) do not produce consistent + # values for the default. + session.execute_async_script(""" + let dialog_type = arguments[0]; + let text = arguments[1]; + + setTimeout(function() { + if (dialog_type == 'prompt') { + window.dialog_return_value = window[dialog_type](text, ''); + } else { + window.dialog_return_value = window[dialog_type](text); + } + }, 0); + """, args=(dialog_type, text)) + + wait = Poll( + session, + timeout=15, + ignored_exceptions=webdriver.NoSuchAlertException, + message="No user prompt with text '{}' detected".format(text)) + wait.until(lambda s: s.alert.text == text) + + return create_dialog + + +@pytest.fixture +def closed_window(session, create_window): + original_handle = session.window_handle + + new_handle = create_window() + session.window_handle = new_handle + + session.close() + assert new_handle not in session.handles, "Unable to close window {}".format(new_handle) + + yield new_handle + + session.window_handle = original_handle
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py new file mode 100644 index 0000000..a0e6a75 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py
@@ -0,0 +1,215 @@ +from __future__ import print_function + +import math +import sys + +import webdriver + +from tests.support import defaults +from tests.support.sync import Poll + + +def ignore_exceptions(f): + def inner(*args, **kwargs): + try: + return f(*args, **kwargs) + except webdriver.error.WebDriverException as e: + #Remove it temporaryily because of the wpt lint + return + inner.__name__ = f.__name__ + return inner + + +def cleanup_session(session): + """Clean-up the current session for a clean state.""" + @ignore_exceptions + def _dismiss_user_prompts(session): + """Dismiss any open user prompts in windows.""" + current_window = session.window_handle + + for window in _windows(session): + session.window_handle = window + try: + session.alert.dismiss() + except webdriver.NoSuchAlertException: + pass + + session.window_handle = current_window + + @ignore_exceptions + def _ensure_valid_window(session): + """If current window was closed, ensure to have a valid one selected.""" + try: + session.window_handle + except webdriver.NoSuchWindowException: + session.window_handle = session.handles[0] + + @ignore_exceptions + def _restore_timeouts(session): + """Restore modified timeouts to their default values.""" + session.timeouts.implicit = defaults.IMPLICIT_WAIT_TIMEOUT + session.timeouts.page_load = defaults.PAGE_LOAD_TIMEOUT + session.timeouts.script = defaults.SCRIPT_TIMEOUT + + @ignore_exceptions + def _restore_window_state(session): + """Reset window to an acceptable size. + + This also includes bringing it out of maximized, minimized, + or fullscreened state. + """ + session.window.size = defaults.WINDOW_SIZE + + @ignore_exceptions + def _restore_windows(session): + """Close superfluous windows opened by the test. + + It will not end the session implicitly by closing the last window. + """ + current_window = session.window_handle + + for window in _windows(session, exclude=[current_window]): + session.window_handle = window + if len(session.handles) > 1: + session.close() + + session.window_handle = current_window + + _restore_timeouts(session) + _ensure_valid_window(session) + _dismiss_user_prompts(session) + _restore_windows(session) + _restore_window_state(session) + _switch_to_top_level_browsing_context(session) + + +@ignore_exceptions +def _switch_to_top_level_browsing_context(session): + """If the current browsing context selected by WebDriver is a + `<frame>` or an `<iframe>`, switch it back to the top-level + browsing context. + """ + session.switch_frame(None) + + +def _windows(session, exclude=None): + """Set of window handles, filtered by an `exclude` list if + provided. + """ + if exclude is None: + exclude = [] + wins = [w for w in session.handles if w not in exclude] + return set(wins) + + +def clear_all_cookies(session): + """Removes all cookies associated with the current active document""" + session.transport.send("DELETE", "session/%s/cookie" % session.session_id) + + +def document_dimensions(session): + return tuple(session.execute_script(""" + let rect = document.documentElement.getBoundingClientRect(); + return [rect.width, rect.height]; + """)) + + +def center_point(element): + """Calculates the in-view center point of a web element.""" + inner_width, inner_height = element.session.execute_script( + "return [window.innerWidth, window.innerHeight]") + rect = element.rect + + # calculate the intersection of the rect that is inside the viewport + visible = { + "left": max(0, min(rect["x"], rect["x"] + rect["width"])), + "right": min(inner_width, max(rect["x"], rect["x"] + rect["width"])), + "top": max(0, min(rect["y"], rect["y"] + rect["height"])), + "bottom": min(inner_height, max(rect["y"], rect["y"] + rect["height"])), + } + + # arrive at the centre point of the visible rectangle + x = (visible["left"] + visible["right"]) / 2.0 + y = (visible["top"] + visible["bottom"]) / 2.0 + + # convert to CSS pixels, as centre point can be float + return (math.floor(x), math.floor(y)) + + +def document_hidden(session): + """Polls for the document to become hidden.""" + def hidden(session): + return session.execute_script("return document.hidden") + return Poll(session, timeout=3, raises=None).until(hidden) + + +def element_rect(session, element): + return session.execute_script(""" + let element = arguments[0]; + let rect = element.getBoundingClientRect(); + + return { + x: rect.left + window.pageXOffset, + y: rect.top + window.pageYOffset, + width: rect.width, + height: rect.height, + }; + """, args=(element,)) + + +def is_element_in_viewport(session, element): + """Check if element is outside of the viewport""" + return session.execute_script(""" + let el = arguments[0]; + + let rect = el.getBoundingClientRect(); + let viewport = { + height: window.innerHeight || document.documentElement.clientHeight, + width: window.innerWidth || document.documentElement.clientWidth, + }; + + return !(rect.right < 0 || rect.bottom < 0 || + rect.left > viewport.width || rect.top > viewport.height) + """, args=(element,)) + + +def is_fullscreen(session): + # At the time of writing, WebKit does not conform to the + # Fullscreen API specification. + # + # Remove the prefixed fallback when + # https://bugs.webkit.org/show_bug.cgi?id=158125 is fixed. + return session.execute_script(""" + return !!(window.fullScreen || document.webkitIsFullScreen) + """) + + +def document_dimensions(session): + return tuple(session.execute_script(""" + let {devicePixelRatio} = window; + let {width, height} = document.documentElement.getBoundingClientRect(); + return [width * devicePixelRatio, height * devicePixelRatio]; + """)) + + +def screen_size(session): + """Returns the available width/height size of the screen.""" + return tuple(session.execute_script(""" + return [ + screen.availWidth, + screen.availHeight, + ]; + """)) + + +def available_screen_size(session): + """ + Returns the effective available screen width/height size, + excluding any fixed window manager elements. + """ + return tuple(session.execute_script(""" + return [ + screen.availWidth - screen.availLeft, + screen.availHeight - screen.availTop, + ]; + """))
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_request.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_request.py new file mode 100644 index 0000000..5e46d97 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/http_request.py
@@ -0,0 +1,41 @@ +import contextlib +import httplib +import json + +from six import text_type + + +class HTTPRequest(object): + def __init__(self, host, port): + self.host = host + self.port = port + + def head(self, path): + return self._request("HEAD", path) + + def get(self, path): + return self._request("GET", path) + + def post(self, path, body): + return self._request("POST", path, body) + + @contextlib.contextmanager + def _request(self, method, path, body=None): + payload = None + + if body is not None: + try: + payload = json.dumps(body) + except ValueError: + raise ValueError("Failed to encode request body as JSON: {}".format( + json.dumps(body, indent=2))) + + if isinstance(payload, text_type): + payload = body.encode("utf-8") + + conn = httplib.HTTPConnection(self.host, self.port) + try: + conn.request(method, path, payload) + yield conn.getresponse() + finally: + conn.close()
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/image.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/image.py new file mode 100644 index 0000000..81dd933 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/image.py
@@ -0,0 +1,12 @@ +import base64 +import math +import struct + +from tests.support.asserts import assert_png + + +def png_dimensions(screenshot): + assert_png(screenshot) + image = base64.decodestring(screenshot) + width, height = struct.unpack(">LL", image[16:24]) + return int(width), int(height)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/inline.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/inline.py new file mode 100644 index 0000000..8b4f165 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/inline.py
@@ -0,0 +1,51 @@ +import urllib + + +def inline(doc, doctype="html", mime="text/html;charset=utf-8", protocol="http"): + from .fixtures import server_config, url + build_url = url(server_config()) + + if doctype == "html": + mime = "text/html;charset=utf-8" + elif doctype == "xhtml": + mime = "application/xhtml+xml" + doc = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title>XHTML might be the future</title> + </head> + + <body> + {} + </body> +</html>""".format(doc) + elif doctype == "xml": + mime = "text/xml" + doc = """<?xml version="1.0" encoding="UTF-8"?>{}""".format(doc) + + query = {"doc": doc} + if mime != "text/html;charset=utf8": + query["content-type"] = mime + + return build_url("/webdriver/tests/support/inline.py", + query=urllib.urlencode(query), + protocol=protocol) + + +def iframe(doc): + return "<iframe src='%s'></iframe>" % inline(doc) + + +def main(request, response): + doc = request.GET.first("doc", None) + content_type = request.GET.first("content-type", "text/html;charset=utf8") + if doc is None: + rv = 404, [("Content-Type", "text/plain")], "Missing doc parameter in query" + else: + response.headers.update([ + ("Content-Type", content_type), + ("X-XSS-Protection", "0") + ]) + rv = doc + return rv
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/merge_dictionaries.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/merge_dictionaries.py new file mode 100644 index 0000000..cf06c9b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/merge_dictionaries.py
@@ -0,0 +1,49 @@ +def iteritems(d): + """Create a key-value iterator for the given dict in both Python 2.x and + Python 3.x environments""" + if hasattr(d, "iteritems"): + return d.iteritems() + return d.items() + +def merge_dictionaries(first, second): + """Given two dictionaries, create a third that defines all specified + key/value pairs. This merge_dictionaries is performed "deeply" on any nested + dictionaries. If a value is defined for the same key by both dictionaries, + an exception will be raised.""" + result = dict(first) + + for key, value in iteritems(second): + if key in result and result[key] != value: + if isinstance(result[key], dict) and isinstance(value, dict): + result[key] = merge_dictionaries(result[key], value) + elif result[key] != value: + raise TypeError("merge_dictionaries: refusing to overwrite " + + "attribute: `%s`" % key) + else: + result[key] = value + + return result + +if __name__ == "__main__": + assert merge_dictionaries({}, {}) == {} + assert merge_dictionaries({}, {"a": 23}) == {"a": 23} + assert merge_dictionaries({"a": 23}, {"b": 45}) == {"a": 23, "b": 45} + + e = None + try: + merge_dictionaries({"a": 23}, {"a": 45}) + except Exception as _e: + e = _e + assert isinstance(e, TypeError) + + assert merge_dictionaries({"a": 23}, {"a": 23}) == {"a": 23} + + assert merge_dictionaries({"a": {"b": 23}}, {"a": {"c": 45}}) == {"a": {"b": 23, "c": 45}} + assert merge_dictionaries({"a": {"b": 23}}, {"a": {"b": 23}}) == {"a": {"b": 23}} + + e = None + try: + merge_dictionaries({"a": {"b": 23}}, {"a": {"b": 45}}) + except Exception as _e: + e = _e + assert isinstance(e, TypeError)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/sync.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/sync.py new file mode 100644 index 0000000..561fcd8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/sync.py
@@ -0,0 +1,146 @@ +import collections +import sys +import time + +from webdriver import error + + +DEFAULT_TIMEOUT = 5 +DEFAULT_INTERVAL = 0.1 + + +class Poll(object): + """ + An explicit conditional utility primitive for polling until a + condition evaluates to something truthy. + + A `Poll` instance defines the maximum amount of time to wait + for a condition, as well as the frequency with which to check + the condition. Furthermore, the user may configure the wait + to ignore specific types of exceptions whilst waiting, such as + `error.NoSuchElementException` when searching for an element + on the page. + """ + + def __init__(self, + session, + timeout=DEFAULT_TIMEOUT, + interval=DEFAULT_INTERVAL, + raises=error.TimeoutException, + message=None, + ignored_exceptions=None, + clock=time): + """ + Configure the poller to have a custom timeout, interval, + and list of ignored exceptions. Optionally a different time + implementation than the one provided by the standard library + (`time`) can also be provided. + + Sample usage:: + + # Wait 30 seconds for window to open, + # checking for its presence once every 5 seconds. + from support.sync import Poll + wait = Poll(session, timeout=30, interval=5, + ignored_exceptions=error.NoSuchWindowException) + window = wait.until(lambda s: s.switch_to_window(42)) + + :param session: The input value to be provided to conditions, + usually a `webdriver.Session` instance. + + :param timeout: How long to wait for the evaluated condition + to become true. + + :param interval: How often the condition should be evaluated. + In reality the interval may be greater as the cost of + evaluating the condition function. If that is not the case the + interval for the next condition function call is shortend to keep + the original interval sequence as best as possible. + + :param raises: Optional exception to raise when poll elapses. + If not used, an `error.TimeoutException` is raised. + If it is `None`, no exception is raised on the poll elapsing. + + :param message: An optional message to include in `raises`'s + message if the `until` condition times out. + + :param ignored_exceptions: Ignore specific types of exceptions + whilst waiting for the condition. Any exceptions not + whitelisted will be allowed to propagate, terminating the + wait. + + :param clock: Allows overriding the use of the runtime's + default time library. See `sync.SystemClock` for + implementation details. + """ + self.session = session + self.timeout = timeout + self.interval = interval + self.exc_cls = raises + self.exc_msg = message + self.clock = clock + + exceptions = [] + if ignored_exceptions is not None: + if isinstance(ignored_exceptions, collections.Iterable): + exceptions.extend(iter(ignored_exceptions)) + else: + exceptions.append(ignored_exceptions) + self.exceptions = tuple(set(exceptions)) + + def until(self, condition): + """ + This will repeatedly evaluate `condition` in anticipation + for a truthy return value, or the timeout to expire. + + A condition that returns `None` or does not evaluate to + true will fully elapse its timeout before raising, unless + the `raises` keyword argument is `None`, in which case the + condition's return value is propagated unconditionally. + + If an exception is raised in `condition` and it's not ignored, + this function will raise immediately. If the exception is + ignored it will be swallowed and polling will resume until + either the condition meets the return requirements or the + timeout duration is reached. + + :param condition: A callable function whose return value will + be returned by this function. + """ + rv = None + last_exc = None + start = self.clock.time() + end = start + self.timeout + + while not self.clock.time() >= end: + try: + next = self.clock.time() + self.interval + rv = condition(self.session) + except (KeyboardInterrupt, SystemExit): + raise + except self.exceptions: + last_exc = sys.exc_info() + + # re-adjust the interval depending on how long + # the callback took to evaluate the condition + interval_new = max(next - self.clock.time(), 0) + + if not rv: + self.clock.sleep(interval_new) + continue + + if rv is not None: + return rv + + self.clock.sleep(interval_new) + + if self.exc_cls is not None: + elapsed = round((self.clock.time() - start), 1) + message = "" + if self.exc_msg is not None: + message = " with message: {}".format(self.exc_msg) + raise self.exc_cls( + "Timed out after {} seconds{}".format(elapsed, message), + stacktrace=last_exc) + else: + return rv
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events-expected.txt new file mode 100644 index 0000000..5ac52f49 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events-expected.txt
@@ -0,0 +1,2 @@ +Test the Timeline instrumentation contains Profile event. +
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events.js new file mode 100644 index 0000000..ab0a2ab --- /dev/null +++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-js/timeline-has-cpu-profile-events.js
@@ -0,0 +1,15 @@ +// Copyright 2019 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. + +(async function() { + TestRunner.addResult(`Test the Timeline instrumentation contains Profile event.`); + await TestRunner.loadModule('performance_test_runner'); + await TestRunner.showPanel('timeline'); + + await PerformanceTestRunner.evaluateWithTimeline('42'); + const profileEvent = PerformanceTestRunner.findTimelineEvent(TimelineModel.TimelineModel.RecordType.Profile); + TestRunner.check(profileEvent, 'Profile trace event not found.'); + + TestRunner.completeTest(); +})();
diff --git a/third_party/blink/web_tests/paint/paintlayer/scrolling_issues.html b/third_party/blink/web_tests/paint/paintlayer/scrolling_issues.html new file mode 100644 index 0000000..391cc29 --- /dev/null +++ b/third_party/blink/web_tests/paint/paintlayer/scrolling_issues.html
@@ -0,0 +1,47 @@ +<!doctype html> + +<p>The scrollbar should be roughly in the middle of the scroll range.</p> +<p>This is intended to duplicate https://crbug.com/927560</p> + +<div id="scroller"> + <div style="width: 200px; height: 5000px;"></div> +</div> + +<style> +#scroller { + overflow-y: scroll; + width: 500px; + height: 500px; + border: 1px solid black; +} +</style> + +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src='../../resources/gesture-util.js'></script> + +<script> +var scroller = document.getElementById('scroller'); +var rect = scroller.getBoundingClientRect(); +var start_x = (rect.left + rect.right) / 2; +var start_y = (rect.top + rect.bottom) / 2; + +async function scrollDown(pixels_to_scroll, gesture_source_type) { + const target_scroll_offset = scroller.scrollTop + pixels_to_scroll; + await waitForCompositorCommit(); + await smoothScroll(pixels_to_scroll, start_x, start_y, gesture_source_type, + 'down', SPEED_INSTANT); + await waitFor(() => { + return approx_equals(scroller.scrollTop, target_scroll_offset, 20); + }, "Didn't scroll by expected amount: " + pixels_to_scroll + " scroller.scrollTop is " + scroller.scrollTop + ", target is " + target_scroll_offset); + await waitForCompositorCommit(); +} + +promise_test(async () => { + await scrollDown(800, GestureSourceType.TOUCHPAD_INPUT); + await scrollDown(800, GestureSourceType.TOUCH_INPUT); + await scrollDown(800, GestureSourceType.MOUSE_INPUT); + assert_approx_equals(scroller.scrollTop,2400,60,"Scroll didn't work: ") +}, "Scrolling by wheel then touch should work."); + +</script>
diff --git a/third_party/blink/web_tests/virtual/freeze-frames/external/wpt/lifecycle/README.txt b/third_party/blink/web_tests/virtual/freeze-frames/external/wpt/lifecycle/README.txt new file mode 100644 index 0000000..2b0e32f --- /dev/null +++ b/third_party/blink/web_tests/virtual/freeze-frames/external/wpt/lifecycle/README.txt
@@ -0,0 +1,2 @@ +# This suite runs the tests with +# --enable-features=FreezeFramesOnVisibility
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index ab9660a..57beddb 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -292,6 +292,7 @@ 'Windows Clang deterministic': 'clang_release_bot_minimal_symbols_x86', 'win-annotator-rel': 'release_bot', 'win-autofill-captured-sites-rel': 'release_bot', + 'win32-arm64-rel': 'win32_arm64_release_bot', }, 'chromium.goma': { @@ -1829,6 +1830,10 @@ 'no_clang', 'release_bot', ], + 'win32_arm64_release_bot': [ + 'arm64', 'disable_nacl', 'release_bot', + ], + 'clang_tot_win_release_cross': [ 'clang_tot', 'win_cross', 'minimal_symbols', 'shared', 'release', 'dcheck_always_on', ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index ef64b53..1880accb 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -7886,7 +7886,7 @@ </enum> <enum name="ConnectionFailureReason"> - <int value="0" label="Unknown"/> + <int value="0" label="Unknown (deprecated)"/> <int value="1" label="Bad Passphrase"/> <int value="2" label="Bad WEP Key"/> <int value="3" label="Failed to Connect"/> @@ -7897,6 +7897,8 @@ <int value="8" label="EAP Remote TLS"/> <int value="9" label="Out-of-range"/> <int value="10" label="Pin Missing"/> + <int value="11" label="Unknown"/> + <int value="12" label="No failure"/> </enum> <enum name="ConnectionInfo">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 6d68d2c1..9a35d811 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -12052,16 +12052,6 @@ </summary> </histogram> -<histogram name="Bluetooth.Availability" enum="BluetoothAvailability" - expires_after="2019-12-31"> - <owner>kenrb@chromium.org</owner> - <owner>kpaulhamus@chromium.org</owner> - <summary> - Determines the availability and capabilities of the Bluetooth driver. This - metric is logged on startup. - </summary> -</histogram> - <histogram name="Bluetooth.ConnectedDeviceCount" units="devices"> <owner>adlr@chromium.org</owner> <summary> @@ -76012,9 +76002,6 @@ <histogram name="OSX.BluetoothAvailability" enum="BluetoothAvailability" expires_after="2018-08-30"> - <obsolete> - Obsolete as of Chrome 73. This has been replaced by Bluetooth.Availability. - </obsolete> <owner>erikchen@chromium.org</owner> <summary> The availability and capabilities of the Bluetooth driver on OSX devices.
diff --git a/tools/perf/contrib/network_service/loading.py b/tools/perf/contrib/network_service/loading.py index 2b17f08..538c568 100644 --- a/tools/perf/contrib/network_service/loading.py +++ b/tools/perf/contrib/network_service/loading.py
@@ -15,8 +15,6 @@ from telemetry.value import none_values from telemetry.value.list_of_scalar_values import StandardDeviation -from tracing.value import convert_chart_json - def _ListSubtraction(diff_list, control_list): """Subtract |control_list|'s elements from the corresponding elements in |diff_list|, and store the results in |diff_list|. @@ -159,12 +157,10 @@ def Run(self, finder_options): """We shouldn't be overriding this according to telemetry.benchmark.Benchmark""" - assert 'histograms' in finder_options.output_formats, ( - 'loading.desktop.network_service requires --output-format=histograms.') - - # feed the story_runner with 'chartjson' output formats. - finder_options.output_formats = ['chartjson'] - + assert 'chartjson' in finder_options.output_formats, ( + 'loading.desktop.network_service requires --output-format=chartjson. ' + 'Please contact owner to rewrite the benchmark if chartjson is going ' + 'away.') assert finder_options.output_dir output_dir = finder_options.output_dir temp_file_path = os.path.join(output_dir, 'results-chart.json') @@ -202,17 +198,6 @@ with open(temp_file_path, 'w') as f: json.dump(enabled_chart_json, f, indent=2, separators=(',', ': ')) f.write('\n') - logging.info('Converting chartjsons to histograms') - histogram_result = convert_chart_json.ConvertChartJson(temp_file_path) - if histogram_result.returncode != 0: - logging.error('Error converting chart json to Histograms:\n' + - histogram_result.stdout) - return 1 - - temp_file_path = os.path.join(output_dir, 'histograms.json') - with open(temp_file_path, 'w') as f: - f.write(histogram_result.stdout) - return 0 def SetExtraBrowserOptions(self, options):
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc index a3642dc..78e2b98 100644 --- a/ui/accessibility/ax_role_properties.cc +++ b/ui/accessibility/ax_role_properties.cc
@@ -343,6 +343,16 @@ } } +bool IsTextOrLineBreak(ax::mojom::Role role) { + switch (role) { + case ax::mojom::Role::kLineBreak: + case ax::mojom::Role::kStaticText: + return true; + default: + return false; + } +} + bool SupportsExpandCollapse(const ax::mojom::Role role) { switch (role) { case ax::mojom::Role::kComboBoxGrouping:
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h index d79ec98..a9ea967e 100644 --- a/ui/accessibility/ax_role_properties.h +++ b/ui/accessibility/ax_role_properties.h
@@ -86,6 +86,9 @@ // table is not used for layout purposes. AX_EXPORT bool IsTableRow(ax::mojom::Role role); +// Returns true if it's a text or line break node. +AX_EXPORT bool IsTextOrLineBreak(ax::mojom::Role role); + // Returns true if the provided role supports expand/collapse. AX_EXPORT bool SupportsExpandCollapse(const ax::mojom::Role role);
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc index 98bf5a3..487fbbc 100644 --- a/ui/aura/test/mus/test_window_tree.cc +++ b/ui/aura/test/mus/test_window_tree.cc
@@ -332,7 +332,10 @@ OnChangeReceived(change_id, WindowTreeChangeType::FOCUS); } -void TestWindowTree::SetCanFocus(ws::Id window_id, bool can_focus) {} +void TestWindowTree::SetCanFocus(ws::Id window_id, bool can_focus) { + ++can_focus_count_; + last_can_focus_ = can_focus; +} void TestWindowTree::SetEventTargetingPolicy( ws::Id window_id,
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h index 142c259..c42155c 100644 --- a/ui/aura/test/mus/test_window_tree.h +++ b/ui/aura/test/mus/test_window_tree.h
@@ -146,6 +146,13 @@ return value; } + bool last_can_focus() const { return last_can_focus_; } + size_t get_and_clear_can_focus_count() { + const int value = can_focus_count_; + can_focus_count_ = 0u; + return value; + } + int last_move_hit_test() const { return last_move_hit_test_; } size_t get_and_clear_window_resize_shadow_count() { @@ -336,6 +343,8 @@ ws::Id last_transfer_new_ = 0u; bool last_transfer_should_cancel_ = false; bool last_accepts_drops_ = false; + size_t can_focus_count_ = 0u; + bool last_can_focus_ = false; size_t accepts_drops_count_ = 0u;
diff --git a/ui/base/dragdrop/os_exchange_data_provider_win.cc b/ui/base/dragdrop/os_exchange_data_provider_win.cc index db7425bb..a9bd4912f 100644 --- a/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/ui/base/dragdrop/os_exchange_data_provider_win.cc
@@ -345,8 +345,7 @@ data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( ClipboardFormatType::GetUrlType().ToFormatEtc(), storage)); - // TODO(beng): add CF_HTML. - // http://code.google.com/p/chromium/issues/detail?id=6767GetIDListStorageForFileName + // TODO(https://crbug.com/6767): add CF_HTML. // Also add text representations (these should be last since they're the // least preferable). @@ -715,11 +714,10 @@ } void DataObjectImpl::StopDownloads() { - for (StoredData::iterator iter = contents_.begin(); - iter != contents_.end(); ++iter) { - if ((*iter)->downloader.get()) { - (*iter)->downloader->Stop(); - (*iter)->downloader = 0; + for (const std::unique_ptr<StoredDataInfo>& content : contents_) { + if (content->downloader.get()) { + content->downloader->Stop(); + content->downloader = 0; } } } @@ -741,44 +739,32 @@ } void DataObjectImpl::OnDownloadCompleted(const base::FilePath& file_path) { - DataObjectImpl::StoredData::iterator iter = contents_.begin(); - for (; iter != contents_.end(); ++iter) { - if ((*iter)->format_etc.cfFormat == CF_HDROP) { - // Release the old storage. - if ((*iter)->owns_medium) { - ReleaseStgMedium((*iter)->medium); - delete (*iter)->medium; - } - - // Update the storage. + for (std::unique_ptr<StoredDataInfo>& content : contents_) { + if (content->format_etc.cfFormat == CF_HDROP) { + // Replace stored data. STGMEDIUM* storage = GetStorageForFileNames({FileInfo(file_path, base::FilePath())}); - if (storage) { - (*iter)->owns_medium = true; - (*iter)->medium = storage; - } + content.reset(new StoredDataInfo( + ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage)); break; } } - DCHECK(iter != contents_.end()); } -void DataObjectImpl::OnDownloadAborted() { -} +void DataObjectImpl::OnDownloadAborted() {} HRESULT DataObjectImpl::GetData(FORMATETC* format_etc, STGMEDIUM* medium) { if (is_aborting_) return DV_E_FORMATETC; - StoredData::iterator iter = contents_.begin(); - while (iter != contents_.end()) { - if ((*iter)->format_etc.cfFormat == format_etc->cfFormat && - (*iter)->format_etc.lindex == format_etc->lindex && - ((*iter)->format_etc.tymed & format_etc->tymed)) { + for (const std::unique_ptr<StoredDataInfo>& content : contents_) { + if (content->format_etc.cfFormat == format_etc->cfFormat && + content->format_etc.lindex == format_etc->lindex && + (content->format_etc.tymed & format_etc->tymed)) { // If medium is NULL, delay-rendering will be used. - if ((*iter)->medium) { - DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium); + if (content->medium) { + DuplicateMedium(content->format_etc.cfFormat, content->medium, medium); } else { // Fail all GetData() attempts for DownloadURL data if the drag and drop // operation is still in progress. @@ -803,9 +789,9 @@ observer_->OnWaitForData(); // Now we can start the download. - if ((*iter)->downloader.get()) { - (*iter)->downloader->Start(this); - if (!(*iter)->downloader->Wait()) { + if (content->downloader.get()) { + content->downloader->Start(this); + if (!content->downloader->Wait()) { is_aborting_ = true; return DV_E_FORMATETC; } @@ -817,7 +803,6 @@ } return S_OK; } - ++iter; } return DV_E_FORMATETC; @@ -829,11 +814,9 @@ } HRESULT DataObjectImpl::QueryGetData(FORMATETC* format_etc) { - StoredData::const_iterator iter = contents_.begin(); - while (iter != contents_.end()) { - if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) + for (const std::unique_ptr<StoredDataInfo>& content : contents_) { + if (content->format_etc.cfFormat == format_etc->cfFormat) return S_OK; - ++iter; } return DV_E_FORMATETC; } @@ -1014,7 +997,7 @@ // For example, //| DROPFILES | FILENAME 1 | NULL | ... | FILENAME n | NULL | NULL | // For more details, please refer to - // https://docs.microsoft.com/ko-kr/windows/desktop/shell/clipboard#cf_hdrop + // https://docs.microsoft.com/en-us/windows/desktop/shell/clipboard#cf_hdrop if (filenames.empty()) return nullptr;
diff --git a/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc b/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc index a2abea8..47ca7597 100644 --- a/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc +++ b/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc
@@ -6,8 +6,8 @@ #include <utility> -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "base/logging.h" namespace ui { @@ -16,7 +16,7 @@ fuchsia::ui::input::ImeService* ime_service) : ime_service_(ime_service), ime_visibility_( - base::fuchsia::ComponentContext::GetDefault() + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::ui::input::ImeVisibilityService>()) { DCHECK(ime_service_);
diff --git a/ui/base/ime/input_method_fuchsia.cc b/ui/base/ime/input_method_fuchsia.cc index 0ce7f33..e8c3bd6 100644 --- a/ui/base/ime/input_method_fuchsia.cc +++ b/ui/base/ime/input_method_fuchsia.cc
@@ -9,7 +9,7 @@ #include <utility> #include "base/bind_helpers.h" -#include "base/fuchsia/component_context.h" +#include "base/fuchsia/service_directory_client.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/base_event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" @@ -21,7 +21,7 @@ : InputMethodBase(delegate), event_converter_(this), ime_client_binding_(this), - ime_service_(base::fuchsia::ComponentContext::GetDefault() + ime_service_(base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::ui::input::ImeService>()), virtual_keyboard_controller_(ime_service_.get()) {}
diff --git a/ui/chromeos/ime/candidate_window_view.cc b/ui/chromeos/ime/candidate_window_view.cc index f34fba82..c3d8c43 100644 --- a/ui/chromeos/ime/candidate_window_view.cc +++ b/ui/chromeos/ime/candidate_window_view.cc
@@ -154,7 +154,7 @@ should_show_upper_side_(false), was_candidate_window_open_(false), window_shell_id_(window_shell_id) { - set_can_activate(false); + SetCanActivate(false); DCHECK(parent || features::IsUsingWindowService()); set_parent_window(parent); set_margins(gfx::Insets());
diff --git a/ui/chromeos/ime/infolist_window.cc b/ui/chromeos/ime/infolist_window.cc index 9a6de77..bc63378 100644 --- a/ui/chromeos/ime/infolist_window.cc +++ b/ui/chromeos/ime/infolist_window.cc
@@ -173,7 +173,7 @@ title_font_list_(gfx::Font(kJapaneseFontName, kFontSizeDelta + 15)), description_font_list_( gfx::Font(kJapaneseFontName, kFontSizeDelta + 11)) { - set_can_activate(false); + SetCanActivate(false); set_accept_events(false); set_margins(gfx::Insets());
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 0e3bc3285..c596a628 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -1212,6 +1212,10 @@ void Layer::SetVisibilityFromAnimation(bool visible, PropertyChangeReason reason) { + // Sync changes with the mirror layers. + for (const auto& mirror : mirrors_) + mirror->dest()->SetVisible(visible); + if (visible_ == visible) return;
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h index 3e1771ff..e90777f 100644 --- a/ui/compositor/layer.h +++ b/ui/compositor/layer.h
@@ -249,8 +249,10 @@ void SetMaskLayer(Layer* layer_mask); Layer* layer_mask_layer() { return layer_mask_; } - // Sets the visibility of the Layer. A Layer may be visible but not - // drawn. This happens if any ancestor of a Layer is not visible. + // Sets the visibility of the Layer. A Layer may be visible but not drawn. + // This happens if any ancestor of a Layer is not visible. + // Any changes made to this in the source layer will override the visibility + // of its mirror layer. void SetVisible(bool visible); bool visible() const { return visible_; }
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index fdde61a..3036ff3 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -1117,6 +1117,110 @@ EXPECT_TRUE(l3->cc_layer_for_testing()->hide_layer_and_subtree()); } +// Various visible/drawn assertions. +TEST_F(LayerWithNullDelegateTest, MirroringVisibility) { + std::unique_ptr<Layer> l1(new Layer(LAYER_TEXTURED)); + std::unique_ptr<Layer> l2(new Layer(LAYER_TEXTURED)); + std::unique_ptr<Layer> l2_mirror = l2->Mirror(); + l1->Add(l2.get()); + l1->Add(l2_mirror.get()); + + NullLayerDelegate delegate; + l1->set_delegate(&delegate); + l2->set_delegate(&delegate); + l2_mirror->set_delegate(&delegate); + + // Layers should initially be drawn. + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_TRUE(l2->IsDrawn()); + EXPECT_TRUE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + compositor()->SetRootLayer(l1.get()); + + Draw(); + + // Hiding the root layer should hide that specific layer and its subtree. + l1->SetVisible(false); + + // Since the entire subtree is hidden, no layer should be drawn. + EXPECT_FALSE(l1->IsDrawn()); + EXPECT_FALSE(l2->IsDrawn()); + EXPECT_FALSE(l2_mirror->IsDrawn()); + + // The visibitily property for the subtree is rooted at |l1|. + EXPECT_TRUE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Hiding |l2| should also set the visibility on its mirror layer. In this + // case the visibility of |l2| will be mirrored by |l2_mirror|. + l2->SetVisible(false); + + // None of the layers are drawn since the visibility is false at every node. + EXPECT_FALSE(l1->IsDrawn()); + EXPECT_FALSE(l2->IsDrawn()); + EXPECT_FALSE(l2_mirror->IsDrawn()); + + // Visibility property is set on every node and hence their subtree is also + // hidden. + EXPECT_TRUE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Setting visibility on the root layer should make that layer visible and its + // subtree ready for visibility. + l1->SetVisible(true); + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_FALSE(l2->IsDrawn()); + EXPECT_FALSE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Setting visibility on the mirrored layer should not effect its source + // layer. + l2_mirror->SetVisible(true); + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_FALSE(l2->IsDrawn()); + EXPECT_TRUE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Setting visibility on the source layer should keep the mirror layer in + // sync and not cause any invalid state. + l2->SetVisible(true); + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_TRUE(l2->IsDrawn()); + EXPECT_TRUE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Setting visibility on the mirrored layer should not effect its source + // layer. + l2_mirror->SetVisible(false); + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_TRUE(l2->IsDrawn()); + EXPECT_FALSE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_TRUE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); + + // Setting source layer's visibility to true should update the mirror layer + // even if the source layer did not change in the process. + l2->SetVisible(true); + EXPECT_TRUE(l1->IsDrawn()); + EXPECT_TRUE(l2->IsDrawn()); + EXPECT_TRUE(l2_mirror->IsDrawn()); + EXPECT_FALSE(l1->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2->cc_layer_for_testing()->hide_layer_and_subtree()); + EXPECT_FALSE(l2_mirror->cc_layer_for_testing()->hide_layer_and_subtree()); +} + // Checks that stacking-related methods behave as advertised. TEST_F(LayerWithNullDelegateTest, Stacking) { std::unique_ptr<Layer> root(new Layer(LAYER_NOT_DRAWN));
diff --git a/ui/file_manager/file_manager/background/js/entry_location_impl.js b/ui/file_manager/file_manager/background/js/entry_location_impl.js index 65843c1..dda0643f 100644 --- a/ui/file_manager/file_manager/background/js/entry_location_impl.js +++ b/ui/file_manager/file_manager/background/js/entry_location_impl.js
@@ -47,7 +47,8 @@ /** @type{boolean} */ this.hasFixedLabel = this.isRootEntry && (rootType !== VolumeManagerCommon.RootType.TEAM_DRIVE && - rootType !== VolumeManagerCommon.RootType.COMPUTER); + rootType !== VolumeManagerCommon.RootType.COMPUTER && + rootType !== VolumeManagerCommon.RootType.REMOVABLE); Object.freeze(this); }
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js index e02d30a..f01b6de 100644 --- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js +++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -146,9 +146,11 @@ * @param {!VolumeManagerCommon.VolumeType} type Volume type. * @param {string} volumeId Volume id. * @param {string=} label Label. + * @param {string=} devicePath Device path. * @return {!VolumeInfo} Created mock VolumeInfo. */ -MockVolumeManager.createMockVolumeInfo = function(type, volumeId, label) { +MockVolumeManager.createMockVolumeInfo = function( + type, volumeId, label, devicePath) { var fileSystem = new MockFileSystem(volumeId, 'filesystem:' + volumeId); // If there's no label set it to volumeId to make it shorter to write tests. @@ -156,7 +158,7 @@ type, volumeId, fileSystem, '', // error '', // deviceType - '', // devicePath + devicePath || '', // devicePath false, // isReadOnly false, // isReadOnlyRemovableDevice {isCurrentProfile: true, displayName: ''}, // profile
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js index 7ed3d31..e1cd989e 100644 --- a/ui/file_manager/file_manager/common/js/files_app_entry_types.js +++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -258,9 +258,9 @@ * @param {string} label: Label to be used when displaying to user, it should * already translated. * @param {VolumeManagerCommon.RootType} rootType root type. - * + * @param {string} devicePath Device path */ - constructor(label, rootType) { + constructor(label, rootType, devicePath = '') { /** * @private {string} label: Label to be used when displaying to user, it * should be already translated. */ @@ -270,6 +270,12 @@ this.rootType_ = rootType; /** + * @private {string} devicePath Path belonging to the external media + * device. Partitions on the same external drive have the same device path. + */ + this.devicePath_ = devicePath; + + /** * @private {!Array<!Entry|!FilesAppEntry>} children entries of * this EntryList instance. */ @@ -317,6 +323,11 @@ * @override */ toURL() { + // There may be multiple entry lists. Append the device path to return + // a unique identifiable URL for the entry list. + if (this.devicePath_) { + return 'entry-list://' + this.rootType + '/' + this.devicePath_; + } return 'entry-list://' + this.rootType; }
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css index 9a8da83..e54c611d 100644 --- a/ui/file_manager/file_manager/foreground/css/file_types.css +++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -455,8 +455,8 @@ url(../images/volumes/2x/phone_active.png) 2x); } -[volume-type-icon='removable'][volume-subtype='partition'], [volume-type-icon='removable'][volume-subtype='unknown'], +.tree-item .tree-item [volume-type-icon='removable'], [file-type-icon='removable'] { background-image: -webkit-image-set( url(../images/volumes/hard_drive.png) 1x, @@ -464,8 +464,15 @@ } tree .tree-item[selected] > .tree-row > -[volume-type-icon='removable'][volume-subtype='partition'], -.tree-row[selected] [volume-type-icon='removable'][volume-subtype='unknown'] { +.tree-row[selected] [volume-type-icon='removable'][volume-subtype='unknown'], +.tree-item .tree-item[selected] [volume-type-icon='removable'] { + background-image: -webkit-image-set( + url(../images/volumes/hard_drive_active.png) 1x, + url(../images/volumes/2x/hard_drive_active.png) 2x); +} + +tree .tree-item > .tree-item > .tree-row > +[volume-type-icon='removable'] { background-image: -webkit-image-set( url(../images/volumes/hard_drive_active.png) 1x, url(../images/volumes/2x/hard_drive_active.png) 2x);
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/delete.svg b/ui/file_manager/file_manager/foreground/images/files/ui/delete.svg index 592fe13..9507cdf 100644 --- a/ui/file_manager/file_manager/foreground/images/files/ui/delete.svg +++ b/ui/file_manager/file_manager/foreground/images/files/ui/delete.svg
@@ -1,4 +1,4 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24" fill="#616161"> +<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24" fill="#616161"> <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/> <path d="M0 0h24v24H0z" fill="none"/> </svg>
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/share.svg b/ui/file_manager/file_manager/foreground/images/files/ui/share.svg index 032ae7d..748a8b6 100644 --- a/ui/file_manager/file_manager/foreground/images/files/ui/share.svg +++ b/ui/file_manager/file_manager/foreground/images/files/ui/share.svg
@@ -1,4 +1,4 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24" fill="#616161"> +<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24" fill="#616161"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/> </svg>
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js index 3733c63..03599eb5 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -603,6 +603,63 @@ }); /** + * Unmounts external drive which contains partitions. + * @type {Command} + */ +CommandHandler.COMMANDS_['unmount-all-partitions'] = /** @type {Command} */ ({ + /** + * @param {!Event} event Command event. + * @param {!CommandHandlerDeps} fileManager The file manager instance. + */ + execute: function(event, fileManager) { + const errorCallback = () => { + fileManager.ui.alertDialog.showHtml( + '', str('UNMOUNT_FAILED'), null, null, null); + }; + + const successCallback = () => { + const rootLabel = entry.label || ''; + const msg = strf('A11Y_VOLUME_EJECT', rootLabel); + fileManager.ui.speakA11yMessage(msg); + }; + + const entry = event.target.entry; + if (!entry) { + errorCallback(); + return; + } + + // Eject partitions one by one. + const partitions = entry.getUIChildren(); + for (let i = 0; i < partitions.length; i++) { + const volumeInfo = partitions[i].volumeInfo; + fileManager.volumeManager.unmount( + volumeInfo, (i == partitions.length) ? successCallback : () => {}, + errorCallback); + } + }, + + /** + * @param {!Event} event Command event. + * @this {CommandHandler} + */ + canExecute: function(event, fileManager) { + const entry = event.target.entry; + if (!entry) { + event.canExecute = false; + event.command.setHidden(true); + return; + } + + event.canExecute = + (entry.rootType === VolumeManagerCommon.VolumeType.REMOVABLE && + entry.type_name === 'EntryList'); + event.command.label = str('UNMOUNT_DEVICE_BUTTON_LABEL'); + event.command.setHidden(!event.canExecute); + } +}); + +/** * Formats external drive. * @type {Command} */
diff --git a/ui/file_manager/file_manager/foreground/js/file_selection.js b/ui/file_manager/file_manager/foreground/js/file_selection.js index 0ba0e74..c543e04 100644 --- a/ui/file_manager/file_manager/foreground/js/file_selection.js +++ b/ui/file_manager/file_manager/foreground/js/file_selection.js
@@ -62,11 +62,11 @@ */ this.additionalPromise_ = null; - entries.forEach(function(entry) { + entries.forEach(entry => { if (this.iconType == null) { this.iconType = FileType.getIcon(entry); } else if (this.iconType != 'unknown') { - var iconType = FileType.getIcon(entry); + const iconType = FileType.getIcon(entry); if (this.iconType != iconType) { this.iconType = 'unknown'; } @@ -78,7 +78,7 @@ this.directoryCount += 1; } this.totalCount++; - }.bind(this)); + }); } FileSelection.prototype.computeAdditional = function(metadataModel) { @@ -88,21 +88,21 @@ .get( this.entries, constants.FILE_SELECTION_METADATA_PREFETCH_PROPERTY_NAMES) - .then(function(props) { - var present = props.filter(function(p) { + .then(props => { + const present = props.filter(p => { // If no availableOffline property, then assume it's available. return !('availableOffline' in p) || p.availableOffline; }); - const hosted = props.filter(function(p) { + const hosted = props.filter(p => { return p.hosted; }); this.anyFilesNotInCache = present.length !== props.length; this.anyFilesHosted = !!hosted.length; - this.mimeTypes = props.map(function(value) { + this.mimeTypes = props.map(value => { return value.contentMimeType || ''; }); return true; - }.bind(this)); + }); } return this.additionalPromise_; }; @@ -220,11 +220,11 @@ * Update the UI when the selection model changes. */ FileSelectionHandler.prototype.onFileSelectionChanged = function() { - var indexes = this.listContainer_.selectionModel.selectedIndexes; - var entries = indexes.map(function(index) { + const indexes = this.listContainer_.selectionModel.selectedIndexes; + const entries = indexes.map(index => { return /** @type {!Entry} */ ( this.directoryModel_.getFileList().item(index)); - }.bind(this)); + }); this.selection = new FileSelection(indexes, entries); if (this.selectionUpdateTimer_) { @@ -236,8 +236,8 @@ // asynchronous calls. We initiate these calls after a timeout. If the // selection is changing quickly we only do this once when it slows down. - var updateDelay = FileSelectionHandler.UPDATE_DELAY; - var now = Date.now(); + let updateDelay = FileSelectionHandler.UPDATE_DELAY; + const now = Date.now(); if (now > (this.lastFileSelectionTime_ || 0) + updateDelay && indexes.length < FileSelectionHandler.NUMBER_OF_ITEMS_HEAVY_TO_COMPUTE) { @@ -248,13 +248,13 @@ } this.lastFileSelectionTime_ = now; - var selection = this.selection; - this.selectionUpdateTimer_ = setTimeout(function() { + const selection = this.selection; + this.selectionUpdateTimer_ = setTimeout(() => { this.selectionUpdateTimer_ = null; if (this.selection === selection) { this.updateFileSelectionAsync_(selection); } - }.bind(this), updateDelay); + }, updateDelay); cr.dispatchSimpleEvent(this, FileSelectionHandler.EventType.CHANGE); };
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js index 3e15389..ef36a99 100644 --- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js +++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -199,6 +199,14 @@ this.myFilesModel_ = null; /** + * A collection of NavigationModel objects for Removable partition groups. + * Store the reference to each model here since DirectoryTree expects it to + * always have the same reference. + * @private {!Map<string, !NavigationModelFakeItem>} + */ + this.removableModels_ = new Map(); + + /** * True when MyFiles should be a volume and Downloads just a plain folder * inside it. When false MyFiles is an EntryList, which means UI only type, * which contains Downloads as a child volume. @@ -504,6 +512,32 @@ return indexes.map(idx => volumeList[idx]); }; + /** + * Removable volumes which share the same device path (i.e. partitions) are + * grouped. + * @return !Map<string, !Array<!NavigationModelVolumeItem>> + */ + const groupRemovables = function() { + const removableGroups = new Map(); + const removableVolumes = + getVolumes(VolumeManagerCommon.VolumeType.REMOVABLE); + + for (const removable of removableVolumes) { + // Partitions on the same physical device share device path and drive + // label. Create keys using these two identifiers. + let key = removable.volumeInfo.devicePath + '/' + + removable.volumeInfo.driveLabel; + if (!removableGroups.has(key)) { + // New key, so create a new array to hold partitions. + removableGroups.set(key, []); + } + // Add volume to array of volumes matching device path and drive label. + removableGroups.get(key).push(removable); + } + + return removableGroups; + }; + // Items as per required order. this.navigationItems_ = []; @@ -637,11 +671,49 @@ provider.section = NavigationSection.CLOUD; } - // Join MTP, ARCHIVE and REMOVABLE. These types belong to same section. + // Add REMOVABLE volumes and partitions. + const removableModels = new Map(); + for (const [devicePath, removableGroup] of groupRemovables().entries()) { + if (removableGroup.length == 1) { + // Add unpartitioned removable device as a regular volume. + this.navigationItems_.push(removableGroup[0]); + removableGroup[0].section = NavigationSection.REMOVABLE; + continue; + } + + // Multiple partitions found. + let removableModel; + if (this.removableModels_.has(devicePath)) { + // Removable model has been seen before. Use the same reference. + removableModel = this.removableModels_.get(devicePath); + } else { + // Create an EntryList for new removable group. + const rootLabel = removableGroup[0].volumeInfo.driveLabel ? + removableGroup[0].volumeInfo.driveLabel : + /*default*/ 'External Drive'; + const removableEntry = new EntryList( + rootLabel, VolumeManagerCommon.RootType.REMOVABLE, devicePath); + removableModel = new NavigationModelFakeItem( + removableEntry.label, NavigationModelItemType.ENTRY_LIST, + removableEntry); + removableModel.section = NavigationSection.REMOVABLE; + // Add partitions as entries. + for (const partition of removableGroup) { + // Only add partition if it doesn't exist as a child already. + if (removableEntry.findIndexByVolumeInfo(partition.volumeInfo) === -1) { + removableEntry.addEntry(new VolumeEntry(partition.volumeInfo)); + } + } + } + removableModels.set(devicePath, removableModel); + this.navigationItems_.push(removableModel); + } + this.removableModels_ = removableModels; + + // Join MTP, ARCHIVE. These types belong to same section. const zipIndexes = volumeIndexes[NavigationListModel.ZIP_VOLUME_TYPE] || []; const otherVolumes = [].concat( - getVolumes(VolumeManagerCommon.VolumeType.REMOVABLE), getVolumes(VolumeManagerCommon.VolumeType.ARCHIVE), getVolumes(VolumeManagerCommon.VolumeType.MTP), zipIndexes.map(idx => volumeList[idx]))
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js index 13afa9717..a75581c 100644 --- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
@@ -193,7 +193,8 @@ // Mount removable volume 'hoge'. volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge')); + VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge', '', + 'device/path/1')); assertEquals(4, model.length); assertEquals( @@ -207,9 +208,11 @@ 'removable:hoge', /** @type {!NavigationModelVolumeItem} */ (model.item(3)).volumeInfo.volumeId); - // Mount removable volume 'fuga'. + // Mount removable volume 'fuga'. Not a partition, so set a different device + // path to 'hoge'. volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga')); + VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga', '', + 'device/path/2')); assertEquals(5, model.length); assertEquals( @@ -268,14 +271,18 @@ // Create different volumes. volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge')); - volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.PROVIDED, 'provided:prov1')); + // Set the device paths of the removable volumes to different strings to + // test the behaviour of two physically separate external devices. + volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge', '', + 'device/path/1')); + volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga', '', + 'device/path/2')); volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.ARCHIVE, 'archive:a-rar')); volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga')); - volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.MTP, 'mtp:a-phone')); volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.PROVIDED, 'provided:prov2')); @@ -311,8 +318,8 @@ // 10. provided:prov2 // // 11. removable:hoge - // 12. archive:a-rar - mounted as archive - // 13. removable:fuga + // 12. removable:fuga + // 13. archive:a-rar - mounted as archive // 14. mtp:a-phone // 15. provided:"zip" - mounted as provided: $zipVolumeId @@ -338,8 +345,9 @@ assertEquals('provided:prov2', model.item(9).label); assertEquals('removable:hoge', model.item(10).label); - assertEquals('archive:a-rar', model.item(11).label); - assertEquals('removable:fuga', model.item(12).label); + assertEquals('removable:fuga', model.item(11).label); + + assertEquals('archive:a-rar', model.item(12).label); assertEquals('mtp:a-phone', model.item(13).label); assertEquals(zipVolumeId, model.item(14).label); @@ -372,9 +380,9 @@ // MTP/Archive/Removable are grouped together. // removable:hoge. assertEquals(NavigationSection.REMOVABLE, model.item(10).section); - // archive:a-rar. - assertEquals(NavigationSection.REMOVABLE, model.item(11).section); // removable:fuga. + assertEquals(NavigationSection.REMOVABLE, model.item(11).section); + // archive:a-rar. assertEquals(NavigationSection.REMOVABLE, model.item(12).section); // mtp:a-phone. assertEquals(NavigationSection.REMOVABLE, model.item(13).section);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js index 634f718..4066b88 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js +++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -662,7 +662,9 @@ ejectButton.setAttribute('tabindex', '0'); ejectButton.addEventListener('click', (event) => { event.stopPropagation(); - const unmountCommand = cr.doc.querySelector('command#unmount'); + const unmountCommand = (this instanceof EntryListItem) ? + cr.doc.querySelector('command#unmount-all-partitions') : + cr.doc.querySelector('command#unmount'); // Let's make sure 'canExecute' state of the command is properly set for // the root before executing it. unmountCommand.canExecuteChange(this); @@ -716,6 +718,7 @@ if (window.IN_TEST && location.volumeInfo) { item.setAttribute( 'volume-type-for-testing', location.volumeInfo.volumeType); + item.setAttribute('drive-label', location.volumeInfo.driveLabel); } } else { const rootType = location.rootType || null; @@ -803,6 +806,10 @@ item.dirEntry_ = modelItem.entry; item.parentTree_ = tree; + if (rootType === VolumeManagerCommon.RootType.REMOVABLE) { + item.setupEjectButton_(item.rowElement); + } + const icon = queryRequiredElement('.icon', item); if (window.IN_TEST && item.entry && item.entry.volumeInfo) { item.setAttribute( @@ -1806,8 +1813,11 @@ /** @type {!NavigationModelFakeItem} */ (modelItem), tree); break; case NavigationModelItemType.ENTRY_LIST: + const rootType = modelItem.section === NavigationSection.REMOVABLE ? + VolumeManagerCommon.RootType.REMOVABLE : + VolumeManagerCommon.RootType.MY_FILES; return new EntryListItem( - VolumeManagerCommon.RootType.MY_FILES, + rootType, /** @type {!NavigationModelFakeItem} */ (modelItem), tree); break; }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/location_line.js b/ui/file_manager/file_manager/foreground/js/ui/location_line.js index 5bae388..0390a2e 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/location_line.js +++ b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
@@ -75,7 +75,7 @@ if (util.isFakeEntry(entry)) { components.push(new LocationLine.PathComponent( - util.getRootTypeLabel(locationInfo), entry.toURL(), + util.getEntryLabel(locationInfo, entry), entry.toURL(), /** @type {!FakeEntry} */ (entry))); return components; }
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html index 9b88cdd..aa682e4 100644 --- a/ui/file_manager/file_manager/main.html +++ b/ui/file_manager/file_manager/main.html
@@ -138,6 +138,7 @@ <command id="unmount" label="$i18n{UNMOUNT_DEVICE_BUTTON_LABEL}" shortcut="E|Shift|Ctrl"> + <command id="unmount-all-partitions" label="$i18n{UNMOUNT_DEVICE_BUTTON_LABEL}"> <command id="format" label="$i18n{FORMAT_DEVICE_BUTTON_LABEL}"> <command id="configure" label="$i18n{CONFIGURE_VOLUME_BUTTON_LABEL}"> @@ -226,6 +227,7 @@ menu-item-selector="cr-menu-item"> <cr-menu-item command="#configure"></cr-menu-item> <cr-menu-item command="#unmount"></cr-menu-item> + <cr-menu-item command="#unmount-all-partitions"></cr-menu-item> <cr-menu-item command="#format"></cr-menu-item> <cr-menu-item command="#rename"></cr-menu-item> <cr-menu-item command="#remove-folder-shortcut"></cr-menu-item>
diff --git a/ui/file_manager/integration_tests/file_manager/file_display.js b/ui/file_manager/integration_tests/file_manager/file_display.js index d772dd1..5c316d6 100644 --- a/ui/file_manager/integration_tests/file_manager/file_display.js +++ b/ui/file_manager/integration_tests/file_manager/file_display.js
@@ -180,7 +180,7 @@ }; /** - * Tests files display for partitions on a removable USB volume. + * Tests files display on a removable USB volume with and without partitions. */ testcase.fileDisplayUsbPartition = async function() { // Open Files app on local downloads. @@ -207,13 +207,27 @@ chrome.test.assertEq( 'removable', singleUSB.attributes['volume-type-for-testing']); - // Check whether the drive label is shared by the partitions. - chrome.test.assertEq( - 'PARTITION_DRIVE_LABEL', partitionOne.attributes['drive-label']); - chrome.test.assertEq( - 'PARTITION_DRIVE_LABEL', partitionTwo.attributes['drive-label']); - chrome.test.assertEq( - 'SINGLE_DRIVE_LABEL', singleUSB.attributes['drive-label']); + // Wait for removable root to appear in the directory tree. + const removableRoot = await remoteCall.waitForElement( + appId, '#directory-tree [entry-label="PARTITION_DRIVE_LABEL"]'); + + // Check partitions are children of the root label. + const childEntriesQuery = + ['[entry-label="PARTITION_DRIVE_LABEL"] .tree-children .tree-item']; + const childEntries = await remoteCall.callRemoteTestUtil( + 'queryAllElements', appId, childEntriesQuery); + const childEntryLabels = + childEntries.map(child => child.attributes['entry-label']); + chrome.test.assertEq(['partition-1', 'partition-2'], childEntryLabels); + + // Check single USB does not have partitions as tree children. + const itemEntriesQuery = + ['[entry-label="singleUSB"] .tree-children .tree-item']; + const itemEntries = await remoteCall.callRemoteTestUtil( + 'queryAllElements', appId, itemEntriesQuery); + chrome.test.assertEq(1, itemEntries.length); + const childVolumeType = itemEntries[0].attributes['volume-type-for-testing']; + chrome.test.assertTrue('removable' !== childVolumeType); }; /**
diff --git a/ui/file_manager/integration_tests/video_player/background.js b/ui/file_manager/integration_tests/video_player/background.js index 58e88ab..6839ff4 100644 --- a/ui/file_manager/integration_tests/video_player/background.js +++ b/ui/file_manager/integration_tests/video_player/background.js
@@ -46,8 +46,6 @@ return Promise.all([ remoteCallVideoPlayer.waitForElement( appWindow, '#video-player[first-video][last-video]'), - remoteCallVideoPlayer.waitForElement( - appWindow, '.play.media-button[state="playing"]'), ]); }).then(function(args) { return [appWindow, args[0]];
diff --git a/ui/file_manager/integration_tests/video_player/check_elements.js b/ui/file_manager/integration_tests/video_player/check_elements.js index 52a9a907..e379229 100644 --- a/ui/file_manager/integration_tests/video_player/check_elements.js +++ b/ui/file_manager/integration_tests/video_player/check_elements.js
@@ -17,12 +17,6 @@ remoteCallVideoPlayer.waitForElement(appId, 'html[i18n-processed]'), remoteCallVideoPlayer.waitForElement(appId, 'div#video-player'), remoteCallVideoPlayer.waitForElement(appId, '#video-container > video'), - remoteCallVideoPlayer.waitForElement(appId, 'files-icon-button.play') - .then(function(element) { - // files-icon-button is a Polymer element and should have a - // shadowRoot. - chrome.test.assertTrue(element.hasShadowRoot); - }), ]); }); };
diff --git a/ui/file_manager/integration_tests/video_player/click_control_buttons.js b/ui/file_manager/integration_tests/video_player/click_control_buttons.js index 2a4fd08..24e337d 100644 --- a/ui/file_manager/integration_tests/video_player/click_control_buttons.js +++ b/ui/file_manager/integration_tests/video_player/click_control_buttons.js
@@ -28,68 +28,6 @@ } /** - * The openSingleImage test for Downloads. - * @return {Promise} Promise to be fulfilled with on success. - */ -testcase.clickControlButtons = function() { - var openVideo = openSingleVideo('local', 'downloads', ENTRIES.world); - var appId; - return openVideo - .then(function(args) { - appId = args[0]; - // Video player starts playing given file automatically. - return waitForFunctionResult('isPlaying', 'world.ogv', true); - }) - .then(function() { - // Play will finish in 2 seconds (world.ogv is 2-second short movie.) - return waitForFunctionResult('isPlaying', 'world.ogv', false); - }) - .then(function() { - // Conform that clicking play button will re-play the video. - return remoteCallVideoPlayer.callRemoteTestUtil( - 'fakeMouseClick', appId, ['.media-button.play']); - }) - .then(function() { - return waitForFunctionResult('isPlaying', 'world.ogv', true); - }) - .then(function() { - // Confirm that clicking volume button mutes the video. - return remoteCallVideoPlayer.callRemoteTestUtil( - 'fakeMouseClick', appId, ['.media-button.sound']); - }) - .then(function() { - return waitForFunctionResult('isMuted', 'world.ogv', true); - }) - .then(function() { - // Confirm that clicking volume button again unmutes the video. - return remoteCallVideoPlayer.callRemoteTestUtil( - 'fakeMouseClick', appId, ['.media-button.sound']); - }) - .then(function() { - return waitForFunctionResult('isMuted', 'world.ogv', false); - }) - .then(function() { - // Confirm that clicking fullscreen button enables fullscreen mode. - return remoteCallVideoPlayer.callRemoteTestUtil( - 'fakeMouseClick', appId, ['.media-button.fullscreen']); - }) - .then(function() { - return remoteCallVideoPlayer.waitForElement( - appId, '#controls[fullscreen]'); - }) - .then(function() { - // Confirm that clicking fullscreen-exit button disables fullscreen - // mode. - return remoteCallVideoPlayer.callRemoteTestUtil( - 'fakeMouseClick', appId, ['.media-button.fullscreen']); - }) - .then(function() { - return remoteCallVideoPlayer.waitForElement( - appId, '#controls:not([fullscreen])'); - }); -}; - -/** * Confirms that native media keys are dispatched correctly. * @return {Promise} Promise to be fulfilled on success. */
diff --git a/ui/file_manager/integration_tests/video_player/open_video_files.js b/ui/file_manager/integration_tests/video_player/open_video_files.js index e0685039..fcdf2d2 100644 --- a/ui/file_manager/integration_tests/video_player/open_video_files.js +++ b/ui/file_manager/integration_tests/video_player/open_video_files.js
@@ -9,11 +9,16 @@ * @return {Promise} Promise to be fulfilled with on success. */ testcase.openSingleVideoOnDownloads = function() { - var test = openSingleVideo('local', 'downloads', ENTRIES.world); - return test.then(function(args) { - var videoPlayer = args[1]; - chrome.test.assertFalse('cast-available' in videoPlayer.attributes); - }); + var test = openSingleVideo('local', 'downloads', ENTRIES.world); + return test + .then(function() { + // Video player starts playing given file automatically. + return waitForFunctionResult('isPlaying', 'world.ogv', true); + }) + .then(function() { + // Play will finish in 2 seconds (world.ogv is 2-second short movie.) + return waitForFunctionResult('isPlaying', 'world.ogv', false); + }); }; /** @@ -21,17 +26,14 @@ * @return {Promise} Promise to be fulfilled with on success. */ testcase.openSingleVideoOnDrive = function() { - var test = openSingleVideo('drive', 'drive', ENTRIES.world); - return test.then(function(args) { - var appWindow = args[0]; - var videoPlayer = args[1]; - chrome.test.assertFalse('cast-available' in videoPlayer.attributes); - - return remoteCallVideoPlayer.callRemoteTestUtil( - 'loadMockCastExtension', appWindow, []).then(function() { - // Loads cast extension and wait for available cast. - return remoteCallVideoPlayer.waitForElement( - appWindow, '#video-player[cast-available]'); + var test = openSingleVideo('drive', 'drive', ENTRIES.world); + return test + .then(function() { + // Video player starts playing given file automatically. + return waitForFunctionResult('isPlaying', 'world.ogv', true); + }) + .then(function() { + // Play will finish in 2 seconds (world.ogv is 2-second short movie.) + return waitForFunctionResult('isPlaying', 'world.ogv', false); }); - }); };
diff --git a/ui/file_manager/video_player/js/video_player_native_controls.js b/ui/file_manager/video_player/js/video_player_native_controls.js index 70eb6443b..71bb4b0 100644 --- a/ui/file_manager/video_player/js/video_player_native_controls.js +++ b/ui/file_manager/video_player/js/video_player_native_controls.js
@@ -278,6 +278,8 @@ * @private */ NativeControlsVideoPlayer.prototype.loadVideo_ = function(video, opt_callback) { + document.title = video.name; + this.videoElement_.src = video.toURL(); if (opt_callback) { this.videoElement_.addEventListener(
diff --git a/ui/gl/gl_context_egl.cc b/ui/gl/gl_context_egl.cc index a8f1c62..00471969 100644 --- a/ui/gl/gl_context_egl.cc +++ b/ui/gl/gl_context_egl.cc
@@ -62,11 +62,7 @@ namespace gl { GLContextEGL::GLContextEGL(GLShareGroup* share_group) - : GLContextReal(share_group), - context_(EGL_NO_CONTEXT), - display_(EGL_NO_DISPLAY), - config_(nullptr), - unbind_fbo_on_makecurrent_(false) {} + : GLContextReal(share_group) {} bool GLContextEGL::Initialize(GLSurface* compatible_surface, const GLContextAttribs& attribs) { @@ -278,8 +274,10 @@ bool GLContextEGL::MakeCurrent(GLSurface* surface) { DCHECK(context_); + if (lost_) + return false; if (IsCurrent(surface)) - return true; + return true; ScopedReleaseCurrent release_current; TRACE_EVENT2("gpu", "GLContextEGL::MakeCurrent", @@ -326,14 +324,20 @@ glBindFramebufferEXT(GL_FRAMEBUFFER, 0); SetCurrent(nullptr); - eglMakeCurrent(display_, - EGL_NO_SURFACE, - EGL_NO_SURFACE, - EGL_NO_CONTEXT); + if (!eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + DVLOG(1) << "eglMakeCurrent failed to release current with error " + << GetLastEGLErrorString(); + lost_ = true; + } + + DCHECK(!IsCurrent(nullptr)); } bool GLContextEGL::IsCurrent(GLSurface* surface) { DCHECK(context_); + if (lost_) + return false; bool native_context_is_current = context_ == eglGetCurrentContext();
diff --git a/ui/gl/gl_context_egl.h b/ui/gl/gl_context_egl.h index 3e0346c..09ffe6a 100644 --- a/ui/gl/gl_context_egl.h +++ b/ui/gl/gl_context_egl.h
@@ -45,10 +45,11 @@ void Destroy(); void ReleaseYUVToRGBConverters(); - EGLContext context_; - EGLDisplay display_; - EGLConfig config_; - bool unbind_fbo_on_makecurrent_; + EGLContext context_ = nullptr; + EGLDisplay display_ = nullptr; + EGLConfig config_ = nullptr; + bool unbind_fbo_on_makecurrent_ = false; + bool lost_ = false; std::map<gfx::ColorSpace, std::unique_ptr<YUVToRGBConverter>> yuv_to_rgb_converters_;
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc index 4df26ab..8dfa5bde 100644 --- a/ui/gl/gl_surface_egl_surface_control.cc +++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -31,7 +31,11 @@ GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl( ANativeWindow* window, scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : root_surface_(new SurfaceControl::Surface(window, kRootSurfaceName)), + : window_rect_(0, + 0, + ANativeWindow_getWidth(window), + ANativeWindow_getHeight(window)), + root_surface_(new SurfaceControl::Surface(window, kRootSurfaceName)), gpu_task_runner_(std::move(task_runner)), weak_factory_(this) {} @@ -57,7 +61,7 @@ float scale_factor, ColorSpace color_space, bool has_alpha) { - // Resizing requires resizing the SurfaceView in the browser. + window_rect_ = gfx::Rect(0, 0, size.width(), size.height()); return true; } @@ -71,31 +75,68 @@ return gfx::SwapResult::SWAP_FAILED; } -void GLSurfaceEGLSurfaceControl::SwapBuffersAsync( - SwapCompletionCallback completion_callback, - PresentationCallback presentation_callback) { - CommitPendingTransaction(std::move(completion_callback), - std::move(presentation_callback)); -} - gfx::SwapResult GLSurfaceEGLSurfaceControl::CommitOverlayPlanes( PresentationCallback callback) { NOTREACHED(); return gfx::SwapResult::SWAP_FAILED; } +gfx::SwapResult GLSurfaceEGLSurfaceControl::PostSubBuffer( + int x, + int y, + int width, + int height, + PresentationCallback callback) { + NOTREACHED(); + return gfx::SwapResult::SWAP_FAILED; +} + +void GLSurfaceEGLSurfaceControl::SwapBuffersAsync( + SwapCompletionCallback completion_callback, + PresentationCallback presentation_callback) { + CommitPendingTransaction(window_rect_, std::move(completion_callback), + std::move(presentation_callback)); +} + void GLSurfaceEGLSurfaceControl::CommitOverlayPlanesAsync( SwapCompletionCallback completion_callback, PresentationCallback presentation_callback) { - CommitPendingTransaction(std::move(completion_callback), + CommitPendingTransaction(window_rect_, std::move(completion_callback), + std::move(presentation_callback)); +} + +void GLSurfaceEGLSurfaceControl::PostSubBufferAsync( + int x, + int y, + int width, + int height, + SwapCompletionCallback completion_callback, + PresentationCallback presentation_callback) { + CommitPendingTransaction(gfx::Rect(x, y, width, height), + std::move(completion_callback), std::move(presentation_callback)); } void GLSurfaceEGLSurfaceControl::CommitPendingTransaction( + const gfx::Rect& damage_rect, SwapCompletionCallback completion_callback, PresentationCallback present_callback) { DCHECK(pending_transaction_); + // Mark the intersection of a surface's rect with the damage rect as the dirty + // rect for that surface. + DCHECK_LE(pending_surfaces_count_, surface_list_.size()); + for (size_t i = 0; i < pending_surfaces_count_; ++i) { + const auto& surface_state = surface_list_[i]; + if (!surface_state.buffer_updated_in_pending_transaction) + continue; + + gfx::Rect surface_damage_rect = surface_state.dst; + surface_damage_rect.Intersect(damage_rect); + pending_transaction_->SetDamageRect(*surface_state.surface, + surface_damage_rect); + } + // Release resources for the current frame once the next frame is acked. ResourceRefs resources_to_release; resources_to_release.swap(current_frame_resources_); @@ -167,7 +208,9 @@ resource_ref.scoped_buffer = std::move(scoped_hardware_buffer); } - if (uninitialized || surface_state.hardware_buffer != hardware_buffer) { + surface_state.buffer_updated_in_pending_transaction = + uninitialized || surface_state.hardware_buffer != hardware_buffer; + if (surface_state.buffer_updated_in_pending_transaction) { surface_state.hardware_buffer = hardware_buffer; if (!fence_fd.is_valid() && gpu_fence && surface_state.hardware_buffer) { @@ -220,6 +263,10 @@ return nullptr; } +bool GLSurfaceEGLSurfaceControl::SupportsPostSubBuffer() { + return true; +} + bool GLSurfaceEGLSurfaceControl::SupportsAsyncSwap() { return true; } @@ -232,11 +279,6 @@ return true; } -bool GLSurfaceEGLSurfaceControl::SupportsSwapBuffersWithBounds() { - // TODO(khushalsagar): Add support for partial swap. - return false; -} - bool GLSurfaceEGLSurfaceControl::SupportsCommitOverlayPlanes() { return true; }
diff --git a/ui/gl/gl_surface_egl_surface_control.h b/ui/gl/gl_surface_egl_surface_control.h index 6a7ab0a..ec5e93b 100644 --- a/ui/gl/gl_surface_egl_surface_control.h +++ b/ui/gl/gl_surface_egl_surface_control.h
@@ -41,13 +41,7 @@ ColorSpace color_space, bool has_alpha) override; bool IsOffscreen() override; - gfx::SwapResult SwapBuffers(PresentationCallback callback) override; - void SwapBuffersAsync(SwapCompletionCallback completion_callback, - PresentationCallback presentation_callback) override; - gfx::SwapResult CommitOverlayPlanes(PresentationCallback callback) override; - void CommitOverlayPlanesAsync( - SwapCompletionCallback completion_callback, - PresentationCallback presentation_callback) override; + gfx::Size GetSize() override; bool OnMakeCurrent(GLContext* context) override; bool ScheduleOverlayPlane(int z_order, @@ -60,10 +54,31 @@ bool IsSurfaceless() const override; void* GetHandle() override; + // Sync versions of frame update, should never be used. + gfx::SwapResult SwapBuffers(PresentationCallback callback) override; + gfx::SwapResult CommitOverlayPlanes(PresentationCallback callback) override; + gfx::SwapResult PostSubBuffer(int x, + int y, + int width, + int height, + PresentationCallback callback) override; + + void SwapBuffersAsync(SwapCompletionCallback completion_callback, + PresentationCallback presentation_callback) override; + void CommitOverlayPlanesAsync( + SwapCompletionCallback completion_callback, + PresentationCallback presentation_callback) override; + void PostSubBufferAsync(int x, + int y, + int width, + int height, + SwapCompletionCallback completion_callback, + PresentationCallback presentation_callback) override; + bool SupportsAsyncSwap() override; bool SupportsPlaneGpuFences() const override; bool SupportsPresentationCallback() override; - bool SupportsSwapBuffersWithBounds() override; + bool SupportsPostSubBuffer() override; bool SupportsCommitOverlayPlanes() override; private: @@ -84,6 +99,11 @@ gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE; bool opaque = true; + // Indicates whether buffer for this layer was updated in the currently + // pending transaction, or the last transaction submitted if there isn't + // one pending. + bool buffer_updated_in_pending_transaction = true; + scoped_refptr<SurfaceControl::Surface> surface; }; @@ -99,7 +119,8 @@ }; using ResourceRefs = base::flat_map<ASurfaceControl*, ResourceRef>; - void CommitPendingTransaction(SwapCompletionCallback completion_callback, + void CommitPendingTransaction(const gfx::Rect& damage_rect, + SwapCompletionCallback completion_callback, PresentationCallback callback); // Called on the |gpu_task_runner_| when a transaction is acked by the @@ -110,6 +131,9 @@ ResourceRefs released_resources, SurfaceControl::TransactionStats transaction_stats); + // The rect of the native window backing this surface. + gfx::Rect window_rect_; + // Holds the surface state changes made since the last call to SwapBuffers. base::Optional<SurfaceControl::Transaction> pending_transaction_;
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc index e6935b3..a6a23e9 100644 --- a/ui/message_center/views/message_popup_collection_unittest.cc +++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -161,7 +161,7 @@ } void Activate() { - set_can_activate(true); + SetCanActivate(true); GetWidget()->Activate(); }
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc index 9da43a6..b5025f3 100644 --- a/ui/message_center/views/notification_view_md.cc +++ b/ui/message_center/views/notification_view_md.cc
@@ -1291,7 +1291,7 @@ } void NotificationViewMD::Activate() { - GetWidget()->widget_delegate()->set_can_activate(true); + GetWidget()->widget_delegate()->SetCanActivate(true); GetWidget()->Activate(); }
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc index bf98dcaf..bccfaad 100644 --- a/ui/message_center/views/notification_view_md_unittest.cc +++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -317,7 +317,7 @@ widget_->SetContentsView(notification_view_.get()); widget_->SetSize(notification_view_->GetPreferredSize()); widget_->Show(); - widget_->widget_delegate()->set_can_activate(true); + widget_->widget_delegate()->SetCanActivate(true); widget_->Activate(); } else { notification_view_->UpdateWithNotification(notification);
diff --git a/ui/ozone/platform/scenic/scenic_surface_factory.cc b/ui/ozone/platform/scenic/scenic_surface_factory.cc index ed0c5e4..c896272 100644 --- a/ui/ozone/platform/scenic/scenic_surface_factory.cc +++ b/ui/ozone/platform/scenic/scenic_surface_factory.cc
@@ -7,8 +7,8 @@ #include <lib/zx/event.h> #include "base/bind.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "ui/gfx/native_pixmap.h" @@ -215,7 +215,7 @@ fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!scenic_) { - scenic_ = base::fuchsia::ComponentContext::GetDefault() + scenic_ = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::ui::scenic::Scenic>(); scenic_.set_error_handler([](zx_status_t status) { ZX_LOG(FATAL, status) << "Scenic connection failed";
diff --git a/ui/ozone/platform/scenic/scenic_window_manager.cc b/ui/ozone/platform/scenic/scenic_window_manager.cc index 82b0e01f..c19d20d 100644 --- a/ui/ozone/platform/scenic/scenic_window_manager.cc +++ b/ui/ozone/platform/scenic/scenic_window_manager.cc
@@ -4,8 +4,8 @@ #include "ui/ozone/platform/scenic/scenic_window_manager.h" -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" #include "ui/ozone/platform/scenic/ozone_platform_scenic.h" namespace ui { @@ -22,7 +22,7 @@ fuchsia::ui::viewsv1::ViewManager* ScenicWindowManager::GetViewManager() { if (!view_manager_) { - view_manager_ = base::fuchsia::ComponentContext::GetDefault() + view_manager_ = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::ui::viewsv1::ViewManager>(); view_manager_.set_error_handler([](zx_status_t status) { ZX_LOG(FATAL, status) << " ViewManager lost."; @@ -34,7 +34,7 @@ fuchsia::ui::scenic::Scenic* ScenicWindowManager::GetScenic() { if (!scenic_) { - scenic_ = base::fuchsia::ComponentContext::GetDefault() + scenic_ = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<fuchsia::ui::scenic::Scenic>(); scenic_.set_error_handler( [](zx_status_t status) { ZX_LOG(FATAL, status) << " Scenic lost."; });
diff --git a/ui/platform_window/fuchsia/initialize_presenter_api_view.cc b/ui/platform_window/fuchsia/initialize_presenter_api_view.cc index af7be03..353538a 100644 --- a/ui/platform_window/fuchsia/initialize_presenter_api_view.cc +++ b/ui/platform_window/fuchsia/initialize_presenter_api_view.cc
@@ -7,8 +7,8 @@ #include <fuchsia/ui/policy/cpp/fidl.h> #include <lib/zx/eventpair.h> -#include "base/fuchsia/component_context.h" #include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/service_directory_client.h" namespace ui { namespace fuchsia { @@ -26,7 +26,7 @@ ZX_CHECK(status == ZX_OK, status) << "zx_eventpair_create"; // Request Presenter to show the view full-screen. - auto presenter = base::fuchsia::ComponentContext::GetDefault() + auto presenter = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() ->ConnectToService<::fuchsia::ui::policy::Presenter>(); presenter->Present2(std::move(view_holder_token), nullptr);
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc index 5b2313f..a7ba97b1 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc +++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -301,13 +301,13 @@ EXPECT_TRUE(bubble_widget->IsVisible()); } -// Test that setting WidgetDelegate::set_can_activate() to false makes the +// Test that setting WidgetDelegate::SetCanActivate() to false makes the // widget created via BubbleDialogDelegateView::CreateBubble() not activatable. TEST_F(BubbleDialogDelegateViewTest, NotActivatable) { std::unique_ptr<Widget> anchor_widget(CreateTestWidget()); BubbleDialogDelegateView* bubble_delegate = new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); - bubble_delegate->set_can_activate(false); + bubble_delegate->SetCanActivate(false); Widget* bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble_delegate); bubble_widget->Show();
diff --git a/ui/views/bubble/info_bubble.cc b/ui/views/bubble/info_bubble.cc index 442287d..1caa3895 100644 --- a/ui/views/bubble/info_bubble.cc +++ b/ui/views/bubble/info_bubble.cc
@@ -51,7 +51,7 @@ set_margins(LayoutProvider::Get()->GetInsetsMetric( InsetsMetric::INSETS_TOOLTIP_BUBBLE)); - set_can_activate(false); + SetCanActivate(false); SetLayoutManager(std::make_unique<FillLayout>()); Label* label = new Label(message);
diff --git a/ui/views/bubble/tooltip_icon.cc b/ui/views/bubble/tooltip_icon.cc index 09be527..0ff1e6bd 100644 --- a/ui/views/bubble/tooltip_icon.cc +++ b/ui/views/bubble/tooltip_icon.cc
@@ -87,7 +87,7 @@ bubble_->SetArrow(anchor_point_arrow_); // When shown due to a gesture event, close on deactivate (i.e. don't use // "focusless"). - bubble_->set_can_activate(!mouse_inside_); + bubble_->SetCanActivate(!mouse_inside_); bubble_->Show(); observer_.Add(bubble_->GetWidget());
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc index d12dbd9..7ed01f8 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -934,6 +934,11 @@ SetBoundsInPixels(rect, viz::LocalSurfaceIdAllocation()); } +void DesktopWindowTreeHostMus::OnCanActivateChanged() { + MusClient::Get()->window_tree_client()->SetCanFocus( + window(), native_widget_delegate_->CanActivate()); +} + void DesktopWindowTreeHostMus::OnWindowManagerFrameValuesChanged() { NonClientView* non_client_view = native_widget_delegate_->AsWidget()->non_client_view();
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h index ed0ba6c..4c424c8 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.h +++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -149,6 +149,7 @@ bool ShouldUseDesktopNativeCursorManager() const override; bool ShouldCreateVisibilityController() const override; void SetBoundsInDIP(const gfx::Rect& bounds_in_dip) override; + void OnCanActivateChanged() override; // MusClientObserver: void OnWindowManagerFrameValuesChanged() override;
diff --git a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc index 1e83dbb..5b61586fe 100644 --- a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc +++ b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -1075,6 +1075,30 @@ EXPECT_FALSE(window->GetProperty(aura::client::kMaximumSize)); } +TEST_F(DesktopWindowTreeHostMusTest, SetCanFocus) { + auto* delegate = new WidgetDelegateView; + std::unique_ptr<Widget> widget = CreateWidget(delegate); + // Swap the WindowTree implementation to verify SetCanFocus() is called when + // the active state changes. + aura::TestWindowTree test_window_tree; + aura::WindowTreeClientTestApi window_tree_client_private( + MusClient::Get()->window_tree_client()); + ws::mojom::WindowTree* old_tree = + window_tree_client_private.SwapTree(&test_window_tree); + + delegate->SetCanActivate(false); + EXPECT_FALSE(delegate->CanActivate()); + EXPECT_FALSE(test_window_tree.last_can_focus()); + EXPECT_EQ(1u, test_window_tree.get_and_clear_can_focus_count()); + + delegate->SetCanActivate(true); + EXPECT_TRUE(delegate->CanActivate()); + EXPECT_TRUE(test_window_tree.last_can_focus()); + EXPECT_EQ(1u, test_window_tree.get_and_clear_can_focus_count()); + + window_tree_client_private.SwapTree(old_tree); +} + // DesktopWindowTreeHostMusTest with --force-device-scale-factor=1.25. class DesktopWindowTreeHostMusTestFractionalDPI : public DesktopWindowTreeHostMusTest {
diff --git a/ui/views/touchui/touch_selection_menu_views.cc b/ui/views/touchui/touch_selection_menu_views.cc index 0c339bfe..f8287f13 100644 --- a/ui/views/touchui/touch_selection_menu_views.cc +++ b/ui/views/touchui/touch_selection_menu_views.cc
@@ -50,7 +50,7 @@ set_shadow(BubbleBorder::SMALL_SHADOW); set_parent_window(context); set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin)); - set_can_activate(false); + SetCanActivate(false); set_adjust_if_offscreen(true); EnableCanvasFlippingForRTLUI(true);
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc index 0f09d44f..50a8a92 100644 --- a/ui/views/view_unittest.cc +++ b/ui/views/view_unittest.cc
@@ -2143,7 +2143,7 @@ // TYPE_POPUP widgets default to non-activatable, so the Show() above wouldn't // have activated the Widget. First, allow activation. - widget->widget_delegate()->set_can_activate(true); + widget->widget_delegate()->SetCanActivate(true); // When a non-child view is active, it should handle accelerators. view->accelerator_count_map_[return_accelerator] = 0;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 0015739..6d1eb72 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -1005,6 +1005,10 @@ desktop_window_tree_host_->SizeConstraintsChanged(); } +void DesktopNativeWidgetAura::OnCanActivateChanged() { + desktop_window_tree_host_->OnCanActivateChanged(); +} + std::string DesktopNativeWidgetAura::GetName() const { return name_; }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 8e85a95..f21c8eb 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -188,6 +188,7 @@ bool IsTranslucentWindowOpacitySupported() const override; ui::GestureRecognizer* GetGestureRecognizer() override; void OnSizeConstraintsChanged() override; + void OnCanActivateChanged() override; std::string GetName() const override; // Overridden from aura::WindowDelegate:
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index ecac029..58f5681 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
@@ -731,7 +731,7 @@ // We should be able to activate the window even if the WidgetDelegate // says no, when a modal dialog is active. - widget_delegate.set_can_activate(false); + widget_delegate.SetCanActivate(false); Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( dialog_delegate, NULL, top_level_widget->GetNativeView());
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/ui/views/widget/desktop_aura/desktop_window_tree_host.h index a0ec589..d2bc1a78 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
@@ -191,6 +191,9 @@ // Sets the bounds in screen coordinate DIPs (WindowTreeHost generally // operates in pixels). This function is implemented in terms of Screen. virtual void SetBoundsInDIP(const gfx::Rect& bounds); + + // See description in Widget::OnCanActivateChanged(). + virtual void OnCanActivateChanged() {} }; } // namespace views
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm index e3b1af7..ceea26b 100644 --- a/ui/views/widget/native_widget_mac_unittest.mm +++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -1699,7 +1699,7 @@ canBecomeMainWindow]); // Disabling activation should prevent key and main status. - regular_widget->widget_delegate()->set_can_activate(false); + regular_widget->widget_delegate()->SetCanActivate(false); EXPECT_FALSE([regular_widget->GetNativeWindow().GetNativeNSWindow() canBecomeKeyWindow]); EXPECT_FALSE([regular_widget->GetNativeWindow().GetNativeNSWindow()
diff --git a/ui/views/widget/native_widget_private.cc b/ui/views/widget/native_widget_private.cc index a5933a8..1983a9c 100644 --- a/ui/views/widget/native_widget_private.cc +++ b/ui/views/widget/native_widget_private.cc
@@ -26,5 +26,7 @@ ui::ShowEmojiPanel(); } +void NativeWidgetPrivate::OnCanActivateChanged() {} + } // namespace internal } // namespace views
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h index e4af6f1..0373ddb 100644 --- a/ui/views/widget/native_widget_private.h +++ b/ui/views/widget/native_widget_private.h
@@ -231,6 +231,7 @@ virtual bool IsTranslucentWindowOpacitySupported() const = 0; virtual ui::GestureRecognizer* GetGestureRecognizer() = 0; virtual void OnSizeConstraintsChanged() = 0; + virtual void OnCanActivateChanged(); // Returns an internal name that matches the name of the associated Widget. virtual std::string GetName() const = 0;
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 496e724..04a14bf9 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -329,7 +329,7 @@ widget_delegate_ = params.delegate ? params.delegate : new DefaultWidgetDelegate(this); - widget_delegate_->set_can_activate(can_activate); + widget_delegate_->SetCanActivate(can_activate); // Henceforth, ensure the delegate outlives the Widget. widget_delegate_->can_delete_this_ = false; @@ -1027,6 +1027,11 @@ void Widget::OnOwnerClosing() {} +void Widget::OnCanActivateChanged() { + if (native_widget_) + native_widget_->OnCanActivateChanged(); +} + std::string Widget::GetName() const { return native_widget_->GetName(); }
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index 0323bbb..bb13f4e9 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h
@@ -795,6 +795,9 @@ // Called when the delegate's CanResize or CanMaximize changes. void OnSizeConstraintsChanged(); + // Called when WidgetDelegate::CanActivate() changes. + void OnCanActivateChanged(); + // Notification that our owner is closing. // NOTE: this is not invoked for aura as it's currently not needed there. // Under aura menus close by way of activation getting reset when the owner
diff --git a/ui/views/widget/widget_delegate.cc b/ui/views/widget/widget_delegate.cc index fb1eb96f..c27fb35 100644 --- a/ui/views/widget/widget_delegate.cc +++ b/ui/views/widget/widget_delegate.cc
@@ -23,6 +23,19 @@ CHECK(can_delete_this_) << "A WidgetDelegate must outlive its Widget"; } +void WidgetDelegate::SetCanActivate(bool can_activate) { + if (can_activate == can_activate_) + return; + + const bool previous_value = CanActivate(); + can_activate_ = can_activate; + if (previous_value != CanActivate()) { + Widget* widget = GetWidget(); + if (widget) + widget->OnCanActivateChanged(); + } +} + void WidgetDelegate::OnWidgetMove() { }
diff --git a/ui/views/widget/widget_delegate.h b/ui/views/widget/widget_delegate.h index 186a5509..75ace4b 100644 --- a/ui/views/widget/widget_delegate.h +++ b/ui/views/widget/widget_delegate.h
@@ -32,9 +32,7 @@ WidgetDelegate(); // Sets the return value of CanActivate(). Default is true. - void set_can_activate(bool can_activate) { - can_activate_ = can_activate; - } + void SetCanActivate(bool can_activate); // Called whenever the widget's position changes. virtual void OnWidgetMove();
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc index f5e2d3e..d2cf16d1 100644 --- a/ui/views/widget/widget_interactive_uitest.cc +++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -202,12 +202,12 @@ // active. But we can simulate deactivation (e.g. as if another application // became active) by temporarily making |widget| non-activatable, then // activating (and closing) a temporary widget. - widget->widget_delegate()->set_can_activate(false); + widget->widget_delegate()->SetCanActivate(false); Widget* stealer = new Widget; stealer->Init(Widget::InitParams(Widget::InitParams::TYPE_WINDOW)); ShowSync(stealer); stealer->CloseNow(); - widget->widget_delegate()->set_can_activate(true); + widget->widget_delegate()->SetCanActivate(true); #else views::test::WidgetActivationWaiter waiter(widget, false); widget->Deactivate();