diff --git a/DEPS b/DEPS index 226d73b..3de63a4c 100644 --- a/DEPS +++ b/DEPS
@@ -129,11 +129,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': 'bb482ab871a29fad004f318210e0e01d44110a37', + 'skia_revision': 'b6a3a3b245a5f97a2c1325278dab90193f69e9df', # 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': '6859f97f97d4fce1850b2256b82435e7f412d5f5', + 'v8_revision': 'e160126cb3cb635a47b992812a6983ee4a6fdc6a', # 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. @@ -141,19 +141,19 @@ # 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': '3f7ace324e178a9466f3f1a002e3e5a025c070df', + 'angle_revision': '9cce3cd9e376ff5889bad52fb197774ca6b3bc52', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '52a67b6495ce4973c4e17830107f09c35f7abbcc', + 'swiftshader_revision': '37904182dd242a2734576cf11151a8350fca3031', # 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': '21d000094adeb6be3b97f3758523c6117f334e82', + 'pdfium_revision': '010a4da72d171ad386d1475e4f2e9e1620ed765c', # 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. - 'openmax_dl_revision': '3b18bf8338abbb9cc0dd5de4c69609c38eee9c8b', + 'openmax_dl_revision': '25492ab7195649acce190708e095692402593e2b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -196,7 +196,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': '1ca62263a6307ab8ffcf90fdb00c9d8933d15d65', + 'catapult_revision': '83131f4f49335945ea647db8f35fbe286a185152', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -252,7 +252,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. - 'spv_tools_revision': '320a7de5c9a58912edd4167d80523f26e2f57ccd', + 'spv_tools_revision': '0300a464a4ccdfac0052fcc0524a67592e9b19e8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -805,7 +805,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '507bf397d15ce43076ded2298db4a23bdecc2b1d', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f6cbd3926d95820760927770b3c9fbde84a17999', 'condition': 'checkout_linux', }, @@ -1343,7 +1343,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '688fbfe33779392aa210d67d4aa12cb012f112c2', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '4e2d015be8f7898151d0b76366a00d856899fb61', + Var('webrtc_git') + '/src.git' + '@' + '6c072efe9f0ea1d4a286d71e85013f5c43ab919f', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1384,7 +1384,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7ecd04321b1bf725d3745fe909363a6eade9a1f1', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@096bd921bbabae23f5b8826106e82e3c95ffbadf', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index aebf419..0a8e33ab 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -597,6 +597,8 @@ "browser/net/aw_network_change_notifier_factory.h", "browser/net/aw_network_delegate.cc", "browser/net/aw_network_delegate.h", + "browser/net/aw_proxy_config_monitor.cc", + "browser/net/aw_proxy_config_monitor.h", "browser/net/aw_request_interceptor.cc", "browser/net/aw_request_interceptor.h", "browser/net/aw_url_request_context_getter.cc",
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h index ae00e7b..a22e50f 100644 --- a/android_webview/browser/aw_browser_context.h +++ b/android_webview/browser/aw_browser_context.h
@@ -9,6 +9,7 @@ #include <vector> #include "android_webview/browser/aw_ssl_host_state_delegate.h" +#include "android_webview/browser/net/aw_proxy_config_monitor.h" #include "android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.h" #include "base/compiler_specific.h" #include "base/files/file_path.h"
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index 4889f81..0a40e96 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc
@@ -27,6 +27,7 @@ #include "android_webview/browser/aw_speech_recognition_manager_delegate.h" #include "android_webview/browser/aw_web_contents_view_delegate.h" #include "android_webview/browser/cookie_manager.h" +#include "android_webview/browser/net/aw_proxy_config_monitor.h" #include "android_webview/browser/net/aw_url_request_context_getter.h" #include "android_webview/browser/network_service/aw_cookie_manager_wrapper.h" #include "android_webview/browser/network_service/aw_proxying_url_loader_factory.h" @@ -359,6 +360,11 @@ g_created_network_context_params = true; #endif context_params->check_clear_text_permitted = g_check_cleartext_permitted; + + // Add proxy settings + AwProxyConfigMonitor::GetInstance()->AddProxyToNetworkContextParams( + context_params); + return context_params; }
diff --git a/android_webview/browser/aw_content_browser_client_unittest.cc b/android_webview/browser/aw_content_browser_client_unittest.cc index e72a1927..af3e83a 100644 --- a/android_webview/browser/aw_content_browser_client_unittest.cc +++ b/android_webview/browser/aw_content_browser_client_unittest.cc
@@ -3,12 +3,26 @@ // found in the LICENSE file. #include "android_webview/browser/aw_content_browser_client.h" + #include "android_webview/browser/aw_feature_list_creator.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/scoped_task_environment.h" +#include "mojo/core/embedder/embedder.h" +#include "services/network/public/cpp/features.h" #include "testing/gtest/include/gtest/gtest.h" namespace android_webview { -class AwContentBrowserClientTest : public testing::Test {}; +class AwContentBrowserClientTest : public testing::Test { + protected: + void SetUp() override { + mojo::core::Init(); + feature_list_.InitAndEnableFeature(network::features::kNetworkService); + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + base::test::ScopedFeatureList feature_list_; +}; TEST_F(AwContentBrowserClientTest, DisableCreatingTaskScheduler) { AwFeatureListCreator aw_feature_list_creator;
diff --git a/android_webview/browser/aw_proxy_controller.cc b/android_webview/browser/aw_proxy_controller.cc index 087ca95..bbf9e6c4 100644 --- a/android_webview/browser/aw_proxy_controller.cc +++ b/android_webview/browser/aw_proxy_controller.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "android_webview/browser/aw_browser_context.h" +#include "android_webview/browser/net/aw_proxy_config_monitor.h" #include "android_webview/browser/net/aw_url_request_context_getter.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" @@ -70,11 +71,14 @@ std::vector<std::string> bypass_rules; base::android::AppendJavaStringArrayToStringVector(env, jbypass_rules, &bypass_rules); - std::string result; if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - // TODO(laisminchillo): implement the Network Service code path - // (http://crbug.com/902658). + result = AwProxyConfigMonitor::GetInstance()->SetProxyOverride( + proxy_rules, bypass_rules, + base::BindOnce(&ProxyOverrideChanged, + ScopedJavaGlobalRef<jobject>(env, obj), + ScopedJavaGlobalRef<jobject>(env, listener), + ScopedJavaGlobalRef<jobject>(env, executor))); } else { result = AwBrowserContext::GetDefault() @@ -95,8 +99,10 @@ const JavaParamRef<jobject>& listener, const JavaParamRef<jobject>& executor) { if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - // TODO(laisminchillo): implement the Network Service code path - // (http://crbug.com/902658). + AwProxyConfigMonitor::GetInstance()->ClearProxyOverride(base::BindOnce( + &ProxyOverrideChanged, ScopedJavaGlobalRef<jobject>(env, obj), + ScopedJavaGlobalRef<jobject>(env, listener), + ScopedJavaGlobalRef<jobject>(env, executor))); } else { AwBrowserContext::GetDefault() ->GetAwURLRequestContext()
diff --git a/android_webview/browser/gfx/aw_draw_fn_impl.cc b/android_webview/browser/gfx/aw_draw_fn_impl.cc index eb687069..4299b44 100644 --- a/android_webview/browser/gfx/aw_draw_fn_impl.cc +++ b/android_webview/browser/gfx/aw_draw_fn_impl.cc
@@ -500,10 +500,12 @@ // If we have a |gl_done_fd|, create a Skia GrBackendSemaphore from // |gl_done_fd| and wait. if (gl_done_fd.is_valid()) { - VkSemaphore gl_done_semaphore; - if (!vulkan_context_provider_->implementation()->ImportSemaphoreFdKHR( - vulkan_context_provider_->device(), std::move(gl_done_fd), - &gl_done_semaphore)) { + VkSemaphore gl_done_semaphore = + vulkan_context_provider_->implementation()->ImportSemaphoreHandle( + vulkan_context_provider_->device(), + gpu::SemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + std::move(gl_done_fd))); + if (gl_done_semaphore == VK_NULL_HANDLE) { LOG(ERROR) << "Could not create Vulkan semaphore for GL completion."; return; } @@ -624,12 +626,15 @@ LOG(ERROR) << "Skia could not submit GrSemaphore."; return; } - if (!vulkan_context_provider_->implementation()->GetSemaphoreFdKHR( - vulkan_context_provider_->device(), pending_draw->post_draw_semaphore, - &pending_draw->sync_fd)) { + gpu::SemaphoreHandle semaphore_handle = + vulkan_context_provider_->implementation()->GetSemaphoreHandle( + vulkan_context_provider_->device(), + pending_draw->post_draw_semaphore); + if (!semaphore_handle.is_valid()) { LOG(ERROR) << "Could not retrieve SyncFD from |post_draw_semaphore|."; return; } + pending_draw->sync_fd = semaphore_handle.TakeHandle(); DCHECK(VK_NULL_HANDLE == pending_draw->post_draw_fence);
diff --git a/android_webview/browser/net/aw_proxy_config_monitor.cc b/android_webview/browser/net/aw_proxy_config_monitor.cc new file mode 100644 index 0000000..c9dd5b5 --- /dev/null +++ b/android_webview/browser/net/aw_proxy_config_monitor.cc
@@ -0,0 +1,105 @@ +// 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 "android_webview/browser/net/aw_proxy_config_monitor.h" + +#include <memory> +#include <utility> + +#include "base/barrier_closure.h" +#include "base/bind.h" +#include "base/feature_list.h" +#include "services/network/public/cpp/features.h" + +namespace android_webview { + +namespace { +base::LazyInstance<AwProxyConfigMonitor>::Leaky g_instance; +} // namespace + +AwProxyConfigMonitor::AwProxyConfigMonitor() { + proxy_config_service_android_ = + std::make_unique<net::ProxyConfigServiceAndroid>( + base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + proxy_config_service_android_->AddObserver(this); +} + +AwProxyConfigMonitor::~AwProxyConfigMonitor() { + proxy_config_service_android_->RemoveObserver(this); +} + +AwProxyConfigMonitor* AwProxyConfigMonitor::GetInstance() { + return g_instance.Pointer(); +} + +void AwProxyConfigMonitor::AddProxyToNetworkContextParams( + network::mojom::NetworkContextParamsPtr& network_context_params) { + network::mojom::ProxyConfigClientPtr proxy_config_client; + network_context_params->proxy_config_client_request = + mojo::MakeRequest(&proxy_config_client); + proxy_config_client_set_.AddPtr(std::move(proxy_config_client)); + + net::ProxyConfigWithAnnotation proxy_config; + net::ProxyConfigService::ConfigAvailability availability = + proxy_config_service_android_->GetLatestProxyConfig(&proxy_config); + if (availability == net::ProxyConfigService::CONFIG_VALID) { + network_context_params->initial_proxy_config = proxy_config; + } +} + +void AwProxyConfigMonitor::OnProxyConfigChanged( + const net::ProxyConfigWithAnnotation& config, + net::ProxyConfigService::ConfigAvailability availability) { + proxy_config_client_set_.ForAllPtrs( + [config, + availability](network::mojom::ProxyConfigClient* proxy_config_client) { + switch (availability) { + case net::ProxyConfigService::CONFIG_VALID: + proxy_config_client->OnProxyConfigUpdated(config); + break; + case net::ProxyConfigService::CONFIG_UNSET: + proxy_config_client->OnProxyConfigUpdated( + net::ProxyConfigWithAnnotation::CreateDirect()); + break; + case net::ProxyConfigService::CONFIG_PENDING: + NOTREACHED(); + break; + } + }); +} + +std::string AwProxyConfigMonitor::SetProxyOverride( + const std::vector<net::ProxyConfigServiceAndroid::ProxyOverrideRule>& + proxy_rules, + const std::vector<std::string>& bypass_rules, + base::OnceClosure callback) { + return proxy_config_service_android_->SetProxyOverride( + proxy_rules, bypass_rules, + base::BindOnce(&AwProxyConfigMonitor::FlushProxyConfig, + base::Unretained(this), std::move(callback))); +} + +void AwProxyConfigMonitor::ClearProxyOverride(base::OnceClosure callback) { + proxy_config_service_android_->ClearProxyOverride( + base::BindOnce(&AwProxyConfigMonitor::FlushProxyConfig, + base::Unretained(this), std::move(callback))); +} + +void AwProxyConfigMonitor::FlushProxyConfig(base::OnceClosure callback) { + int count = 0; + proxy_config_client_set_.ForAllPtrs( + [&count](network::mojom::ProxyConfigClient* proxy_config_client) { + ++count; + }); + + base::RepeatingClosure closure = + base::BarrierClosure(count, std::move(callback)); + proxy_config_client_set_.ForAllPtrs( + [closure](network::mojom::ProxyConfigClient* proxy_config_client) { + proxy_config_client->FlushProxyConfig(closure); + }); +} + +} // namespace android_webview
diff --git a/android_webview/browser/net/aw_proxy_config_monitor.h b/android_webview/browser/net/aw_proxy_config_monitor.h new file mode 100644 index 0000000..00e28340 --- /dev/null +++ b/android_webview/browser/net/aw_proxy_config_monitor.h
@@ -0,0 +1,56 @@ +// 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 ANDROID_WEBVIEW_BROWSER_NET_AW_PROXY_CONFIG_MONITOR_H_ +#define ANDROID_WEBVIEW_BROWSER_NET_AW_PROXY_CONFIG_MONITOR_H_ + +#include <memory> +#include <vector> + +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "net/proxy_resolution/proxy_config_service_android.h" +#include "net/url_request/url_request_context_builder.h" +#include "services/network/public/mojom/network_service.mojom.h" + +namespace android_webview { + +// This class configures proxy settings for NetworkContext if network service +// is enabled. +class AwProxyConfigMonitor : public net::ProxyConfigService::Observer { + public: + static AwProxyConfigMonitor* GetInstance(); + + void AddProxyToNetworkContextParams( + network::mojom::NetworkContextParamsPtr& network_context_params); + std::string SetProxyOverride( + const std::vector<net::ProxyConfigServiceAndroid::ProxyOverrideRule>& + proxy_rules, + const std::vector<std::string>& bypass_rules, + base::OnceClosure callback); + void ClearProxyOverride(base::OnceClosure callback); + + private: + AwProxyConfigMonitor(); + ~AwProxyConfigMonitor() override; + + AwProxyConfigMonitor(const AwProxyConfigMonitor&) = delete; + AwProxyConfigMonitor& operator=(const AwProxyConfigMonitor&) = delete; + + friend struct base::LazyInstanceTraitsBase<AwProxyConfigMonitor>; + + // net::ProxyConfigService::Observer implementation: + void OnProxyConfigChanged( + const net::ProxyConfigWithAnnotation& config, + net::ProxyConfigService::ConfigAvailability availability) override; + + void FlushProxyConfig(base::OnceClosure callback); + + std::unique_ptr<net::ProxyConfigServiceAndroid> proxy_config_service_android_; + mojo::InterfacePtrSet<network::mojom::ProxyConfigClient> + proxy_config_client_set_; +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_NET_AW_PROXY_CONFIG_MONITOR_H_
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java index 4c676927..a21570c4 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -556,7 +556,7 @@ @Test @MediumTest @Feature({"AndroidWebView", "Privacy"}) - public void testThirdPartyCookie_redirectFromThirdToFirst() throws Throwable { + public void testThirdPartyCookie_redirectFromThirdPartyToFirst() throws Throwable { TestWebServer webServer = TestWebServer.start(); try { allowFirstPartyCookies();
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc index b2dddca..117b4507 100644 --- a/ash/app_list/views/search_result_view.cc +++ b/ash/app_list/views/search_result_view.cc
@@ -284,10 +284,6 @@ return false; } -void SearchResultView::ChildPreferredSizeChanged(views::View* child) { - Layout(); -} - void SearchResultView::PaintButtonContents(gfx::Canvas* canvas) { gfx::Rect rect(GetContentsBounds()); if (rect.IsEmpty())
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h index b3b88c8..f9b7cd2 100644 --- a/ash/app_list/views/search_result_view.h +++ b/ash/app_list/views/search_result_view.h
@@ -102,7 +102,6 @@ gfx::Size CalculatePreferredSize() const override; void Layout() override; bool OnKeyPressed(const ui::KeyEvent& event) override; - void ChildPreferredSizeChanged(views::View* child) override; void PaintButtonContents(gfx::Canvas* canvas) override; void OnFocus() override; void OnBlur() override;
diff --git a/ash/media/media_notification_view.cc b/ash/media/media_notification_view.cc index c382e941..d8bb08d0 100644 --- a/ash/media/media_notification_view.cc +++ b/ash/media/media_notification_view.cc
@@ -44,13 +44,6 @@ constexpr gfx::Size kMediaButtonSize = gfx::Size(36, 36); constexpr int kMediaButtonRowSeparator = 8; constexpr gfx::Insets kMediaTitleArtistInsets = gfx::Insets(8, 8, 0, 8); -constexpr gfx::Insets kMediaNotificationMainRowInsets = - gfx::Insets(0, kDefaultMarginSize, 14, kRightMarginSize); -constexpr gfx::Insets kMediaNotificationExpandedMainRowInsets = - gfx::Insets(kDefaultMarginSize, - kDefaultMarginSize, - kDefaultMarginSize, - kRightMarginExpandedSize); constexpr int kMediaNotificationHeaderTopInset = 6; constexpr int kMediaNotificationHeaderRightInset = 6; constexpr int kMediaNotificationHeaderInset = 0; @@ -285,6 +278,13 @@ void MediaNotificationView::UpdateWithMediaArtwork( const gfx::ImageSkia& image) { GetMediaNotificationBackground()->UpdateArtwork(image); + + has_artwork_ = !image.isNull(); + UpdateViewForExpandedState(); + + PreferredSizeChanged(); + Layout(); + SchedulePaint(); } void MediaNotificationView::UpdateWithMediaIcon(const gfx::ImageSkia& image) { @@ -337,16 +337,23 @@ main_row_ ->SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kVertical, - kMediaNotificationExpandedMainRowInsets, kDefaultMarginSize)) + gfx::Insets( + kDefaultMarginSize, kDefaultMarginSize, kDefaultMarginSize, + has_artwork_ ? kRightMarginExpandedSize : kDefaultMarginSize), + kDefaultMarginSize)) ->SetDefaultFlex(1); } else { main_row_ ->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::kHorizontal, kMediaNotificationMainRowInsets, + views::BoxLayout::kHorizontal, + gfx::Insets(0, kDefaultMarginSize, 14, + has_artwork_ ? kRightMarginSize : kDefaultMarginSize), kDefaultMarginSize, true)) ->SetDefaultFlex(1); } + main_row_->Layout(); + GetMediaNotificationBackground()->UpdateArtworkMaxWidthPct( expanded_ ? kMediaImageMaxWidthExpandedPct : kMediaImageMaxWidthPct);
diff --git a/ash/media/media_notification_view.h b/ash/media/media_notification_view.h index 9256a5e8..2405f47 100644 --- a/ash/media/media_notification_view.h +++ b/ash/media/media_notification_view.h
@@ -94,6 +94,8 @@ std::unique_ptr<message_center::NotificationControlButtonsView> control_buttons_view_; + bool has_artwork_ = false; + // Whether this notification is expanded or not. bool expanded_ = false;
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc index 613607d..04c8dab 100644 --- a/ash/media/media_notification_view_unittest.cc +++ b/ash/media/media_notification_view_unittest.cc
@@ -621,6 +621,7 @@ } TEST_F(MediaNotificationViewTest, UpdateArtworkFromItem) { + int title_artist_width = title_artist_row()->width(); gfx::Size size = view()->size(); SkBitmap image; @@ -632,6 +633,10 @@ GetItem()->MediaControllerImageChanged( media_session::mojom::MediaSessionImageType::kArtwork, image); + // Ensure the title artist row has a small width than before now that we + // have artwork. + EXPECT_GT(title_artist_width, title_artist_row()->width()); + // Ensure that the image is displayed in the background artwork and that the // size of the notification was not affected. EXPECT_FALSE(GetArtworkImage().isNull()); @@ -641,6 +646,10 @@ GetItem()->MediaControllerImageChanged( media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap()); + // Ensure the title artist row goes back to the original width now that we + // do not have any artwork. + EXPECT_EQ(title_artist_width, title_artist_row()->width()); + // Ensure that the background artwork was reset and the size was still not // affected. EXPECT_TRUE(GetArtworkImage().isNull());
diff --git a/ash/public/cpp/caption_buttons/frame_back_button.cc b/ash/public/cpp/caption_buttons/frame_back_button.cc index a8cdb436..09e990b 100644 --- a/ash/public/cpp/caption_buttons/frame_back_button.cc +++ b/ash/public/cpp/caption_buttons/frame_back_button.cc
@@ -10,7 +10,6 @@ #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/events/event_sink.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/widget/widget.h" #include "ui/views/window/caption_button_layout_constants.h" @@ -29,15 +28,14 @@ void FrameBackButton::ButtonPressed(Button* sender, const ui::Event& event) { // Send up event as well as down event as ARC++ clients expect this sequence. + // TODO: Investigate if we should be using the current modifiers. aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow(); ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK, ui::EF_NONE); - ignore_result(root_window->GetHost()->event_sink()->OnEventFromSource( - &press_key_event)); + ignore_result(root_window->GetHost()->SendEventToSink(&press_key_event)); ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED, ui::VKEY_BROWSER_BACK, ui::EF_NONE); - ignore_result(root_window->GetHost()->event_sink()->OnEventFromSource( - &release_key_event)); + ignore_result(root_window->GetHost()->SendEventToSink(&release_key_event)); } } // namespace ash
diff --git a/ash/public/cpp/frame_header.h b/ash/public/cpp/frame_header.h index a644e30..3c1f0f8 100644 --- a/ash/public/cpp/frame_header.h +++ b/ash/public/cpp/frame_header.h
@@ -34,6 +34,10 @@ ~FrameHeader() override; + const base::string16& frame_text_override() const { + return frame_text_override_; + } + // Returns the header's minimum width. int GetMinimumHeaderWidth() const;
diff --git a/ash/shelf/back_button.cc b/ash/shelf/back_button.cc index a36bcc6..31b2595e 100644 --- a/ash/shelf/back_button.cc +++ b/ash/shelf/back_button.cc
@@ -5,24 +5,12 @@ #include "ash/shelf/back_button.h" #include "ash/resources/vector_icons/vector_icons.h" -#include "ash/shelf/shelf.h" -#include "ash/shelf/shelf_constants.h" -#include "ash/shelf/shelf_view.h" -#include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" -#include "ash/system/tray/tray_popup_utils.h" -#include "ash/wm/tablet_mode/tablet_mode_controller.h" -#include "base/metrics/user_metrics.h" -#include "base/metrics/user_metrics_action.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/events/event_sink.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" -#include "ui/views/animation/flood_fill_ink_drop_ripple.h" -#include "ui/views/animation/ink_drop_impl.h" -#include "ui/views/animation/ink_drop_mask.h" #include "ui/views/widget/widget.h" namespace ash { @@ -36,15 +24,18 @@ BackButton::~BackButton() = default; -void BackButton::OnGestureEvent(ui::GestureEvent* event) { - ShelfButton::OnGestureEvent(event); - if (event->type() == ui::ET_GESTURE_TAP) - GenerateAndSendBackEvent(); -} +void BackButton::NotifyClick(const ui::Event& event) { + Button::NotifyClick(event); -void BackButton::OnMouseReleased(const ui::MouseEvent& event) { - ShelfButton::OnMouseReleased(event); - GenerateAndSendBackEvent(); + // Send up event as well as down event as ARC++ clients expect this sequence. + // TODO: Investigate if we should be using the current modifiers. + aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow(); + ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK, + ui::EF_NONE); + ignore_result(root_window->GetHost()->SendEventToSink(&press_key_event)); + ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED, ui::VKEY_BROWSER_BACK, + ui::EF_NONE); + ignore_result(root_window->GetHost()->SendEventToSink(&release_key_event)); } void BackButton::PaintButtonContents(gfx::Canvas* canvas) { @@ -59,18 +50,4 @@ return kViewClassName; } -void BackButton::GenerateAndSendBackEvent() { - base::RecordAction(base::UserMetricsAction("Tablet_BackButton")); - - // Send the back event to the root window of the back button's widget. - const views::Widget* widget = GetWidget(); - if (widget && widget->GetNativeWindow()) { - aura::Window* root_window = widget->GetNativeWindow()->GetRootWindow(); - ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK, - ui::EF_NONE); - ignore_result( - root_window->GetHost()->event_sink()->OnEventFromSource(&key_event)); - } -} - } // namespace ash
diff --git a/ash/shelf/back_button.h b/ash/shelf/back_button.h index 7a8119a1..8eab8f2 100644 --- a/ash/shelf/back_button.h +++ b/ash/shelf/back_button.h
@@ -25,18 +25,11 @@ protected: // views::Button: - void OnGestureEvent(ui::GestureEvent* event) override; - void OnMouseReleased(const ui::MouseEvent& event) override; + void NotifyClick(const ui::Event& event) override; void PaintButtonContents(gfx::Canvas* canvas) override; const char* GetClassName() const override; private: - // Generate and send a VKEY_BROWSER_BACK key event sequence when the back - // button is pressed. This should on be called on a tap down or mouse release - // event, and will only send a key down event since that is the one which - // triggers the back event. - void GenerateAndSendBackEvent(); - DISALLOW_COPY_AND_ASSIGN(BackButton); };
diff --git a/ash/shelf/back_button_unittest.cc b/ash/shelf/back_button_unittest.cc index d50767a0..c757909 100644 --- a/ash/shelf/back_button_unittest.cc +++ b/ash/shelf/back_button_unittest.cc
@@ -117,7 +117,7 @@ generator->ReleaseLeftButton(); EXPECT_EQ(1, target_back_press.accelerator_count()); - EXPECT_EQ(0, target_back_release.accelerator_count()); + EXPECT_EQ(1, target_back_release.accelerator_count()); } } // namespace ash
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 94fa12c6..67daa75 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -294,12 +294,13 @@ // or the overflow shelf. ShelfView* main_shelf() { return main_shelf_ ? main_shelf_ : this; } // Returns the overflow shelf. This can be called on either the main shelf - // or the overflow shelf. Returns nullptr if there is no overflow shelf. + // or the overflow shelf. Returns nullptr if the overflow shelf isn't visible. ShelfView* overflow_shelf() { if (is_overflow_mode()) return this; - return overflow_bubble_ ? overflow_bubble_->bubble_view()->shelf_view() - : nullptr; + return IsShowingOverflowBubble() + ? overflow_bubble_->bubble_view()->shelf_view() + : nullptr; } const ShelfAppButton* drag_view() const { return drag_view_; }
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc index 139bc6d..e2e8c4f 100644 --- a/ash/system/accessibility/tray_accessibility.cc +++ b/ash/system/accessibility/tray_accessibility.cc
@@ -77,11 +77,9 @@ TrayPopupUtils::UpdateCheckMarkVisibility(select_to_speak_view_, select_to_speak_enabled_); - if (dictation_view_) { - dictation_enabled_ = controller->dictation_enabled(); - TrayPopupUtils::UpdateCheckMarkVisibility(dictation_view_, - dictation_enabled_); - } + dictation_enabled_ = controller->dictation_enabled(); + TrayPopupUtils::UpdateCheckMarkVisibility(dictation_view_, + dictation_enabled_); high_contrast_enabled_ = controller->high_contrast_enabled(); TrayPopupUtils::UpdateCheckMarkVisibility(high_contrast_view_,
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index f51eb24e1..58bed79 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -773,24 +773,27 @@ Shell::Get()->split_view_controller()->end_reason() == SplitViewController::EndReason::kUnsnappableWindowActivated; - if (state != SplitViewController::NO_SNAP || unsnappable_window_activated) { - // Do not restore focus if a window was just snapped and activated or - // splitview mode is ended by activating an unsnappable window. + // Restore focus unless either a window was just snapped (and activated) or + // split view mode was ended by activating an unsnappable window. + if (state != SplitViewController::NO_SNAP || unsnappable_window_activated) ResetFocusRestoreWindow(false); - } + // If two windows were snapped to both sides of the screen or an unsnappable + // window was just activated, end overview mode and bail out. if (state == SplitViewController::BOTH_SNAPPED || unsnappable_window_activated) { - // If two windows were snapped to both sides of the screen or an unsnappable - // window was just activated, end overview mode. CancelSelection(); - } else { - // Otherwise adjust the overview window grid bounds if overview mode is - // active at the moment. - OnDisplayBoundsChanged(); - for (auto& grid : grid_list_) - grid->UpdateCannotSnapWarningVisibility(); + return; } + + // Adjust the overview window grid bounds if overview mode is active. + OnDisplayBoundsChanged(); + for (auto& grid : grid_list_) + grid->UpdateCannotSnapWarningVisibility(); + + // Notify |split_view_drag_indicators_| if split view mode ended. + if (split_view_drag_indicators_ && state == SplitViewController::NO_SNAP) + split_view_drag_indicators_->OnSplitViewModeEnded(); } void OverviewSession::OnSplitViewDividerPositionChanged() {
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc index 81d0950..32d5acdf 100644 --- a/ash/wm/splitview/split_view_controller.cc +++ b/ash/wm/splitview/split_view_controller.cc
@@ -71,7 +71,9 @@ constexpr float kBlackScrimFadeInRatio = 0.1f; constexpr float kBlackScrimOpacity = 0.4f; -// The duration of the divider snap animation, in milliseconds. +// The duration of the divider snap animation, in milliseconds. Before you +// change this value, read the comment on kIsWindowMovedTimeoutMs in +// tablet_mode_window_drag_delegate.cc. constexpr int kDividerSnapDurationMs = 300; // Toast data.
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc index 979a3c9..959d2c3 100644 --- a/ash/wm/splitview/split_view_drag_indicators.cc +++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -314,24 +314,26 @@ ~SplitViewDragIndicatorsView() override {} - // Called by parent widget when the state machine changes. Handles setting the - // opacity and bounds of the highlights and labels based on the state. + // Called by parent widget when the state machine changes. void OnIndicatorTypeChanged(IndicatorState indicator_state) { DCHECK_NE(indicator_state_, indicator_state); - previous_indicator_state_ = indicator_state_; indicator_state_ = indicator_state; + Update(); + } - left_rotated_view_->OnIndicatorTypeChanged(indicator_state, + // Handles setting the opacity and bounds of the highlights and labels. + void Update() { + left_rotated_view_->OnIndicatorTypeChanged(indicator_state_, previous_indicator_state_); - right_rotated_view_->OnIndicatorTypeChanged(indicator_state, + right_rotated_view_->OnIndicatorTypeChanged(indicator_state_, previous_indicator_state_); - left_highlight_view_->OnIndicatorTypeChanged(indicator_state, + left_highlight_view_->OnIndicatorTypeChanged(indicator_state_, previous_indicator_state_); - right_highlight_view_->OnIndicatorTypeChanged(indicator_state, + right_highlight_view_->OnIndicatorTypeChanged(indicator_state_, previous_indicator_state_); - if (indicator_state != IndicatorState::kNone || + if (indicator_state_ != IndicatorState::kNone || IsPreviewAreaState(previous_indicator_state_)) Layout(previous_indicator_state_ != IndicatorState::kNone); } @@ -559,6 +561,14 @@ indicators_view_->OnIndicatorTypeChanged(current_indicator_state_); } +void SplitViewDragIndicators::OnSplitViewModeEnded() { + if (current_indicator_state_ == IndicatorState::kNone || + IsPreviewAreaState(current_indicator_state_)) { + return; + } + indicators_view_->Update(); +} + void SplitViewDragIndicators::OnDisplayBoundsChanged() { aura::Window* root_window = widget_->GetNativeView()->GetRootWindow(); widget_->SetBounds(GetWorkAreaBoundsNoOverlapWithShelf(root_window));
diff --git a/ash/wm/splitview/split_view_drag_indicators.h b/ash/wm/splitview/split_view_drag_indicators.h index e2f1710..a6b4c4a 100644 --- a/ash/wm/splitview/split_view_drag_indicators.h +++ b/ash/wm/splitview/split_view_drag_indicators.h
@@ -82,6 +82,11 @@ void SetIndicatorState(IndicatorState indicator_state, const gfx::Point& event_location); + // Called by owner of this class when split view mode ends, so that this class + // can show the drag indicators which are usually hidden in split view mode. + // https://crbug.com/946601 + void OnSplitViewModeEnded(); + // Called by owner of this class when display bounds changes are observed, so // that this class can relayout accordingly. void OnDisplayBoundsChanged();
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 c8f2f3b..d6943f27d 100644 --- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc +++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -41,7 +41,10 @@ // tablet mode. constexpr float kIndicatorsThresholdRatio = 0.1; -// Duration of a drag that it will be considered as an intended drag. +// Duration of a drag that it will be considered as an intended drag. Must be at +// least the duration of the split view divider snap animation, or else there +// will be an issue similar to https://crbug.com/946601 but involving dragging a +// snapped window from the top. constexpr base::TimeDelta kIsWindowMovedTimeoutMs = base::TimeDelta::FromMilliseconds(300);
diff --git a/base/profiler/register_context.h b/base/profiler/register_context.h index 92d2e51..7c7dfd8b 100644 --- a/base/profiler/register_context.h +++ b/base/profiler/register_context.h
@@ -54,6 +54,16 @@ #endif } +inline uintptr_t& RegisterContextInstructionPointer(::CONTEXT* context) { +#if defined(ARCH_CPU_X86_64) + return context->Rip; +#elif defined(ARCH_CPU_ARM64) + return context->Pc; +#else + return AsUintPtr(&context->Eip); +#endif +} + #elif defined(OS_MACOSX) && !defined(OS_IOS) // #if defined(OS_WIN) using RegisterContext = x86_thread_state64_t; @@ -66,12 +76,18 @@ return AsUintPtr(&context->__rbp); } +inline uintptr_t& RegisterContextInstructionPointer( + x86_thread_state64_t* context) { + return AsUintPtr(&context->__rip); +} + #else // #if defined(OS_WIN) // Placeholders for other platforms. struct RegisterContext { uintptr_t stack_pointer; uintptr_t frame_pointer; + uintptr_t instruction_pointer; }; inline uintptr_t& RegisterContextStackPointer(RegisterContext* context) { @@ -82,6 +98,10 @@ return context->frame_pointer; } +inline uintptr_t& RegisterContextInstructionPointer(RegisterContext* context) { + return context->instruction_pointer; +} + #endif // #if defined(OS_WIN) #endif // BASE_PROFILER_REGISTER_CONTEXT_H_
diff --git a/base/profiler/stack_sampler_impl.cc b/base/profiler/stack_sampler_impl.cc index 563829e..f89be3c 100644 --- a/base/profiler/stack_sampler_impl.cc +++ b/base/profiler/stack_sampler_impl.cc
@@ -163,6 +163,11 @@ // fewer. stack.reserve(128); + // Record the first frame from the context values. + stack.emplace_back(RegisterContextInstructionPointer(thread_context), + module_cache_->GetModuleForAddress( + RegisterContextInstructionPointer(thread_context))); + thread_delegate_->WalkNativeFrames(thread_context, stack_top, module_cache_, &stack);
diff --git a/base/profiler/thread_delegate.h b/base/profiler/thread_delegate.h index 17e55a9..d415106 100644 --- a/base/profiler/thread_delegate.h +++ b/base/profiler/thread_delegate.h
@@ -68,7 +68,9 @@ RegisterContext* thread_context) = 0; // Walks the native frames on the stack pointed to by the stack pointer in - // |thread_context|, appending the frames to |stack|. + // |thread_context|, appending the frames to |stack|. When invoked + // stack->back() contains the frame corresponding to the state in + // |thread_context|. // TODO(wittman): Move the unwinding support into a separate UnwindDelegate. virtual UnwindResult WalkNativeFrames( RegisterContext* thread_context,
diff --git a/base/profiler/thread_delegate_mac.cc b/base/profiler/thread_delegate_mac.cc index 1426bd41..33e8a13 100644 --- a/base/profiler/thread_delegate_mac.cc +++ b/base/profiler/thread_delegate_mac.cc
@@ -231,11 +231,9 @@ uintptr_t stack_top, ModuleCache* module_cache, std::vector<ProfileBuilder::Frame>* stack) { - // Record the first frame from the context values. - unw_word_t instruction_pointer = thread_context->__rip; - const ModuleCache::Module* module = - module_cache->GetModuleForAddress(instruction_pointer); - stack->emplace_back(instruction_pointer, module); + // We expect the frame corresponding to the |thread_context| register state to + // exist within |stack|. + DCHECK_GT(stack->size(), 0u); // There isn't an official way to create a unw_context other than to create it // from the current state of the current thread's stack. Since we're walking a @@ -250,20 +248,17 @@ // Avoid an out-of-bounds read bug in libunwind that can crash us in some // circumstances. If we're subject to that case, just record the first frame // and bail. See MayTriggerUnwInitLocalCrash for details. - const ModuleCache::Module* leaf_frame_module = - module_cache->GetModuleForAddress(instruction_pointer); - if (leaf_frame_module && MayTriggerUnwInitLocalCrash(leaf_frame_module)) + if (stack->back().module && MayTriggerUnwInitLocalCrash(stack->back().module)) return UnwindResult::ABORTED; unw_cursor_t unwind_cursor; unw_init_local(&unwind_cursor, &unwind_context); - bool at_top_frame = true; int step_result; for (;;) { // First frame unwind step, check pre-conditions for attempting a frame // unwind. - if (!module) { + if (!stack->back().module) { // There's no loaded module containing the instruction pointer. This is // due to either executing code that is not in a module (e.g. V8 // runtime-generated code), or to a previous bad unwind. @@ -289,8 +284,8 @@ // is very fragile. It's a complex DWARF unwind that needs to restore the // entire thread context which was saved by the kernel when the interrupt // occurred. - if (instruction_pointer >= sigtramp_start_ && - instruction_pointer < sigtramp_end_) { + if (stack->back().instruction_pointer >= sigtramp_start_ && + stack->back().instruction_pointer < sigtramp_end_) { return UnwindResult::ABORTED; } @@ -304,13 +299,13 @@ unw_get_reg(&unwind_cursor, UNW_REG_SP, &prev_stack_pointer); step_result = unw_step(&unwind_cursor); - if (step_result == 0 && at_top_frame) { + if (step_result == 0 && stack->size() == 1u) { // libunwind is designed to be triggered by user code on their own thread, // if it hits a library that has no unwind info for the function that is // being executed, it just stops. This isn't a problem in the normal case, - // but in this case, it's quite possible that the stack being walked is - // stopped in a function that bridges to the kernel and thus is missing - // the unwind info. + // but in the case where this is the first frame unwind, it's quite + // possible that the stack being walked is stopped in a function that + // bridges to the kernel and thus is missing the unwind info. // For now, just unwind the single case where the thread is stopped in a // function in libsystem_kernel. @@ -331,9 +326,11 @@ return UnwindResult::ABORTED; // Fourth frame unwind step: record the frame to which we just unwound. + unw_word_t instruction_pointer; unw_get_reg(&unwind_cursor, UNW_REG_IP, &instruction_pointer); unw_word_t stack_pointer; unw_get_reg(&unwind_cursor, UNW_REG_SP, &stack_pointer); + // Record the frame if the last step was successful. if (step_result > 0 || // libunwind considers the unwind complete and returns 0 if no unwind @@ -344,8 +341,9 @@ // whether the stack pointer was moved by unw_step. If so, record the // new frame to enable non-native unwinders to continue the unwinding. (step_result == 0 && stack_pointer > prev_stack_pointer)) { - module = module_cache->GetModuleForAddress(instruction_pointer); - stack->emplace_back(instruction_pointer, module); + stack->emplace_back( + instruction_pointer, + module_cache->GetModuleForAddress(instruction_pointer)); } // libunwind returns 0 if it can't continue because no unwind info was found @@ -359,8 +357,6 @@ // signify that we couldn't unwind further. if (step_result == 0) return UnwindResult::UNRECOGNIZED_FRAME; - - at_top_frame = false; } NOTREACHED();
diff --git a/base/profiler/thread_delegate_win.cc b/base/profiler/thread_delegate_win.cc index 5eef627b..e37d560b 100644 --- a/base/profiler/thread_delegate_win.cc +++ b/base/profiler/thread_delegate_win.cc
@@ -205,15 +205,14 @@ uintptr_t stack_top, ModuleCache* module_cache, std::vector<ProfileBuilder::Frame>* stack) { - // Record the first frame from the context values. - const ModuleCache::Module* module = - module_cache->GetModuleForAddress(ContextPC(thread_context)); - stack->emplace_back(ContextPC(thread_context), module); + // We expect the frame corresponding to the |thread_context| register state to + // exist within |stack|. + DCHECK_GT(stack->size(), 0u); Win32StackFrameUnwinder frame_unwinder; for (;;) { - if (!module) { - // There's no loaded module containing the instruction pointer. This can + if (!stack->back().module) { + // There's no loaded module corresponding to the current frame. This can // be due to executing code that is not in a module (e.g. V8 generated // code or runtime-generated code associated with third-party injected // DLLs). It can also be due to the the module having been unloaded since @@ -233,15 +232,18 @@ return UnwindResult::UNRECOGNIZED_FRAME; } - if (!frame_unwinder.TryUnwind(stack->size() == 1u, thread_context, module)) + if (!frame_unwinder.TryUnwind(stack->size() == 1u, thread_context, + stack->back().module)) { return UnwindResult::ABORTED; + } if (ContextPC(thread_context) == 0) return UnwindResult::COMPLETED; // Record the frame to which we just unwound. - module = module_cache->GetModuleForAddress(ContextPC(thread_context)); - stack->emplace_back(ContextPC(thread_context), module); + stack->emplace_back( + ContextPC(thread_context), + module_cache->GetModuleForAddress(ContextPC(thread_context))); } NOTREACHED();
diff --git a/base/task/common/intrusive_heap.h b/base/task/common/intrusive_heap.h index 084afcaf..7e725b1a 100644 --- a/base/task/common/intrusive_heap.h +++ b/base/task/common/intrusive_heap.h
@@ -54,6 +54,15 @@ } } + IntrusiveHeap& operator=(IntrusiveHeap&& other) { + nodes_ = std::move(other.nodes_); + size_ = other.size_; + + other.size_ = 0; + + return *this; + } + bool empty() const { return size_ == 0; } size_t size() const { return size_; }
diff --git a/base/task/sequence_manager/task_queue_selector.cc b/base/task/sequence_manager/task_queue_selector.cc index 706f8ac..d898f71 100644 --- a/base/task/sequence_manager/task_queue_selector.cc +++ b/base/task/sequence_manager/task_queue_selector.cc
@@ -181,7 +181,7 @@ // the highest priority for which we have work, unless we are starving a lower // priority. TaskQueue::QueuePriority priority = active_priorities_.min_id(); - bool chose_delayed_over_immediate = false; + bool chose_delayed_over_immediate; // Control tasks are allowed to indefinitely stave out other work and any // control tasks we run should not be counted for task starvation purposes.
diff --git a/base/task/sequence_manager/task_queue_selector.h b/base/task/sequence_manager/task_queue_selector.h index db3e766b8..334237fa 100644 --- a/base/task/sequence_manager/task_queue_selector.h +++ b/base/task/sequence_manager/task_queue_selector.h
@@ -187,11 +187,12 @@ bool* out_chose_delayed_over_immediate) const { // Select an immediate work queue if we are starving immediate tasks. if (immediate_starvation_count_ >= kMaxDelayedStarvationTasks) { + *out_chose_delayed_over_immediate = false; WorkQueue* queue = - ChooseImmediateTaskWithPriority<SetOperation>(priority); + SetOperation::GetWithPriority(immediate_work_queue_sets_, priority); if (queue) return queue; - return ChooseDelayedTaskWithPriority<SetOperation>(priority); + return SetOperation::GetWithPriority(delayed_work_queue_sets_, priority); } return ChooseImmediateOrDelayedTaskWithPriority<SetOperation>( priority, out_chose_delayed_over_immediate); @@ -209,23 +210,11 @@ #endif template <typename SetOperation> - WorkQueue* ChooseImmediateTaskWithPriority( - TaskQueue::QueuePriority priority) const { - return SetOperation::GetWithPriority(immediate_work_queue_sets_, priority); - } - - template <typename SetOperation> - WorkQueue* ChooseDelayedTaskWithPriority( - TaskQueue::QueuePriority priority) const { - return SetOperation::GetWithPriority(delayed_work_queue_sets_, priority); - } - - template <typename SetOperation> WorkQueue* ChooseImmediateOrDelayedTaskWithPriority( TaskQueue::QueuePriority priority, bool* out_chose_delayed_over_immediate) const { - DCHECK_EQ(*out_chose_delayed_over_immediate, false); EnqueueOrder immediate_enqueue_order; + *out_chose_delayed_over_immediate = false; WorkQueue* immediate_queue = SetOperation::GetWithPriorityAndEnqueueOrder( immediate_work_queue_sets_, priority, &immediate_enqueue_order); if (immediate_queue) {
diff --git a/base/task/task_features.cc b/base/task/task_features.cc index cf56afb..97428587 100644 --- a/base/task/task_features.cc +++ b/base/task/task_features.cc
@@ -23,4 +23,9 @@ const Feature kMayBlockWithoutDelay = {"MayBlockWithoutDelay", base::FEATURE_DISABLED_BY_DEFAULT}; +#if defined(OS_WIN) || defined(OS_MACOSX) +const Feature kUseNativeThreadPool = {"UseNativeThreadPool", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif + } // namespace base
diff --git a/base/task/task_features.h b/base/task/task_features.h index 2026835..6f70af7 100644 --- a/base/task/task_features.h +++ b/base/task/task_features.h
@@ -7,6 +7,7 @@ #include "base/base_export.h" #include "base/metrics/field_trial_params.h" +#include "build/build_config.h" namespace base { @@ -23,6 +24,13 @@ // instead of waiting for a threshold. extern const BASE_EXPORT Feature kMayBlockWithoutDelay; +#if defined(OS_WIN) || defined(OS_MACOSX) +// Under this feature, TaskScheduler will use a SchedulerWorkerPool backed by a +// native thread pool implementation. The Windows Thread Pool API and +// libdispatch are used on Windows and macOS/iOS respectively. +extern const BASE_EXPORT Feature kUseNativeThreadPool; +#endif + } // namespace base #endif // BASE_TASK_TASK_FEATURES_H_
diff --git a/base/task/task_scheduler/platform_native_worker_pool.cc b/base/task/task_scheduler/platform_native_worker_pool.cc index f47efb5..243c413f 100644 --- a/base/task/task_scheduler/platform_native_worker_pool.cc +++ b/base/task/task_scheduler/platform_native_worker_pool.cc
@@ -39,8 +39,11 @@ PlatformNativeWorkerPool::PlatformNativeWorkerPool( TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate) - : SchedulerWorkerPool(std::move(task_tracker), std::move(delegate)) {} + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool) + : SchedulerWorkerPool(std::move(task_tracker), + std::move(delegate), + predecessor_pool) {} PlatformNativeWorkerPool::~PlatformNativeWorkerPool() { #if DCHECK_IS_ON()
diff --git a/base/task/task_scheduler/platform_native_worker_pool.h b/base/task/task_scheduler/platform_native_worker_pool.h index e5a0d86..ca6a639 100644 --- a/base/task/task_scheduler/platform_native_worker_pool.h +++ b/base/task/task_scheduler/platform_native_worker_pool.h
@@ -29,7 +29,8 @@ protected: PlatformNativeWorkerPool(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate); + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool); // Runs a task off the next sequence on the |priority_queue_|. Called by // callbacks posted to platform native thread pools.
diff --git a/base/task/task_scheduler/platform_native_worker_pool_mac.h b/base/task/task_scheduler/platform_native_worker_pool_mac.h index 06c76579..a091808 100644 --- a/base/task/task_scheduler/platform_native_worker_pool_mac.h +++ b/base/task/task_scheduler/platform_native_worker_pool_mac.h
@@ -26,7 +26,8 @@ : public PlatformNativeWorkerPool { public: PlatformNativeWorkerPoolMac(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate); + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool = nullptr); ~PlatformNativeWorkerPoolMac() override;
diff --git a/base/task/task_scheduler/platform_native_worker_pool_mac.mm b/base/task/task_scheduler/platform_native_worker_pool_mac.mm index 3c1acb2..fbccf5d 100644 --- a/base/task/task_scheduler/platform_native_worker_pool_mac.mm +++ b/base/task/task_scheduler/platform_native_worker_pool_mac.mm
@@ -11,8 +11,11 @@ PlatformNativeWorkerPoolMac::PlatformNativeWorkerPoolMac( TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate) - : PlatformNativeWorkerPool(std::move(task_tracker), std::move(delegate)) {} + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool) + : PlatformNativeWorkerPool(std::move(task_tracker), + std::move(delegate), + predecessor_pool) {} PlatformNativeWorkerPoolMac::~PlatformNativeWorkerPoolMac() {}
diff --git a/base/task/task_scheduler/platform_native_worker_pool_win.cc b/base/task/task_scheduler/platform_native_worker_pool_win.cc index 466aff2e..4178f33109 100644 --- a/base/task/task_scheduler/platform_native_worker_pool_win.cc +++ b/base/task/task_scheduler/platform_native_worker_pool_win.cc
@@ -11,8 +11,11 @@ PlatformNativeWorkerPoolWin::PlatformNativeWorkerPoolWin( TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate) - : PlatformNativeWorkerPool(std::move(task_tracker), std::move(delegate)) {} + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool) + : PlatformNativeWorkerPool(std::move(task_tracker), + std::move(delegate), + predecessor_pool) {} PlatformNativeWorkerPoolWin::~PlatformNativeWorkerPoolWin() { ::DestroyThreadpoolEnvironment(&environment_);
diff --git a/base/task/task_scheduler/platform_native_worker_pool_win.h b/base/task/task_scheduler/platform_native_worker_pool_win.h index 0e119ac8a..db94bf0 100644 --- a/base/task/task_scheduler/platform_native_worker_pool_win.h +++ b/base/task/task_scheduler/platform_native_worker_pool_win.h
@@ -28,7 +28,8 @@ : public PlatformNativeWorkerPool { public: PlatformNativeWorkerPoolWin(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate); + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool = nullptr); ~PlatformNativeWorkerPoolWin() override;
diff --git a/base/task/task_scheduler/priority_queue.cc b/base/task/task_scheduler/priority_queue.cc index a2176c7a..00a6954 100644 --- a/base/task/task_scheduler/priority_queue.cc +++ b/base/task/task_scheduler/priority_queue.cc
@@ -85,6 +85,8 @@ } } +PriorityQueue& PriorityQueue::operator=(PriorityQueue&& other) = default; + void PriorityQueue::Push(scoped_refptr<Sequence> sequence, const SequenceSortKey& sequence_sort_key) { container_.insert(SequenceAndSortKey(std::move(sequence), sequence_sort_key));
diff --git a/base/task/task_scheduler/priority_queue.h b/base/task/task_scheduler/priority_queue.h index 0571de69..3bb7d3db 100644 --- a/base/task/task_scheduler/priority_queue.h +++ b/base/task/task_scheduler/priority_queue.h
@@ -25,6 +25,8 @@ PriorityQueue(); ~PriorityQueue(); + PriorityQueue& operator=(PriorityQueue&& other); + // Inserts |sequence| in the PriorityQueue with |sequence_sort_key|. Note: // |sequence_sort_key| is required as a parameter instead of being extracted // from |sequence| in Push() to avoid this Transaction having a lock @@ -80,8 +82,8 @@ ContainerType container_; - size_t num_sequences_per_priority_[static_cast<int>(TaskPriority::HIGHEST) + - 1] = {}; + std::array<size_t, static_cast<int>(TaskPriority::HIGHEST) + 1> + num_sequences_per_priority_ = {}; // Should only be enabled by EnableFlushSequencesOnDestroyForTesting(). bool is_flush_sequences_on_destroy_enabled_ = false;
diff --git a/base/task/task_scheduler/scheduler_worker_pool.cc b/base/task/task_scheduler/scheduler_worker_pool.cc index 96cd114..14e5e36 100644 --- a/base/task/task_scheduler/scheduler_worker_pool.cc +++ b/base/task/task_scheduler/scheduler_worker_pool.cc
@@ -49,8 +49,11 @@ } SchedulerWorkerPool::SchedulerWorkerPool(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate) - : task_tracker_(std::move(task_tracker)), delegate_(std::move(delegate)) { + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool) + : task_tracker_(std::move(task_tracker)), + delegate_(std::move(delegate)), + lock_(predecessor_pool ? &predecessor_pool->lock_ : nullptr) { DCHECK(task_tracker_); } @@ -72,6 +75,11 @@ void SchedulerWorkerPool::OnCanScheduleSequence( scoped_refptr<Sequence> sequence) { + if (replacement_pool_) { + replacement_pool_->OnCanScheduleSequence(std::move(sequence)); + return; + } + PushSequenceAndWakeUpWorkers( SequenceAndTransaction::FromSequence(std::move(sequence))); } @@ -134,10 +142,19 @@ BaseScopedWorkersExecutor* executor, SequenceAndTransaction sequence_and_transaction) { AutoSchedulerLock auto_lock(lock_); + DCHECK(!replacement_pool_); priority_queue_.Push(std::move(sequence_and_transaction.sequence), sequence_and_transaction.transaction.GetSortKey()); EnsureEnoughWorkersLockRequired(executor); } +void SchedulerWorkerPool::InvalidateAndHandoffAllSequencesToOtherPool( + SchedulerWorkerPool* destination_pool) { + AutoSchedulerLock current_pool_lock(lock_); + AutoSchedulerLock destination_pool_lock(destination_pool->lock_); + destination_pool->priority_queue_ = std::move(priority_queue_); + replacement_pool_ = destination_pool; +} + } // namespace internal } // namespace base
diff --git a/base/task/task_scheduler/scheduler_worker_pool.h b/base/task/task_scheduler/scheduler_worker_pool.h index d5cb31e..bb6cf27c 100644 --- a/base/task/task_scheduler/scheduler_worker_pool.h +++ b/base/task/task_scheduler/scheduler_worker_pool.h
@@ -76,6 +76,15 @@ virtual void PushSequenceAndWakeUpWorkers( SequenceAndTransaction sequence_and_transaction) = 0; + // Removes all sequences from this pool's PriorityQueue and enqueues them in + // another |destination_pool|. After this method is called, any sequences + // posted to this pool will be forwarded to |destination_pool|. + // + // TODO(crbug.com/756547): Remove this method once the UseNativeThreadPool + // experiment is complete. + void InvalidateAndHandoffAllSequencesToOtherPool( + SchedulerWorkerPool* destination_pool); + // Prevents new tasks from starting to run and waits for currently running // tasks to complete their execution. It is guaranteed that no thread will do // work on behalf of this SchedulerWorkerPool after this returns. It is @@ -123,8 +132,19 @@ DISALLOW_COPY_AND_ASSIGN(ScopedReenqueueExecutor); }; + // |predecessor_pool| is a pool whose lock can be acquired before the + // constructed pool's lock. This is necessary to move all sequences from + // |predecessor_pool| to the constructed pool and support the + // UseNativeThreadPool experiment. + // + // TODO(crbug.com/756547): Remove |predecessor_pool| once the experiment is + // complete. SchedulerWorkerPool(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate); + TrackedRef<Delegate> delegate, + SchedulerWorkerPool* predecessor_pool = nullptr); + + const TrackedRef<TaskTracker> task_tracker_; + const TrackedRef<Delegate> delegate_; // Ensures that there are enough workers to run queued sequences. |executor| // is forwarded from the one received in PushSequenceAndWakeUpWorkersImpl() @@ -155,8 +175,10 @@ // PriorityQueue from which all threads of this worker pool get work. PriorityQueue priority_queue_ GUARDED_BY(lock_); - const TrackedRef<TaskTracker> task_tracker_; - const TrackedRef<Delegate> delegate_; + // If |replacement_pool_| is non-null, this pool is invalid and all sequences + // should be scheduled on |replacement_pool_|. Used to support the + // UseNativeThreadPool experiment. + SchedulerWorkerPool* replacement_pool_ = nullptr; private: DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerPool);
diff --git a/base/task/task_scheduler/scheduler_worker_pool_impl.cc b/base/task/task_scheduler/scheduler_worker_pool_impl.cc index e961e30..280d9b86 100644 --- a/base/task/task_scheduler/scheduler_worker_pool_impl.cc +++ b/base/task/task_scheduler/scheduler_worker_pool_impl.cc
@@ -399,6 +399,8 @@ SchedulerWorkerObserver* scheduler_worker_observer, WorkerEnvironment worker_environment, Optional<TimeDelta> may_block_threshold) { + DCHECK(!replacement_pool_); + ScopedWorkersExecutor executor(this); AutoSchedulerLock auto_lock(lock_);
diff --git a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc index 6d208a4..14f7a504 100644 --- a/base/task/task_scheduler/scheduler_worker_pool_unittest.cc +++ b/base/task/task_scheduler/scheduler_worker_pool_unittest.cc
@@ -56,15 +56,8 @@ constexpr size_t kNumThreadsPostingTasks = 4; constexpr size_t kNumTasksPostedPerThread = 150; -enum class PoolType { - GENERIC, -#if defined(OS_WIN) || defined(OS_MACOSX) - NATIVE, -#endif -}; - struct PoolExecutionType { - PoolType pool_type; + test::PoolType pool_type; test::ExecutionMode execution_mode; }; @@ -128,14 +121,14 @@ void CreateWorkerPool() { ASSERT_FALSE(worker_pool_); switch (GetParam().pool_type) { - case PoolType::GENERIC: + case test::PoolType::GENERIC: worker_pool_ = std::make_unique<SchedulerWorkerPoolImpl>( "TestWorkerPool", "A", ThreadPriority::NORMAL, task_tracker_.GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()); break; #if defined(OS_WIN) || defined(OS_MACOSX) - case PoolType::NATIVE: + case test::PoolType::NATIVE: worker_pool_ = std::make_unique<PlatformNativeWorkerPoolType>( task_tracker_.GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()); @@ -150,7 +143,7 @@ void StartWorkerPool() { ASSERT_TRUE(worker_pool_); switch (GetParam().pool_type) { - case PoolType::GENERIC: { + case test::PoolType::GENERIC: { SchedulerWorkerPoolImpl* scheduler_worker_pool_impl = static_cast<SchedulerWorkerPoolImpl*>(worker_pool_.get()); scheduler_worker_pool_impl->Start( @@ -160,7 +153,7 @@ break; } #if defined(OS_WIN) || defined(OS_MACOSX) - case PoolType::NATIVE: { + case test::PoolType::NATIVE: { PlatformNativeWorkerPoolType* scheduler_worker_pool_native_impl = static_cast<PlatformNativeWorkerPoolType*>(worker_pool_.get()); scheduler_worker_pool_native_impl->Start(); @@ -414,23 +407,24 @@ INSTANTIATE_TEST_SUITE_P(GenericParallel, TaskSchedulerWorkerPoolTest, ::testing::Values(PoolExecutionType{ - PoolType::GENERIC, + test::PoolType::GENERIC, test::ExecutionMode::PARALLEL})); INSTANTIATE_TEST_SUITE_P(GenericSequenced, TaskSchedulerWorkerPoolTest, ::testing::Values(PoolExecutionType{ - PoolType::GENERIC, + test::PoolType::GENERIC, test::ExecutionMode::SEQUENCED})); #if defined(OS_WIN) || defined(OS_MACOSX) INSTANTIATE_TEST_SUITE_P(NativeParallel, TaskSchedulerWorkerPoolTest, ::testing::Values(PoolExecutionType{ - PoolType::NATIVE, test::ExecutionMode::PARALLEL})); + test::PoolType::NATIVE, + test::ExecutionMode::PARALLEL})); INSTANTIATE_TEST_SUITE_P(NativeSequenced, TaskSchedulerWorkerPoolTest, ::testing::Values(PoolExecutionType{ - PoolType::NATIVE, + test::PoolType::NATIVE, test::ExecutionMode::SEQUENCED})); #endif
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc index ff50bc8..df1b4bd 100644 --- a/base/task/task_scheduler/task_scheduler_impl.cc +++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -83,6 +83,9 @@ // Reset worker pools to release held TrackedRefs, which block teardown. foreground_pool_.reset(); background_pool_.reset(); +#if defined(OS_WIN) || defined(OS_MACOSX) + native_foreground_pool_.reset(); +#endif } void TaskSchedulerImpl::Start( @@ -95,6 +98,16 @@ if (FeatureList::IsEnabled(kAllTasksUserBlocking)) all_tasks_user_blocking_.Set(); +#if defined(OS_WIN) || defined(OS_MACOSX) + if (FeatureList::IsEnabled(kUseNativeThreadPool)) { + native_foreground_pool_.emplace(task_tracker_->GetTrackedRef(), + tracked_ref_factory_.GetTrackedRef(), + &foreground_pool_.value()); + foreground_pool_->InvalidateAndHandoffAllSequencesToOtherPool( + &native_foreground_pool_.value()); + } +#endif + // Start the service thread. On platforms that support it (POSIX except NaCL // SFI), the service thread runs a MessageLoopForIO which is used to support // FileDescriptorWatcher in the scope in which tasks run. @@ -131,18 +144,26 @@ SchedulerWorkerPoolImpl::WorkerEnvironment::NONE; #endif - // On platforms that can't use the background thread priority, best-effort - // tasks run in foreground pools. A cap is set on the number of background - // tasks that can run in foreground pools to ensure that there is always room - // for incoming foreground tasks and to minimize the performance impact of - // best-effort tasks. - const int max_best_effort_tasks_in_foreground_pool = std::max( - 1, std::min(init_params.background_worker_pool_params.max_tasks(), - init_params.foreground_worker_pool_params.max_tasks() / 2)); - foreground_pool_->Start(init_params.foreground_worker_pool_params, - max_best_effort_tasks_in_foreground_pool, - service_thread_task_runner, scheduler_worker_observer, - worker_environment); +#if defined(OS_WIN) || defined(OS_MACOSX) + if (native_foreground_pool_) { + native_foreground_pool_->Start(); + } else +#endif + { + // On platforms that can't use the background thread priority, best-effort + // tasks run in foreground pools. A cap is set on the number of background + // tasks that can run in foreground pools to ensure that there is always + // room for incoming foreground tasks and to minimize the performance impact + // of best-effort tasks. + + const int max_best_effort_tasks_in_foreground_pool = std::max( + 1, std::min(init_params.background_worker_pool_params.max_tasks(), + init_params.foreground_worker_pool_params.max_tasks() / 2)); + foreground_pool_->Start(init_params.foreground_worker_pool_params, + max_best_effort_tasks_in_foreground_pool, + service_thread_task_runner, + scheduler_worker_observer, worker_environment); + } if (background_pool_.has_value()) { background_pool_->Start( @@ -235,7 +256,7 @@ // https://crbug.com/771701. service_thread_->Stop(); single_thread_task_runner_manager_.JoinForTesting(); - foreground_pool_->JoinForTesting(); + GetForegroundWorkerPool()->JoinForTesting(); if (background_pool_.has_value()) background_pool_->JoinForTesting(); #if DCHECK_IS_ON() @@ -330,6 +351,20 @@ background_pool_.has_value()) { return &background_pool_.value(); } + + return GetForegroundWorkerPool(); +} + +const SchedulerWorkerPool* TaskSchedulerImpl::GetForegroundWorkerPool() const { + return const_cast<TaskSchedulerImpl*>(this)->GetForegroundWorkerPool(); +} + +SchedulerWorkerPool* TaskSchedulerImpl::GetForegroundWorkerPool() { +#if defined(OS_WIN) || defined(OS_MACOSX) + if (native_foreground_pool_) { + return &native_foreground_pool_.value(); + } +#endif return &foreground_pool_.value(); } @@ -341,7 +376,7 @@ } void TaskSchedulerImpl::ReportHeartbeatMetrics() const { - foreground_pool_->ReportHeartbeatMetrics(); + GetForegroundWorkerPool()->ReportHeartbeatMetrics(); if (background_pool_.has_value()) background_pool_->ReportHeartbeatMetrics(); }
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h index 528101d..19dc61a1 100644 --- a/base/task/task_scheduler/task_scheduler_impl.h +++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -33,9 +33,14 @@ #endif #if defined(OS_WIN) +#include "base/task/task_scheduler/platform_native_worker_pool_win.h" #include "base/win/com_init_check_hook.h" #endif +#if defined(OS_MACOSX) +#include "base/task/task_scheduler/platform_native_worker_pool_mac.h" +#endif + namespace base { class Thread; @@ -103,6 +108,10 @@ void ReportHeartbeatMetrics() const; + // Returns the thread pool responsible for foreground execution. + const SchedulerWorkerPool* GetForegroundWorkerPool() const; + SchedulerWorkerPool* GetForegroundWorkerPool(); + const SchedulerWorkerPool* GetWorkerPoolForTraits( const TaskTraits& traits) const; @@ -133,6 +142,12 @@ Optional<SchedulerWorkerPoolImpl> foreground_pool_; Optional<SchedulerWorkerPoolImpl> background_pool_; +#if defined(OS_WIN) + Optional<PlatformNativeWorkerPoolWin> native_foreground_pool_; +#elif defined(OS_MACOSX) + Optional<PlatformNativeWorkerPoolMac> native_foreground_pool_; +#endif + #if DCHECK_IS_ON() // Set once JoinForTesting() has returned. AtomicFlag join_for_testing_returned_;
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc index dd57776..506f24e2 100644 --- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc +++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -61,11 +61,13 @@ struct TaskSchedulerImplTestParams { TaskSchedulerImplTestParams(const TaskTraits& traits, - test::ExecutionMode execution_mode) - : traits(traits), execution_mode(execution_mode) {} + test::ExecutionMode execution_mode, + test::PoolType pool_type) + : traits(traits), execution_mode(execution_mode), pool_type(pool_type) {} TaskTraits traits; test::ExecutionMode execution_mode; + test::PoolType pool_type; }; #if DCHECK_IS_ON() @@ -80,7 +82,7 @@ // Verify that the current thread priority and I/O restrictions are appropriate // to run a Task with |traits|. // Note: ExecutionMode is verified inside TestTaskFactory. -void VerifyTaskEnvironment(const TaskTraits& traits) { +void VerifyTaskEnvironment(const TaskTraits& traits, test::PoolType pool_type) { EXPECT_EQ(CanUseBackgroundPriorityForSchedulerWorker() && traits.priority() == TaskPriority::BEST_EFFORT ? ThreadPriority::BACKGROUND @@ -93,60 +95,71 @@ EXPECT_EQ(traits.may_block(), GetIOAllowed()); #endif - // Verify that the thread the task is running on is named as expected. const std::string current_thread_name(PlatformThread::GetName()); + const bool is_single_threaded = + (current_thread_name.find("SingleThread") != std::string::npos); + const bool is_best_effort = (traits.priority() == TaskPriority::BEST_EFFORT); + +#if defined(OS_WIN) || defined(OS_MACOSX) + // Native thread pools do not provide the ability to name threads. + if (pool_type == test::PoolType::NATIVE && !is_single_threaded && + !is_best_effort) { + return; + } +#endif + + // Verify that the thread the task is running on is named as expected. EXPECT_NE(std::string::npos, current_thread_name.find("TaskScheduler")); - if (current_thread_name.find("SingleThread") != std::string::npos) { + if (is_single_threaded) { // For now, single-threaded best-effort tasks run on their own threads. // TODO(fdoray): Run single-threaded best-effort tasks on foreground workers // on platforms that don't support background thread priority. EXPECT_NE( std::string::npos, - current_thread_name.find(traits.priority() == TaskPriority::BEST_EFFORT - ? "Background" - : "Foreground")); - } else { - EXPECT_NE(std::string::npos, - current_thread_name.find( - CanUseBackgroundPriorityForSchedulerWorker() && - traits.priority() == TaskPriority::BEST_EFFORT - ? "Background" - : "Foreground")); - } - if (current_thread_name.find("SingleThread") == std::string::npos) { - EXPECT_EQ(std::string::npos, current_thread_name.find("Blocking")); - } else { + current_thread_name.find(is_best_effort ? "Background" : "Foreground")); + // SingleThread workers discriminate blocking/non-blocking tasks. EXPECT_EQ(traits.may_block(), current_thread_name.find("Blocking") != std::string::npos); + } else { + EXPECT_NE(std::string::npos, + current_thread_name.find( + CanUseBackgroundPriorityForSchedulerWorker() && is_best_effort + ? "Background" + : "Foreground")); + + EXPECT_EQ(std::string::npos, current_thread_name.find("Blocking")); } } void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits, + test::PoolType pool_type, WaitableEvent* event) { DCHECK(event); - VerifyTaskEnvironment(traits); + VerifyTaskEnvironment(traits, pool_type); event->Signal(); } void VerifyTimeAndTaskEnvironmentAndSignalEvent(const TaskTraits& traits, + test::PoolType pool_type, TimeTicks expected_time, WaitableEvent* event) { DCHECK(event); EXPECT_LE(expected_time, TimeTicks::Now()); - VerifyTaskEnvironment(traits); + VerifyTaskEnvironment(traits, pool_type); event->Signal(); } void VerifyOrderAndTaskEnvironmentAndSignalEvent( const TaskTraits& traits, + test::PoolType pool_type, WaitableEvent* expected_previous_event, WaitableEvent* event) { DCHECK(event); if (expected_previous_event) EXPECT_TRUE(expected_previous_event->IsSignaled()); - VerifyTaskEnvironment(traits); + VerifyTaskEnvironment(traits, pool_type); event->Signal(); } @@ -176,9 +189,11 @@ // |execution_mode|. ThreadPostingTasks(TaskSchedulerImpl* scheduler, const TaskTraits& traits, + test::PoolType pool_type, test::ExecutionMode execution_mode) : SimpleThread("ThreadPostingTasks"), traits_(traits), + pool_type_(pool_type), factory_(CreateTaskRunnerWithTraitsAndExecutionMode(scheduler, traits, execution_mode), @@ -193,18 +208,19 @@ const size_t kNumTasksPerThread = 150; for (size_t i = 0; i < kNumTasksPerThread; ++i) { factory_.PostTask(test::TestTaskFactory::PostNestedTask::NO, - BindOnce(&VerifyTaskEnvironment, traits_)); + BindOnce(&VerifyTaskEnvironment, traits_, pool_type_)); } } const TaskTraits traits_; + test::PoolType pool_type_; test::TestTaskFactory factory_; DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks); }; // Returns a vector with a TaskSchedulerImplTestParams for each valid -// combination of {ExecutionMode, TaskPriority, MayBlock()}. +// combination of {PoolType, ExecutionMode, TaskPriority, MayBlock()}. std::vector<TaskSchedulerImplTestParams> GetTaskSchedulerImplTestParams() { std::vector<TaskSchedulerImplTestParams> params; @@ -212,14 +228,24 @@ test::ExecutionMode::PARALLEL, test::ExecutionMode::SEQUENCED, test::ExecutionMode::SINGLE_THREADED}; - for (test::ExecutionMode execution_mode : execution_modes) { - for (size_t priority_index = static_cast<size_t>(TaskPriority::LOWEST); - priority_index <= static_cast<size_t>(TaskPriority::HIGHEST); - ++priority_index) { - const TaskPriority priority = static_cast<TaskPriority>(priority_index); - params.push_back(TaskSchedulerImplTestParams({priority}, execution_mode)); - params.push_back( - TaskSchedulerImplTestParams({priority, MayBlock()}, execution_mode)); + const test::PoolType pool_types[] = { + test::PoolType::GENERIC, +#if defined(OS_WIN) || defined(OS_MACOSX) + test::PoolType::NATIVE, +#endif + }; + + for (test::PoolType pool_type : pool_types) { + for (test::ExecutionMode execution_mode : execution_modes) { + for (size_t priority_index = static_cast<size_t>(TaskPriority::LOWEST); + priority_index <= static_cast<size_t>(TaskPriority::HIGHEST); + ++priority_index) { + const TaskPriority priority = static_cast<TaskPriority>(priority_index); + params.push_back( + TaskSchedulerImplTestParams({priority}, execution_mode, pool_type)); + params.push_back(TaskSchedulerImplTestParams( + {priority, MayBlock()}, execution_mode, pool_type)); + } } } @@ -232,7 +258,7 @@ TaskSchedulerImplTest() : scheduler_("Test") {} void EnableAllTasksUserBlocking() { - feature_list_.InitWithFeatures({kAllTasksUserBlocking}, {}); + should_enable_all_tasks_user_blocking_ = true; } void set_scheduler_worker_observer( @@ -244,6 +270,8 @@ constexpr int kMaxNumBackgroundThreads = 1; constexpr int kMaxNumForegroundThreads = 4; + SetupFeatures(); + scheduler_.Start({{kMaxNumBackgroundThreads, reclaim_time}, {kMaxNumForegroundThreads, reclaim_time}}, scheduler_worker_observer_); @@ -261,9 +289,25 @@ TaskSchedulerImpl scheduler_; private: + void SetupFeatures() { + std::vector<base::Feature> features; + + if (should_enable_all_tasks_user_blocking_) + features.push_back(kAllTasksUserBlocking); + +#if defined(OS_WIN) || defined(OS_MACOSX) + if (GetParam().pool_type == test::PoolType::NATIVE) + features.push_back(kUseNativeThreadPool); +#endif + + if (!features.empty()) + feature_list_.InitWithFeatures(features, {}); + } + base::test::ScopedFeatureList feature_list_; SchedulerWorkerObserver* scheduler_worker_observer_ = nullptr; bool did_tear_down_ = false; + bool should_enable_all_tasks_user_blocking_ = false; DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImplTest); }; @@ -279,7 +323,7 @@ scheduler_.PostDelayedTaskWithTraits( FROM_HERE, GetParam().traits, BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits, - Unretained(&task_ran)), + GetParam().pool_type, Unretained(&task_ran)), TimeDelta()); task_ran.Wait(); } @@ -294,6 +338,7 @@ scheduler_.PostDelayedTaskWithTraits( FROM_HERE, GetParam().traits, BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits, + GetParam().pool_type, TimeTicks::Now() + TestTimeouts::tiny_timeout(), Unretained(&task_ran)), TestTimeouts::tiny_timeout()); @@ -314,7 +359,8 @@ const size_t kNumTasksPerTest = 150; for (size_t i = 0; i < kNumTasksPerTest; ++i) { factory.PostTask(test::TestTaskFactory::PostNestedTask::NO, - BindOnce(&VerifyTaskEnvironment, GetParam().traits)); + BindOnce(&VerifyTaskEnvironment, GetParam().traits, + GetParam().pool_type)); } factory.WaitForAllTasksToRun(); @@ -327,7 +373,7 @@ scheduler_.PostDelayedTaskWithTraits( FROM_HERE, GetParam().traits, BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits, - Unretained(&task_running)), + GetParam().pool_type, Unretained(&task_running)), TimeDelta()); // Wait a little bit to make sure that the task doesn't run before Start(). @@ -348,6 +394,7 @@ scheduler_.PostDelayedTaskWithTraits( FROM_HERE, GetParam().traits, BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits, + GetParam().pool_type, TimeTicks::Now() + TestTimeouts::tiny_timeout(), Unretained(&task_running)), TestTimeouts::tiny_timeout()); @@ -370,7 +417,7 @@ CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits, GetParam().execution_mode) ->PostTask(FROM_HERE, BindOnce(&VerifyTaskEnvironmentAndSignalEvent, - GetParam().traits, + GetParam().traits, GetParam().pool_type, Unretained(&task_running))); // Wait a little bit to make sure that the task doesn't run before Start(). @@ -400,7 +447,7 @@ BindOnce(&VerifyTaskEnvironmentAndSignalEvent, TaskTraits::Override(GetParam().traits, {TaskPriority::USER_BLOCKING}), - Unretained(&task_running))); + GetParam().pool_type, Unretained(&task_running))); task_running.Wait(); } @@ -418,7 +465,7 @@ BindOnce(&VerifyTaskEnvironmentAndSignalEvent, TaskTraits::Override(GetParam().traits, {TaskPriority::USER_BLOCKING}), - Unretained(&task_running)), + GetParam().pool_type, Unretained(&task_running)), TimeDelta()); task_running.Wait(); } @@ -455,12 +502,12 @@ // TaskTraits and ExecutionModes. Verifies that each Task runs on a thread with // the expected priority and I/O restrictions and respects the characteristics // of its ExecutionMode. -TEST_F(TaskSchedulerImplTest, MultipleTaskSchedulerImplTestParams) { +TEST_P(TaskSchedulerImplTest, MultipleTaskSchedulerImplTestParams) { StartTaskScheduler(); std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks; for (const auto& test_params : GetTaskSchedulerImplTestParams()) { threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>( - &scheduler_, test_params.traits, + &scheduler_, test_params.traits, GetParam().pool_type, test_params.execution_mode)); threads_posting_tasks.back()->Start(); } @@ -471,10 +518,15 @@ } } -TEST_F(TaskSchedulerImplTest, +TEST_P(TaskSchedulerImplTest, GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) { StartTaskScheduler(); +#if defined(OS_WIN) || defined(OS_MACOSX) + if (GetParam().pool_type == test::PoolType::NATIVE) + return; +#endif + // GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() does not support // TaskPriority::BEST_EFFORT. testing::GTEST_FLAG(death_test_style) = "threadsafe"; @@ -499,7 +551,7 @@ // Verify that the RunsTasksInCurrentSequence() method of a SequencedTaskRunner // returns false when called from a task that isn't part of the sequence. -TEST_F(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) { +TEST_P(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) { StartTaskScheduler(); auto single_thread_task_runner = scheduler_.CreateSingleThreadTaskRunnerWithTraits( @@ -523,7 +575,7 @@ // Verify that the RunsTasksInCurrentSequence() method of a // SingleThreadTaskRunner returns false when called from a task that isn't part // of the sequence. -TEST_F(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) { +TEST_P(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) { StartTaskScheduler(); auto sequenced_task_runner = scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits()); @@ -546,7 +598,7 @@ } #if defined(OS_WIN) -TEST_F(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) { +TEST_P(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) { StartTaskScheduler(); auto com_sta_task_runner = scheduler_.CreateCOMSTATaskRunnerWithTraits( TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED); @@ -563,7 +615,7 @@ } #endif // defined(OS_WIN) -TEST_F(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) { +TEST_P(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) { StartTaskScheduler(); // As with delayed tasks in general, this is racy. If the task does happen to // run after Shutdown within the timeout, it will fail this test. @@ -586,7 +638,7 @@ #if defined(OS_POSIX) -TEST_F(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) { +TEST_P(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) { StartTaskScheduler(); int pipes[2]; @@ -633,7 +685,7 @@ // Verify that tasks posted on the same sequence access the same values on // SequenceLocalStorage, and tasks on different sequences see different values. -TEST_F(TaskSchedulerImplTest, SequenceLocalStorage) { +TEST_P(TaskSchedulerImplTest, SequenceLocalStorage) { StartTaskScheduler(); SequenceLocalStorageSlot<int> slot; @@ -664,7 +716,7 @@ scheduler_.FlushForTesting(); } -TEST_F(TaskSchedulerImplTest, FlushAsyncNoTasks) { +TEST_P(TaskSchedulerImplTest, FlushAsyncNoTasks) { StartTaskScheduler(); bool called_back = false; scheduler_.FlushAsyncForTesting( @@ -708,7 +760,7 @@ // Integration test that verifies that workers have a frame on their stacks // which easily identifies the type of worker and shutdown behavior (useful to // diagnose issues from logs without memory dumps). -TEST_F(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) { +TEST_P(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) { StartTaskScheduler(); // Shutdown behaviors and expected stack frames. @@ -788,7 +840,18 @@ scheduler_.FlushForTesting(); } -TEST_F(TaskSchedulerImplTest, SchedulerWorkerObserver) { +TEST_P(TaskSchedulerImplTest, SchedulerWorkerObserver) { +#if defined(OS_WIN) || defined(OS_MACOSX) + // SchedulerWorkers are not created (and hence not observed) when using the + // native thread pools. We still start the TaskScheduler in this case since + // JoinForTesting is always called on TearDown, and DCHECKs that all worker + // pools are started. + if (GetParam().pool_type == test::PoolType::NATIVE) { + StartTaskScheduler(); + return; + } +#endif + testing::StrictMock<test::MockSchedulerWorkerObserver> observer; set_scheduler_worker_observer(&observer); @@ -922,7 +985,8 @@ EXPECT_TRUE(was_destroyed); } -class TaskSchedulerPriorityUpdateTest : public testing::Test { +class TaskSchedulerPriorityUpdateTest + : public testing::TestWithParam<TaskSchedulerImplTestParams> { protected: struct PoolBlockingEvents { PoolBlockingEvents(const TaskTraits& pool_traits) @@ -1002,6 +1066,10 @@ }; // Update the priority of a sequence when it is not scheduled. +// +// TODO(adityakeerthi): Parameterize this test once we have a way to prevent +// sequences from being scheduled without flooding the pool. It is not possible +// to flood the native pools. TEST_F(TaskSchedulerPriorityUpdateTest, UpdatePrioritySequenceNotScheduled) { StartTaskSchedulerWithNumThreadsPerPool(1); @@ -1039,6 +1107,7 @@ FROM_HERE, BindOnce(&VerifyOrderAndTaskEnvironmentAndSignalEvent, task_runner_and_events->updated_priority, + test::PoolType::GENERIC, Unretained(task_runner_and_events->expected_previous_event), Unretained(&task_runner_and_events->task_ran))); } @@ -1063,7 +1132,16 @@ // Update the priority of a sequence when it is scheduled, i.e. not currently // in a priority queue. -TEST_F(TaskSchedulerPriorityUpdateTest, UpdatePrioritySequenceScheduled) { +TEST_P(TaskSchedulerPriorityUpdateTest, UpdatePrioritySequenceScheduled) { +#if defined(OS_WIN) || defined(OS_MACOSX) + base::test::ScopedFeatureList feature_list; + if (GetParam().pool_type == test::PoolType::NATIVE) { + feature_list.InitWithFeatures({kUseNativeThreadPool}, {}); + } else { + feature_list.InitWithFeatures({}, {kUseNativeThreadPool}); + } +#endif + StartTaskSchedulerWithNumThreadsPerPool(5); CreateTaskRunnersAndEvents(); @@ -1095,6 +1173,7 @@ FROM_HERE, BindOnce(&VerifyOrderAndTaskEnvironmentAndSignalEvent, TaskTraits(task_runner_and_events->updated_priority), + GetParam().pool_type, Unretained(task_runner_and_events->expected_previous_event), Unretained(&task_runner_and_events->task_ran))); } @@ -1108,5 +1187,9 @@ } } +INSTANTIATE_TEST_SUITE_P(OneTaskSchedulerPriorityUpdateTestParams, + TaskSchedulerPriorityUpdateTest, + ::testing::ValuesIn(GetTaskSchedulerImplTestParams())); + } // namespace internal } // namespace base
diff --git a/base/task/task_scheduler/test_utils.h b/base/task/task_scheduler/test_utils.h index 9ce6abe..bd82a22 100644 --- a/base/task/task_scheduler/test_utils.h +++ b/base/task/task_scheduler/test_utils.h
@@ -15,6 +15,7 @@ #include "base/task/task_traits.h" #include "base/task_runner.h" #include "base/thread_annotations.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" namespace base { @@ -72,6 +73,15 @@ // TODO(etiennep): Migrate to TaskSourceExecutionMode. enum class ExecutionMode { PARALLEL, SEQUENCED, SINGLE_THREADED }; +// An enumeration of possible pool types. Used to parametrize relevant +// task_scheduler tests. +enum class PoolType { + GENERIC, +#if defined(OS_WIN) || defined(OS_MACOSX) + NATIVE, +#endif +}; + // Creates a Sequence with given |traits| and pushes |task| to it. If a // TaskRunner is associated with |task|, it should be be passed as |task_runner| // along with its |execution_mode|. Returns the created Sequence.
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java index 8776c56a..046e8e8 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java +++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -79,26 +79,22 @@ @Override public Application newApplication(ClassLoader cl, String className, Context context) throws ClassNotFoundException, IllegalAccessException, InstantiationException { - // The multidex support library doesn't currently support having the test apk be multidex - // as well as the under-test apk being multidex. If MultiDex.install() is called for both, - // then re-extraction is triggered every time due to the support library caching only a - // single timestamp & crc. - // - // Attempt to install test apk multidex only if the apk-under-test is not multidex. - // It will likely continue to be true that the two are mutually exclusive because: - // * ProGuard enabled => - // Under-test apk is single dex. - // Test apk duplicates under-test classes, so may need multidex. - // * ProGuard disabled => - // Under-test apk might be multidex - // Test apk does not duplicate classes, so does not need multidex. - // When there is no under-test apk, then Application.onCreate() should trigger multidex - // installation. - // https://crbug.com/824523 - if (!BuildConfig.IS_MULTIDEX_ENABLED) { - ChromiumMultiDexInstaller.install(new BaseChromiumRunnerCommon.MultiDexContextWrapper( - getContext(), getTargetContext())); - BaseChromiumRunnerCommon.reorderDexPathElements(cl, getContext(), getTargetContext()); + boolean hasUnderTestApk = + !getContext().getPackageName().equals(getTargetContext().getPackageName()); + // When there is an under-test APK, BuildConfig belongs to it and does not indicate whether + // the test apk is multidex. In this case, just assume it is. + boolean isTestMultidex = hasUnderTestApk || BuildConfig.IS_MULTIDEX_ENABLED; + if (isTestMultidex) { + if (hasUnderTestApk) { + // Need hacks to have multidex work when there is an under-test apk :(. + ChromiumMultiDexInstaller.install( + new BaseChromiumRunnerCommon.MultiDexContextWrapper( + getContext(), getTargetContext())); + BaseChromiumRunnerCommon.reorderDexPathElements( + cl, getContext(), getTargetContext()); + } else { + ChromiumMultiDexInstaller.install(getContext()); + } } return super.newApplication(cl, className, context); }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java index e5eb2731..31ae7dd 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java +++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java
@@ -35,7 +35,7 @@ */ @MainDex static class MultiDexContextWrapper extends ContextWrapper { - private Context mAppContext; + private final Context mAppContext; MultiDexContextWrapper(Context instrContext, Context appContext) { super(instrContext); @@ -49,7 +49,8 @@ @Override public SharedPreferences getSharedPreferences(String name, int mode) { - return mAppContext.getSharedPreferences(name, mode); + // Prefix so as to not conflict with main app's multidex prefs file. + return mAppContext.getSharedPreferences("test-" + name, mode); } @Override
diff --git a/base/win/scoped_hstring.cc b/base/win/scoped_hstring.cc index d4c01f24..7a046c2 100644 --- a/base/win/scoped_hstring.cc +++ b/base/win/scoped_hstring.cc
@@ -7,6 +7,7 @@ #include <winstring.h> #include "base/numerics/safe_conversions.h" +#include "base/process/memory.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" @@ -83,6 +84,10 @@ namespace win { +ScopedHString::ScopedHString(HSTRING hstr) : ScopedGeneric(hstr) { + DCHECK(g_load_succeeded); +} + // static ScopedHString ScopedHString::Create(WStringPiece str) { DCHECK(g_load_succeeded); @@ -91,18 +96,20 @@ str.data(), checked_cast<UINT32>(str.length()), &hstr); if (SUCCEEDED(hr)) return ScopedHString(hstr); + if (hr == E_OUTOFMEMORY) { + // This size is an approximation. The actual size likely includes + // sizeof(HSTRING_HEADER) as well. + base::TerminateBecauseOutOfMemory((str.length() + 1) * sizeof(wchar_t)); + } DLOG(ERROR) << "Failed to create HSTRING" << std::hex << hr; return ScopedHString(nullptr); } +// static ScopedHString ScopedHString::Create(StringPiece str) { return Create(UTF8ToWide(str)); } -ScopedHString::ScopedHString(HSTRING hstr) : ScopedGeneric(hstr) { - DCHECK(g_load_succeeded); -} - // static bool ScopedHString::ResolveCoreWinRTStringDelayload() { // TODO(finnur): Add AssertIOAllowed once crbug.com/770193 is fixed.
diff --git a/base/win/scoped_hstring.h b/base/win/scoped_hstring.h index 14ec0f2d..4635b87 100644 --- a/base/win/scoped_hstring.h +++ b/base/win/scoped_hstring.h
@@ -63,7 +63,12 @@ // Loads all required HSTRING functions, available from Win8 and onwards. static bool ResolveCoreWinRTStringDelayload(); + // Returns a view into the memory buffer managed by the instance. The returned + // StringPiece is only valid during the lifetime of this ScopedHString + // instance. WStringPiece Get() const; + + // Returns a copy of the instance as a UTF-8 string. std::string GetAsUTF8() const; };
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index ec273c63c..72bcec92 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -939,6 +939,11 @@ help='Dump the Markdown .build_config format documentation ' 'then exit immediately.') + parser.add_option( + '--base-module-build-config', + help='Path to the base module\'s build config ' + 'if this is a feature module.') + options, args = parser.parse_args(argv) if args: @@ -1049,6 +1054,11 @@ all_resources_deps = deps.All('android_resources') all_classpath_library_deps = classpath_deps.All('java_library') + base_module_build_config = None + if options.base_module_build_config: + with open(options.base_module_build_config, 'r') as f: + base_module_build_config = json.load(f) + # Initialize some common config. # Any value that needs to be queryable by dependents must go within deps_info. config = { @@ -1233,16 +1243,36 @@ 'android_resources', 'android_apk', 'junit_binary', 'resource_rewriter', 'dist_aar', 'android_app_bundle_module'): config['resources'] = {} - config['resources']['dependency_zips'] = [ - c['resources_zip'] for c in all_resources_deps] + + dependency_zips = [ + c['resources_zip'] for c in all_resources_deps if c['resources_zip'] + ] extra_package_names = [] extra_r_text_files = [] + if options.type != 'android_resources': extra_package_names = [ c['package_name'] for c in all_resources_deps if 'package_name' in c] extra_r_text_files = [ c['r_text'] for c in all_resources_deps if 'r_text' in c] + # For feature modules, remove any resources that already exist in the base + # module. + if base_module_build_config: + dependency_zips = [ + c for c in dependency_zips + if c not in base_module_build_config['resources']['dependency_zips'] + ] + extra_package_names = [ + c for c in extra_package_names if c not in + base_module_build_config['resources']['extra_package_names'] + ] + extra_r_text_files = [ + c for c in extra_r_text_files if c not in + base_module_build_config['resources']['extra_r_text_files'] + ] + + config['resources']['dependency_zips'] = dependency_zips config['resources']['extra_package_names'] = extra_package_names config['resources']['extra_r_text_files'] = extra_r_text_files
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 4a93841..d8701eb 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -143,6 +143,9 @@ if (defined(invoker.android_manifest_dep)) { deps += [ invoker.android_manifest_dep ] } + if (defined(invoker.base_module_build_config_target)) { + deps += [ invoker.base_module_build_config_target ] + } script = "//build/android/gyp/write_build_config.py" depfile = "$target_gen_dir/$target_name.d" @@ -477,6 +480,12 @@ invoker.main_class, ] } + if (defined(invoker.base_module_build_config)) { + _rebased_base_module_build_config = + rebase_path(invoker.base_module_build_config, root_build_dir) + args += + [ "--base-module-build-config=$_rebased_base_module_build_config" ] + } if (current_toolchain != default_toolchain) { # This has to be a built-time error rather than a GN assert because many # packages have a mix of java and non-java targets. For example, the @@ -3346,8 +3355,10 @@ if (type == "android_app_bundle_module") { forward_variables_from(invoker, [ - "proto_resources_path", + "base_module_build_config", + "base_module_build_config_target", "module_rtxt_path", + "proto_resources_path", ]) } build_config = _build_config
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 0334c9e..dc13739 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -2587,6 +2587,8 @@ "android_manifest", "android_manifest_dep", "apk_under_test", + "base_module_build_config", + "base_module_build_config_target", "chromium_code", "classpath_deps", "emma_never_instrument", @@ -3294,10 +3296,15 @@ _base_module_target_gen_dir = get_label_info(_base_module_target, "target_gen_dir") _base_module_target_name = get_label_info(_base_module_target, "name") + base_module_arsc_resource = "$_base_module_target_gen_dir/$_base_module_target_name.arsc.ap_" base_module_arsc_resource_target = "${_base_module_target}__compile_arsc_resources" + base_module_build_config = "$_base_module_target_gen_dir/" + + "$_base_module_target_name.build_config" + base_module_build_config_target = + "${_base_module_target}$build_config_target_suffix" } } }
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy index db77ddf..dd660a3 100644 --- a/build/config/fuchsia/testing_sandbox_policy +++ b/build/config/fuchsia/testing_sandbox_policy
@@ -15,6 +15,7 @@ "fuchsia.process.Launcher", "fuchsia.sys.Environment", "fuchsia.sys.Launcher", + "fuchsia.sys.Loader", "fuchsia.ui.input.ImeService", "fuchsia.ui.input.ImeVisibilityService", "fuchsia.ui.policy.Presenter",
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn index 33abde9..1555e59 100644 --- a/build/config/win/BUILD.gn +++ b/build/config/win/BUILD.gn
@@ -281,11 +281,20 @@ } } -# Sets the default Windows build version. This is separated because some -# targets need to manually override it for their compiles. +# Chromium supports running on Windows 7, but if these constants are set to +# Windows 7, then newer APIs aren't made available by the Windows SDK. +# So we set this to Windows 10 and then are careful to check at runtime +# to only call newer APIs when they're available. +# Some third-party libraries assume that these defines set what version of +# Windows is available at runtime. Targets using these libraries need to +# manually override this config for their compiles. config("winver") { defines = [ - "NTDDI_VERSION=0x0A000003", # NTDDI_WIN10_RS2 + "NTDDI_VERSION=NTDDI_WIN10_RS2", + + # We can't say `=_WIN32_WINNT_WIN10` here because some files do + # `#if WINVER < 0x0600` without including windows.h before, + # and then _WIN32_WINNT_WIN10 isn't yet known to be 0x0A00. "_WIN32_WINNT=0x0A00", "WINVER=0x0A00", ]
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc index 1d79404..da31320 100644 --- a/cc/paint/paint_image.cc +++ b/cc/paint/paint_image.cc
@@ -152,6 +152,13 @@ } } +bool PaintImage::IsEligibleForAcceleratedDecoding() const { + if (!CanDecodeFromGenerator()) + return false; + DCHECK(paint_image_generator_); + return paint_image_generator_->IsEligibleForAcceleratedDecoding(); +} + SkISize PaintImage::GetSupportedDecodeSize( const SkISize& requested_size) const { // TODO(vmpstr): In some cases we do not support decoding to any other
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h index 33202446..2f68f070 100644 --- a/cc/paint/paint_image.h +++ b/cc/paint/paint_image.h
@@ -140,6 +140,18 @@ bool operator==(const PaintImage& other) const; bool operator!=(const PaintImage& other) const { return !(*this == other); } + // Returns true if the image is eligible for decoding using a hardware + // accelerator (which would require at least that all the encoded data has + // been received). Returns false otherwise or if the image cannot be decoded + // from a PaintImageGenerator. Notice that a return value of true does not + // guarantee that the hardware accelerator supports the image. It only + // indicates that the software decoder hasn't done any work with the image, so + // sending it to a hardware decoder is appropriate. + // + // TODO(andrescj): consider supporting the non-PaintImageGenerator path which + // is expected to be rare. + bool IsEligibleForAcceleratedDecoding() const; + // Returns the smallest size that is at least as big as the requested_size // such that we can decode to exactly that scale. If the requested size is // larger than the image, this returns the image size. Any returned value is
diff --git a/cc/paint/paint_image_generator.h b/cc/paint/paint_image_generator.h index db33427..5e4f474 100644 --- a/cc/paint/paint_image_generator.h +++ b/cc/paint/paint_image_generator.h
@@ -29,6 +29,14 @@ PaintImageGenerator& operator=(const PaintImageGenerator&) = delete; + // Returns true if we can guarantee that the software decoder hasn't done work + // on the image, so it's appropriate to send the encoded image to a hardware + // accelerator. False if we can't guarantee this or if not applicable. For + // example, if the encoded data comes incrementally, and the software decoder + // starts working with partial data, the image shouldn't later be sent to a + // hardware decoder. + virtual bool IsEligibleForAcceleratedDecoding() const = 0; + // Returns a reference to the encoded content of this image. virtual sk_sp<SkData> GetEncodedData() const = 0;
diff --git a/cc/test/fake_paint_image_generator.cc b/cc/test/fake_paint_image_generator.cc index c0bdfbb..f26443e5 100644 --- a/cc/test/fake_paint_image_generator.cc +++ b/cc/test/fake_paint_image_generator.cc
@@ -36,6 +36,10 @@ FakePaintImageGenerator::~FakePaintImageGenerator() = default; +bool FakePaintImageGenerator::IsEligibleForAcceleratedDecoding() const { + return false; +} + sk_sp<SkData> FakePaintImageGenerator::GetEncodedData() const { return nullptr; }
diff --git a/cc/test/fake_paint_image_generator.h b/cc/test/fake_paint_image_generator.h index 8a18535..268c89e 100644 --- a/cc/test/fake_paint_image_generator.h +++ b/cc/test/fake_paint_image_generator.h
@@ -33,6 +33,7 @@ FakePaintImageGenerator& operator=(const FakePaintImageGenerator&) = delete; // PaintImageGenerator implementation. + bool IsEligibleForAcceleratedDecoding() const override; sk_sp<SkData> GetEncodedData() const override; bool GetPixels(const SkImageInfo& info, void* pixels,
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index 3a0eb8a..60f4c43 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc
@@ -2324,10 +2324,18 @@ void PropertyTrees::ResetCachedData() { cached_data_.transform_tree_update_number = 0; - cached_data_.animation_scales = std::vector<AnimationScaleData>( - transform_tree.nodes().size(), AnimationScaleData()); - cached_data_.draw_transforms = std::vector<std::vector<DrawTransformData>>( - transform_tree.nodes().size(), std::vector<DrawTransformData>(1)); + const auto transform_count = transform_tree.nodes().size(); + cached_data_.animation_scales.resize(transform_count); + for (auto& animation_scale : cached_data_.animation_scales) + animation_scale.update_number = -1; + + cached_data_.draw_transforms.resize(transform_count, + std::vector<DrawTransformData>(1)); + for (auto& draw_transforms_for_id : cached_data_.draw_transforms) { + draw_transforms_for_id.resize(1); + draw_transforms_for_id[0].update_number = -1; + draw_transforms_for_id[0].target_id = EffectTree::kInvalidNodeId; + } } void PropertyTrees::UpdateTransformTreeUpdateNumber() {
diff --git a/cc/trees/scroll_node.cc b/cc/trees/scroll_node.cc index 069c731..69b7b33 100644 --- a/cc/trees/scroll_node.cc +++ b/cc/trees/scroll_node.cc
@@ -63,6 +63,9 @@ value->SetBoolean("user_scrollable_horizontal", user_scrollable_horizontal); value->SetBoolean("user_scrollable_vertical", user_scrollable_vertical); + value->SetBoolean("scrolls_inner_viewport", scrolls_inner_viewport); + value->SetBoolean("scrolls_outer_viewport", scrolls_outer_viewport); + element_id.AddToTracedValue(value); value->SetInteger("transform_id", transform_id); value->SetInteger("overscroll_behavior_x", overscroll_behavior.x);
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 4d276b2f..79a8268b 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -96,7 +96,10 @@ "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryBridge.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMetricsRecorder.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java", + "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManagerImpl.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java", + "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponent.java", + "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponentFactory.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingProperties.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java index a397584..1459e80 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -89,6 +89,7 @@ /** Detaches and destroys the view. */ public void destroy() { + setVisible(false); detachAssistantView(); mOverlayCoordinator.destroy(); }
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn index b22fce0..8e21108 100644 --- a/chrome/android/features/tab_ui/BUILD.gn +++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -5,6 +5,14 @@ import("//build/config/android/rules.gni") import("//chrome/common/features.gni") +android_resources("java_resources") { + resource_dirs = [ "java/res" ] + deps = [ + "//chrome/android:chrome_app_java_resources", + ] + custom_package = "org.chromium.chrome.tab_ui" +} + android_library("java") { java_files = [ "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java", @@ -38,6 +46,10 @@ "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java", ] + deps = [ + ":java_resources", + ] + classpath_deps = [ "//base:base_java", "//chrome/android:chrome_java",
diff --git a/chrome/android/java/res/drawable-hdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-hdpi/tabstrip_selected.png similarity index 100% rename from chrome/android/java/res/drawable-hdpi/tabstrip_selected.png rename to chrome/android/features/tab_ui/java/res/drawable-hdpi/tabstrip_selected.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-mdpi/tabstrip_selected.png similarity index 100% rename from chrome/android/java/res/drawable-mdpi/tabstrip_selected.png rename to chrome/android/features/tab_ui/java/res/drawable-mdpi/tabstrip_selected.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xhdpi/tabstrip_selected.png similarity index 100% rename from chrome/android/java/res/drawable-xhdpi/tabstrip_selected.png rename to chrome/android/features/tab_ui/java/res/drawable-xhdpi/tabstrip_selected.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xxhdpi/tabstrip_selected.png similarity index 100% rename from chrome/android/java/res/drawable-xxhdpi/tabstrip_selected.png rename to chrome/android/features/tab_ui/java/res/drawable-xxhdpi/tabstrip_selected.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/tabstrip_selected.png b/chrome/android/features/tab_ui/java/res/drawable-xxxhdpi/tabstrip_selected.png similarity index 100% rename from chrome/android/java/res/drawable-xxxhdpi/tabstrip_selected.png rename to chrome/android/features/tab_ui/java/res/drawable-xxxhdpi/tabstrip_selected.png Binary files differ
diff --git a/chrome/android/java/res/drawable/selected_tab_background.xml b/chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml similarity index 100% rename from chrome/android/java/res/drawable/selected_tab_background.xml rename to chrome/android/features/tab_ui/java/res/drawable/selected_tab_background.xml
diff --git a/chrome/android/java/res/drawable/tab_grid_card_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml similarity index 100% rename from chrome/android/java/res/drawable/tab_grid_card_background.xml rename to chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background.xml
diff --git a/chrome/android/java/res/drawable/tabstrip_favicon_background.xml b/chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml similarity index 100% rename from chrome/android/java/res/drawable/tabstrip_favicon_background.xml rename to chrome/android/features/tab_ui/java/res/drawable/tabstrip_favicon_background.xml
diff --git a/chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml similarity index 100% rename from chrome/android/java/res/layout/bottom_tab_grid_toolbar.xml rename to chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
diff --git a/chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml similarity index 100% rename from chrome/android/java/res/layout/bottom_tab_strip_toolbar.xml rename to chrome/android/features/tab_ui/java/res/layout/bottom_tab_strip_toolbar.xml
diff --git a/chrome/android/java/res/layout/tab_grid_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml similarity index 100% rename from chrome/android/java/res/layout/tab_grid_card_item.xml rename to chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
diff --git a/chrome/android/java/res/layout/tab_list_recycler_view_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tab_list_recycler_view_layout.xml similarity index 100% rename from chrome/android/java/res/layout/tab_list_recycler_view_layout.xml rename to chrome/android/features/tab_ui/java/res/layout/tab_list_recycler_view_layout.xml
diff --git a/chrome/android/java/res/layout/tab_strip_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_strip_item.xml similarity index 100% rename from chrome/android/java/res/layout/tab_strip_item.xml rename to chrome/android/features/tab_ui/java/res/layout/tab_strip_item.xml
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml new file mode 100644 index 0000000..33c74152 --- /dev/null +++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-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. --> +<resources xmlns:tools="http://schemas.android.com/tools"> + <dimen name="tab_grid_favicon_size">32dp</dimen> + <dimen name="tab_list_selected_inset">7dp</dimen> + <dimen name="tab_list_card_padding">8dp</dimen> + <dimen name="tab_list_mini_card_radius">4dp</dimen> + <dimen name="tab_list_mini_card_frame_size">1dp</dimen> + <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen> + <dimen name="swipe_to_dismiss_threshold">72dp</dimen> +</resources>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java index 93f697bc..4e60f32 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -20,10 +20,10 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.task.AsyncTask; import org.chromium.base.task.PostTask; -import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.tab_ui.R; import org.chromium.content_public.browser.UiThreadTaskTraits; import java.io.File; @@ -170,21 +170,21 @@ // Initialize Paints to use. mEmptyThumbnailPaint = new Paint(); mEmptyThumbnailPaint.setStyle(Paint.Style.FILL); - mEmptyThumbnailPaint.setColor( - ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_100)); + mEmptyThumbnailPaint.setColor(ApiCompatibilityUtils.getColor( + context.getResources(), org.chromium.chrome.R.color.modern_grey_100)); mEmptyThumbnailPaint.setAntiAlias(true); mThumbnailFramePaint = new Paint(); mThumbnailFramePaint.setStyle(Paint.Style.STROKE); mThumbnailFramePaint.setStrokeWidth( context.getResources().getDimension(R.dimen.tab_list_mini_card_frame_size)); - mThumbnailFramePaint.setColor( - ApiCompatibilityUtils.getColor(context.getResources(), R.color.modern_grey_300)); + mThumbnailFramePaint.setColor(ApiCompatibilityUtils.getColor( + context.getResources(), org.chromium.chrome.R.color.modern_grey_300)); mThumbnailFramePaint.setAntiAlias(true); mTextPaint = new Paint(); - mTextPaint.setTextSize( - context.getResources().getDimension(R.dimen.compositor_tab_title_text_size)); + mTextPaint.setTextSize(context.getResources().getDimension( + org.chromium.chrome.R.dimen.compositor_tab_title_text_size)); mTextPaint.setFakeBoldText(true); mTextPaint.setAntiAlias(true); mTextPaint.setTextAlign(Paint.Align.CENTER);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java index bde5673..0035890 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/SilenceLintErrors.java
@@ -11,24 +11,21 @@ */ /* package */ class SilenceLintErrors { // TODO(yusufo): Add these resources to the DFM - private int[] mRes = - new int[] {R.dimen.tab_grid_favicon_size, R.string.tab_management_module_title, - R.string.iph_tab_groups_quickly_compare_pages_text, - R.string.iph_tab_groups_tap_to_see_another_tab_text, - R.string.iph_tab_groups_your_tabs_together_text, - R.string.bottom_tab_grid_description, R.string.bottom_tab_grid_opened_half, - R.string.bottom_tab_grid_opened_full, R.string.bottom_tab_grid_closed, - R.dimen.tab_list_selected_inset, R.layout.tab_strip_item, - R.drawable.selected_tab_background, R.drawable.tab_grid_card_background, - R.layout.tab_grid_card_item, R.layout.tab_list_recycler_view_layout, - R.layout.bottom_tab_grid_toolbar, R.string.bottom_tab_grid_new_tab, - R.string.bottom_tab_grid_new_tab, R.plurals.bottom_tab_grid_title_placeholder, - R.string.iph_tab_groups_tap_to_see_another_tab_accessibility_text, - R.string.accessibility_bottom_tab_strip_expand_tab_sheet, - R.layout.bottom_tab_strip_toolbar, R.drawable.tabstrip_selected, - R.dimen.tab_list_card_padding, R.dimen.tab_list_mini_card_text_size, - R.dimen.tab_list_mini_card_frame_size, R.dimen.tab_list_mini_card_radius, - R.drawable.tabstrip_favicon_background, R.dimen.swipe_to_dismiss_threshold}; + private int[] mRes = new int[] { + R.string.iph_tab_groups_quickly_compare_pages_text, + R.string.iph_tab_groups_tap_to_see_another_tab_text, + R.string.iph_tab_groups_your_tabs_together_text, + R.string.bottom_tab_grid_description, + R.string.bottom_tab_grid_opened_half, + R.string.bottom_tab_grid_opened_full, + R.string.bottom_tab_grid_closed, + R.string.bottom_tab_grid_new_tab, + R.string.bottom_tab_grid_new_tab, + R.plurals.bottom_tab_grid_title_placeholder, + R.string.iph_tab_groups_tap_to_see_another_tab_accessibility_text, + R.string.accessibility_bottom_tab_strip_expand_tab_sheet, + R.string.accessibility_bottom_tab_grid_close_tab_sheet, + }; private SilenceLintErrors() {} }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java index 6548ef3..557c080 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
@@ -9,8 +9,8 @@ import android.view.View; import android.view.ViewGroup; -import org.chromium.chrome.R; import org.chromium.chrome.browser.lifecycle.Destroyable; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java index c73373d..e381f5d 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -15,7 +15,7 @@ import android.widget.FrameLayout; import org.chromium.base.Callback; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; @@ -41,7 +41,7 @@ String title = item.get(TabProperties.TITLE); holder.title.setText(title); holder.closeButton.setContentDescription(holder.itemView.getResources().getString( - R.string.accessibility_tabstrip_btn_close_tab, title)); + org.chromium.chrome.R.string.accessibility_tabstrip_btn_close_tab, title)); } else if (TabProperties.IS_SELECTED == propertyKey) { Resources res = holder.itemView.getResources(); Drawable drawable = new InsetDrawable(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java index b92ca55e..1641d6a 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewHolder.java
@@ -13,7 +13,7 @@ import android.widget.TextView; import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.widget.ButtonCompat; /** @@ -35,14 +35,14 @@ this.favicon = itemView.findViewById(R.id.tab_favicon); this.closeButton = itemView.findViewById(R.id.close_button); DrawableCompat.setTint(this.closeButton.getDrawable(), - ApiCompatibilityUtils.getColor(itemView.getResources(), R.color.light_icon_color)); + ApiCompatibilityUtils.getColor( + itemView.getResources(), org.chromium.chrome.R.color.light_icon_color)); this.createGroupButton = itemView.findViewById(R.id.create_group_button); } public static TabGridViewHolder create(ViewGroup parent, int itemViewType) { - View view = - LayoutInflater.from(parent.getContext()) - .inflate(org.chromium.chrome.R.layout.tab_grid_card_item, parent, false); + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.tab_grid_card_item, parent, false); return new TabGridViewHolder(view); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java index 9ea56667..0e138cb6 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -13,7 +13,7 @@ import android.widget.FrameLayout; import android.widget.TextView; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.widget.ChromeImageView; /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java index aff7246b..bd0b2740 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -14,12 +14,12 @@ import android.view.LayoutInflater; import android.view.ViewGroup; -import org.chromium.chrome.R; import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tasks.tab_groups.TabGroupUtils; +import org.chromium.chrome.tab_ui.R; import org.chromium.components.feature_engagement.FeatureConstants; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java index a69b64a..f819ccd 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarCoordinator.java
@@ -9,8 +9,8 @@ import android.view.View; import android.view.ViewGroup; -import org.chromium.chrome.R; import org.chromium.chrome.browser.lifecycle.Destroyable; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java index a26896a..72c89ae 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
@@ -10,7 +10,7 @@ import android.support.v7.widget.RecyclerView.ViewHolder; import android.widget.FrameLayout; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; @@ -45,13 +45,13 @@ item.get(TabProperties.TAB_CLOSED_LISTENER).run(holder.getTabId()); }); holder.button.setContentDescription(holder.itemView.getContext().getString( - R.string.accessibility_tabstrip_btn_close_tab, title)); + org.chromium.chrome.R.string.accessibility_tabstrip_btn_close_tab, title)); } else { holder.button.setOnClickListener(view -> { item.get(TabProperties.TAB_SELECTED_LISTENER).run(holder.getTabId()); }); holder.button.setContentDescription(holder.itemView.getContext().getString( - R.string.accessibility_tabstrip_tab, title)); + org.chromium.chrome.R.string.accessibility_tabstrip_tab, title)); } } else if (TabProperties.FAVICON == propertyKey) { Drawable faviconDrawable = item.get(TabProperties.FAVICON);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java index d199c08..0a1c9550 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java
@@ -10,7 +10,7 @@ import android.view.ViewGroup; import android.widget.ImageButton; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; /** * {@link RecyclerView.ViewHolder} for tab strip.
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java index 89644e5..5375c3c 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinderTest.java
@@ -21,7 +21,7 @@ import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.test.util.CallbackHelper; -import org.chromium.chrome.R; +import org.chromium.chrome.tab_ui.R; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ui.DummyUiActivity; import org.chromium.chrome.test.ui.DummyUiActivityTestCase; @@ -204,14 +204,14 @@ mContainerModel.set(TabListContainerProperties.IS_INCOGNITO, true); assertThat(mRecyclerView.getBackground(), instanceOf(ColorDrawable.class)); assertThat(((ColorDrawable) mRecyclerView.getBackground()).getColor(), - equalTo(ApiCompatibilityUtils.getColor( - mRecyclerView.getResources(), R.color.incognito_modern_primary_color))); + equalTo(ApiCompatibilityUtils.getColor(mRecyclerView.getResources(), + org.chromium.chrome.R.color.incognito_modern_primary_color))); mContainerModel.set(TabListContainerProperties.IS_INCOGNITO, false); assertThat(mRecyclerView.getBackground(), instanceOf(ColorDrawable.class)); assertThat(((ColorDrawable) mRecyclerView.getBackground()).getColor(), - equalTo(ApiCompatibilityUtils.getColor( - mRecyclerView.getResources(), R.color.modern_primary_color))); + equalTo(ApiCompatibilityUtils.getColor(mRecyclerView.getResources(), + org.chromium.chrome.R.color.modern_primary_color))); } @Test
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 1bc2dfac..c8c45972 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -594,14 +594,4 @@ <!-- Explicit Language Ask Prompt dimensions --> <dimen name="explicit_ask_checkbox_end_padding">4dp</dimen> - <!-- Tab List dimensions --> - <dimen name="tab_grid_favicon_size">32dp</dimen> - <dimen name="tab_list_selected_inset">7dp</dimen> - <dimen name="tab_list_card_padding">8dp</dimen> - <dimen name="tab_list_mini_card_radius">4dp</dimen> - <dimen name="tab_list_mini_card_frame_size">1dp</dimen> - <dimen name="tab_list_mini_card_text_size">12sp</dimen> - <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen> - <dimen name="swipe_to_dismiss_threshold">72dp</dimen> - </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index befd101..52902964 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -62,7 +62,8 @@ import org.chromium.chrome.browser.appmenu.AppMenuHandler; import org.chromium.chrome.browser.appmenu.AppMenuObserver; import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; -import org.chromium.chrome.browser.autofill.keyboard_accessory.ManualFillingCoordinator; +import org.chromium.chrome.browser.autofill.keyboard_accessory.ManualFillingComponent; +import org.chromium.chrome.browser.autofill.keyboard_accessory.ManualFillingComponentFactory; import org.chromium.chrome.browser.banners.AppBannerManager; import org.chromium.chrome.browser.bookmarks.BookmarkModel; import org.chromium.chrome.browser.bookmarks.BookmarkUtils; @@ -298,8 +299,8 @@ private Runnable mRecordMultiWindowModeScreenWidthRunnable; private final DiscardableReferencePool mReferencePool = new DiscardableReferencePool(); - private final ManualFillingCoordinator mManualFillingController = - new ManualFillingCoordinator(); + private final ManualFillingComponent mManualFillingComponent = + ManualFillingComponentFactory.createComponent(); private AssistStatusHandler mAssistStatusHandler; @@ -497,7 +498,7 @@ ((BottomContainer) findViewById(R.id.bottom_container)) .initialize(mFullscreenManager, - mManualFillingController.getKeyboardExtensionSizeManager()); + mManualFillingComponent.getKeyboardExtensionSizeManager()); // If onStart was called before postLayoutInflation (because inflation was done in a // background thread) then make sure to call the relevant methods belatedly. @@ -779,10 +780,10 @@ } /** - * @return The ManualFillingCoordinator that belongs to this activity. + * @return The {@link ManualFillingComponent} that belongs to this activity. */ - public ManualFillingCoordinator getManualFillingController() { - return mManualFillingController; + public ManualFillingComponent getManualFillingComponent() { + return mManualFillingComponent; } /** @@ -1031,7 +1032,7 @@ arDelegate.registerOnResumeActivity(this); } - getManualFillingController().onResume(); + getManualFillingComponent().onResume(); } @Override @@ -1049,7 +1050,7 @@ RecordUserAction.record("MobileGoToBackground"); Tab tab = getActivityTab(); if (tab != null) getTabContentManager().cacheTabThumbnail(tab); - getManualFillingController().onPause(); + getManualFillingComponent().onPause(); VrModuleProvider.getDelegate().maybeUnregisterVrEntryHook(); markSessionEnd(); @@ -1362,7 +1363,7 @@ mTabContentManager = null; } - mManualFillingController.destroy(); + mManualFillingComponent.destroy(); if (mActivityTabStartupMetricsTracker != null) { mActivityTabStartupMetricsTracker.destroy(); @@ -1476,11 +1477,11 @@ } super.finishNativeInitialization(); - mManualFillingController.initialize(getWindowAndroid(), + mManualFillingComponent.initialize(getWindowAndroid(), findViewById(R.id.keyboard_accessory_stub), findViewById(R.id.keyboard_accessory_sheet_stub)); getCompositorViewHolder().setKeyboardExtensionView( - mManualFillingController.getKeyboardExtensionSizeManager()); + mManualFillingComponent.getKeyboardExtensionSizeManager()); // Create after native initialization so subclasses that override this method have a chance // to setup. @@ -1553,7 +1554,7 @@ @Override public boolean onOptionsItemSelected(MenuItem item) { if (item != null) { - if (mManualFillingController != null) mManualFillingController.dismiss(); + if (mManualFillingComponent != null) mManualFillingComponent.dismiss(); if (onMenuOrKeyboardAction(item.getItemId(), true)) return true; } return super.onOptionsItemSelected(item);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index 6046950..3d4601d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -231,7 +231,6 @@ public static final String DRAW_VERTICALLY_EDGE_TO_EDGE = "DrawVerticallyEdgeToEdge"; public static final String EPHEMERAL_TAB = "EphemeralTab"; public static final String EXPERIMENTAL_APP_BANNERS = "ExperimentalAppBanners"; - public static final String EXPERIMENTAL_UI = "ExperimentalUi"; public static final String EXPLICIT_LANGUAGE_ASK = "ExplicitLanguageAsk"; public static final String EXPLORE_SITES = "ExploreSites"; public static final String FCM_INVALIDATIONS = "FCMInvalidations";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeKeyboardVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeKeyboardVisibilityDelegate.java index bc6c3424..5ba2f8b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeKeyboardVisibilityDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeKeyboardVisibilityDelegate.java
@@ -59,8 +59,8 @@ boolean wasManualFillingViewShowing = false; if (activity != null) { wasManualFillingViewShowing = - activity.getManualFillingController().isFillingViewShown(view); - activity.getManualFillingController().hide(); + activity.getManualFillingComponent().isFillingViewShown(view); + activity.getManualFillingComponent().hide(); } return super.hideKeyboard(view) || wasManualFillingViewShowing; } @@ -70,6 +70,6 @@ ChromeActivity activity = getActivity(); return super.isKeyboardShowing(context, view) || (activity != null - && activity.getManualFillingController().isFillingViewShown(view)); + && activity.getManualFillingComponent().isFillingViewShown(view)); } } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 35542b15..5b80bcac 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -479,7 +479,6 @@ * * @param intent The intent to set the component for. * @param component The client generated component to be validated. - * @param currentActivity The activity triggering the intent. */ public static void setNonAliasedComponent(Intent intent, ComponentName component) { assert component != null; @@ -1928,7 +1927,7 @@ return getBottomSheet() != null && getBottomSheet().handleBackPress(); } - if (getManualFillingController().handleBackPress()) return true; + if (getManualFillingComponent().handleBackPress()) return true; final Tab currentTab = getActivityTab();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java index 9350e48..ed9b8a64 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/FontSizePrefs.java
@@ -48,6 +48,7 @@ private final ObserverList<FontSizePrefsObserver> mObserverList; private Float mSystemFontScaleForTests; + private boolean mTouchlessMode; /** * Interface for observing changes in font size-related preferences. @@ -154,6 +155,15 @@ } /** + * Enables touchless mode. This overrides user's preference and always enables force enable + * zoom. + */ + public void enableTouchlessMode() { + mTouchlessMode = true; + nativeSetForceEnableZoom(mFontSizePrefsAndroidPtr, true); + } + + /** * Returns whether forceEnableZoom is enabled. */ public boolean getForceEnableZoom() { @@ -174,6 +184,9 @@ } private void setForceEnableZoom(boolean enabled, boolean fromUser) { + // Force enable zoom is always enabled in touchless mode and it should not be changed. + if (mTouchlessMode) return; + SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit(); sharedPreferencesEditor.putBoolean(PREF_USER_SET_FORCE_ENABLE_ZOOM, fromUser); sharedPreferencesEditor.apply();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java index 0a8ae75a..48928f64 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -46,7 +46,7 @@ mAutofillPopup = new AutofillPopup(activity, anchorView, this); mContext = activity; ChromeActivity chromeActivity = (ChromeActivity) activity; - chromeActivity.getManualFillingController().notifyPopupAvailable(mAutofillPopup); + chromeActivity.getManualFillingComponent().notifyPopupAvailable(mAutofillPopup); mWebContentsAccessibility = WebContentsAccessibility.fromWebContents( chromeActivity.getCurrentWebContents()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryBridge.java index 62fe1a9..b8bd9ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryBridge.java
@@ -6,11 +6,9 @@ import android.content.Context; import android.content.DialogInterface; -import android.support.v7.app.AlertDialog; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; -import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ResourceId; import org.chromium.chrome.browser.autofill.keyboard_accessory.data.PropertyProvider; @@ -28,7 +26,7 @@ public class AutofillKeyboardAccessoryBridge implements AutofillDelegate, DialogInterface.OnClickListener { private long mNativeAutofillKeyboardAccessory; - private ManualFillingCoordinator mManualFillingCoordinator; + private ManualFillingComponent mManualFillingComponent; private Context mContext; private PropertyProvider<AutofillSuggestion[]> mChipProvider = new PropertyProvider<>(AccessoryAction.AUTOFILL_SUGGESTION); @@ -48,7 +46,7 @@ @Override public void suggestionSelected(int listIndex) { - mManualFillingCoordinator.dismiss(); + mManualFillingComponent.dismiss(); if (mNativeAutofillKeyboardAccessory == 0) return; nativeSuggestionSelected(mNativeAutofillKeyboardAccessory, listIndex); } @@ -86,8 +84,8 @@ mContext = windowAndroid.getActivity().get(); assert mContext != null; if (mContext instanceof ChromeActivity) { - mManualFillingCoordinator = ((ChromeActivity) mContext).getManualFillingController(); - mManualFillingCoordinator.registerAutofillProvider(mChipProvider, this); + mManualFillingComponent = ((ChromeActivity) mContext).getManualFillingComponent(); + mManualFillingComponent.registerAutofillProvider(mChipProvider, this); } mNativeAutofillKeyboardAccessory = nativeAutofillKeyboardAccessory; @@ -124,14 +122,9 @@ // eventually disappear). @CalledByNative - private void confirmDeletion(String title, String body) { - new AlertDialog.Builder(mContext, R.style.Theme_Chromium_AlertDialog) - .setTitle(title) - .setMessage(body) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, this) - .create() - .show(); + private void confirmDeletion(String title, String body) throws Exception { + // TODO(fhorschig): If deletion is implemented, build a ModalDialogView! + throw new Exception("Not implemented yet!"); } @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java index a9d2d08c..9850b8b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManager.java
@@ -6,23 +6,16 @@ import android.support.annotation.Px; -import org.chromium.base.ObserverList; - /** - * This class holds the size of any extension to or even replacement for a keyboard. - * For example, it is used by {@link ManualFillingCoordinator} to provide the combined height of - * {@link KeyboardAccessoryCoordinator} and {@link AccessorySheetCoordinator}. - * The height can then be used to either compute an offset for bottom bars (e.g. CCTs or PWAs) or to - * push up the content area. + * This class holds the size of any extension to or even replacement for a keyboard. The height can + * be used to either compute an offset for bottom bars (e.g. CCTs or PWAs) or to push up the content + * area. */ -public class KeyboardExtensionSizeManager { - private int mHeight; - private final ObserverList<Observer> mObservers = new ObserverList<>(); - +public interface KeyboardExtensionSizeManager { /** * Observers are notified when the size of the keyboard extension changes. */ - public interface Observer { + interface Observer { /** * Called when the extension height changes. * @param keyboardHeight The new height of the keyboard extension. @@ -34,34 +27,19 @@ * Returns the height of the keyboard extension. * @return A height in pixels. */ - public @Px int getKeyboardExtensionHeight() { - return mHeight; - } + @Px + int getKeyboardExtensionHeight(); /** - * Registered observers are called whenever the extension size changes until unregistered. Does - * not guarantee order. + * Registered observers are called whenever the extension size changes until unregistered. + * Does not guarantee order. * @param observer a {@link KeyboardExtensionSizeManager.Observer}. */ - public void addObserver(Observer observer) { - mObservers.addObserver(observer); - } + void addObserver(Observer observer); /** * Removes a registered observer if present. * @param observer a registered {@link KeyboardExtensionSizeManager.Observer}. */ - public void removeObserver(Observer observer) { - mObservers.removeObserver(observer); - } - - /** - * Sets a new extension height and notifies observers if its value changed. - * @param newKeyboardExtensionHeight The height in pixels. - */ - void setKeyboardExtensionHeight(@Px int newKeyboardExtensionHeight) { - if (mHeight == newKeyboardExtensionHeight) return; - mHeight = newKeyboardExtensionHeight; - for (Observer observer : mObservers) observer.onKeyboardExtensionHeightChanged(mHeight); - } + void removeObserver(Observer observer); } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManagerImpl.java new file mode 100644 index 0000000..86e9a18 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardExtensionSizeManagerImpl.java
@@ -0,0 +1,39 @@ +// 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. + +package org.chromium.chrome.browser.autofill.keyboard_accessory; + +import android.support.annotation.Px; + +import org.chromium.base.ObserverList; + +/** + * This class is used by {@link ManualFillingMediator} to provide the combined height of + * KeyboardAccessoryCoordinator and AccessorySheetCoordinator. + */ +class KeyboardExtensionSizeManagerImpl implements KeyboardExtensionSizeManager { + private int mHeight; + private final ObserverList<Observer> mObservers = new ObserverList<>(); + + @Override + public @Px int getKeyboardExtensionHeight() { + return mHeight; + } + + @Override + public void addObserver(Observer observer) { + mObservers.addObserver(observer); + } + + @Override + public void removeObserver(Observer observer) { + mObservers.removeObserver(observer); + } + + void setKeyboardExtensionHeight(@Px int newKeyboardExtensionHeight) { + if (mHeight == newKeyboardExtensionHeight) return; + mHeight = newKeyboardExtensionHeight; + for (Observer observer : mObservers) observer.onKeyboardExtensionHeightChanged(mHeight); + } +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java index 88a4193..38c1c5b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java
@@ -25,17 +25,17 @@ new PropertyProvider<>(); private final PropertyProvider<Action[]> mActionProvider = new PropertyProvider<>(AccessoryAction.GENERATE_PASSWORD_AUTOMATIC); - private final ManualFillingCoordinator mManualFillingCoordinator; + private final ManualFillingComponent mManualFillingComponent; private final ChromeActivity mActivity; private long mNativeView; private ManualFillingBridge(long nativeView, WindowAndroid windowAndroid) { mNativeView = nativeView; mActivity = (ChromeActivity) windowAndroid.getActivity().get(); - mManualFillingCoordinator = mActivity.getManualFillingController(); - mManualFillingCoordinator.registerPasswordProvider(mSheetDataProvider); - mManualFillingCoordinator.registerCreditCardProvider(); - mManualFillingCoordinator.registerActionProvider(mActionProvider); + mManualFillingComponent = mActivity.getManualFillingComponent(); + mManualFillingComponent.registerPasswordProvider(mSheetDataProvider); + mManualFillingComponent.registerCreditCardProvider(); + mManualFillingComponent.registerActionProvider(mActionProvider); } @CalledByNative @@ -76,22 +76,22 @@ @CalledByNative void showWhenKeyboardIsVisible() { - mManualFillingCoordinator.showWhenKeyboardIsVisible(); + mManualFillingComponent.showWhenKeyboardIsVisible(); } @CalledByNative void hide() { - mManualFillingCoordinator.hide(); + mManualFillingComponent.hide(); } @CalledByNative private void closeAccessorySheet() { - mManualFillingCoordinator.closeAccessorySheet(); + mManualFillingComponent.closeAccessorySheet(); } @CalledByNative private void swapSheetWithKeyboard() { - mManualFillingCoordinator.swapSheetWithKeyboard(); + mManualFillingComponent.swapSheetWithKeyboard(); } @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponent.java new file mode 100644 index 0000000..4c443867 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponent.java
@@ -0,0 +1,128 @@ +// 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. + +package org.chromium.chrome.browser.autofill.keyboard_accessory; + +import android.view.View; +import android.view.ViewStub; + +import org.chromium.chrome.browser.autofill.keyboard_accessory.data.KeyboardAccessoryData; +import org.chromium.chrome.browser.autofill.keyboard_accessory.data.PropertyProvider; +import org.chromium.components.autofill.AutofillDelegate; +import org.chromium.components.autofill.AutofillSuggestion; +import org.chromium.ui.DropdownPopupWindow; +import org.chromium.ui.base.WindowAndroid; + +/** + * This component handles the new, non-popup filling UI. + */ +public interface ManualFillingComponent { + /** + * Initializes the manual filling component. Calls to this class are NoOps until this method + * is called. + * @param windowAndroid The window needed to listen to the keyboard and to connect to + * activity. + * @param barStub The {@link ViewStub} used to inflate the keyboard accessory bar. + * @param sheetStub The {@link ViewStub} used to inflate the keyboard accessory bottom + * sheet. + */ + void initialize(WindowAndroid windowAndroid, ViewStub barStub, ViewStub sheetStub); + + /** + * Cleans up the manual UI by destroying the accessory bar and its bottom sheet. + */ + void destroy(); + + /** + * Handles tapping on the Android back button. + * @return Whether tapping the back button dismissed the accessory sheet or not. + */ + boolean handleBackPress(); + + /** + * Ensures that keyboard accessory and keyboard are hidden and reset. + */ + void dismiss(); + + /** + * Notifies the component that a popup window exists so it can be dismissed if necessary. + * @param popup A {@link DropdownPopupWindow} that might be dismissed later. + */ + void notifyPopupAvailable(DropdownPopupWindow popup); + + /** + * By registering this provider, an empty tab for passwords is created. Call + * {@link PropertyProvider#notifyObservers(Object)} to fill or update the sheet. + * @param sheetDataProvider The {@link PropertyProvider} the tab will get it's data from. + */ + void registerPasswordProvider( + PropertyProvider<KeyboardAccessoryData.AccessorySheetData> sheetDataProvider); + + /** + * By calling this function, an empty tab for credit cards is created. + */ + void registerCreditCardProvider(); + + /** + * Registers a provider, to provide actions for the keyboard accessory bar. Call + * {@link PropertyProvider#notifyObservers(Object)} to fill or update the actions. + * @param actionProvider The {@link PropertyProvider} providing actions. + */ + void registerActionProvider(PropertyProvider<KeyboardAccessoryData.Action[]> actionProvider); + + /** + * Registers a provider, to provide autofill suggestions for the keyboard accessory bar. Call + * {@link PropertyProvider#notifyObservers(Object)} to fill or update the suggestions. + * @param autofillProvider The {@link PropertyProvider} providing autofill suggestions. + * @param delegate The {@link AutofillDelegate} to call for interaction with the suggestions. + */ + void registerAutofillProvider( + PropertyProvider<AutofillSuggestion[]> autofillProvider, AutofillDelegate delegate); + + /** + * Signals that the accessory has permission to show if the user focuses a form field. + */ + void showWhenKeyboardIsVisible(); + + /** + * Requests to close the active tab in the keyboard accessory. If there is no active tab, this + * is a NoOp. + */ + void closeAccessorySheet(); + + /** + * Opens the keyboard which implicitly dismisses the sheet. Without open sheet, this is a NoOp. + */ + void swapSheetWithKeyboard(); + + /** + * Hides the sheet until undone with {@link #showWhenKeyboardIsVisible()}. + */ + void hide(); + + /** + * Notifies the component that the activity it's living in was resumed. + */ + void onResume(); + + /** + * Notifies the component that the activity it's living in was paused. + */ + void onPause(); + + /** + * Returns a size manager that allows to access the combined height of + * KeyboardAccessoryCoordinator and AccessorySheetCoordinator, and to be + * notified when it changes. + * @return A {@link KeyboardExtensionSizeManager}. + */ + KeyboardExtensionSizeManager getKeyboardExtensionSizeManager(); + + /** + * Returns whether the Keyboard is replaced by an accessory sheet or is about to do so. + * @return True if an accessory sheet is (being) opened and replacing the keyboard. + * @param view A {@link View} that is used to find the window root. + */ + boolean isFillingViewShown(View view); +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponentFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponentFactory.java new file mode 100644 index 0000000..8aaa0858f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingComponentFactory.java
@@ -0,0 +1,21 @@ +// 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. + +package org.chromium.chrome.browser.autofill.keyboard_accessory; + +/** + * Use {@link #createComponent()} to instantiate a {@link ManualFillingComponent}. + */ +public class ManualFillingComponentFactory { + private ManualFillingComponentFactory() {} + + /** + * Creates a {@link ManualFillingComponent} if the implementation is available. If it isn't, + * null is returned instead. + * @return A {@link ManualFillingComponent}. + */ + public static ManualFillingComponent createComponent() { + return new ManualFillingCoordinator(); + } +} \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java index 869b314..36f854a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
@@ -8,6 +8,7 @@ import android.view.ViewStub; import org.chromium.base.VisibleForTesting; +import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.autofill.keyboard_accessory.bar_component.KeyboardAccessoryCoordinator; import org.chromium.chrome.browser.autofill.keyboard_accessory.data.KeyboardAccessoryData; @@ -26,21 +27,18 @@ * {@link AccessorySheetCoordinator} to add and trigger surfaces that may assist users while filling * fields. */ -public class ManualFillingCoordinator { +class ManualFillingCoordinator implements ManualFillingComponent { private final ManualFillingMediator mMediator = new ManualFillingMediator(); - /** - * Initializes the manual filling component. Calls to this class are NoOps until this method is - * called. - * @param windowAndroid The window needed to listen to the keyboard and to connect to activity. - * @param barStub The {@link ViewStub} used to inflate the keyboard accessory bar. - * @param sheetStub The {@link ViewStub} used to inflate the keyboard accessory bottom sheet. - */ + @Override public void initialize(WindowAndroid windowAndroid, ViewStub barStub, ViewStub sheetStub) { if (barStub == null || sheetStub == null) return; // The manual filling isn't needed. if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)) { - barStub.setLayoutResource(org.chromium.chrome.R.layout.keyboard_accessory_modern); + barStub.setLayoutResource(R.layout.keyboard_accessory_modern); + } else { + barStub.setLayoutResource(R.layout.keyboard_accessory); } + sheetStub.setLayoutResource(R.layout.keyboard_accessory_sheet); initialize(windowAndroid, new KeyboardAccessoryCoordinator(mMediator, barStub), new AccessorySheetCoordinator(sheetStub)); } @@ -51,9 +49,7 @@ mMediator.initialize(accessoryBar, accessorySheet, windowAndroid); } - /** - * Cleans up the manual UI by destroying the accessory bar and its bottom sheet. - */ + @Override public void destroy() { mMediator.destroy(); } @@ -62,6 +58,7 @@ * Handles tapping on the Android back button. * @return Whether tapping the back button dismissed the accessory sheet or not. */ + @Override public boolean handleBackPress() { return mMediator.handleBackPress(); } @@ -69,6 +66,7 @@ /** * Ensures that keyboard accessory and keyboard are hidden and reset. */ + @Override public void dismiss() { mMediator.dismiss(); } @@ -77,74 +75,70 @@ * Notifies the component that a popup window exists so it can be dismissed if necessary. * @param popup A {@link DropdownPopupWindow} that might be dismissed later. */ + @Override public void notifyPopupAvailable(DropdownPopupWindow popup) { mMediator.notifyPopupOpened(popup); } - /** - * Requests to close the active tab in the keyboard accessory. If there is no active tab, this - * is a NoOp. - */ - void closeAccessorySheet() { + @Override + public void closeAccessorySheet() { mMediator.onCloseAccessorySheet(); } - /** - * Opens the keyboard which implicitly dismisses the sheet. Without open sheet, this is a NoOp. - */ - void swapSheetWithKeyboard() { + @Override + public void swapSheetWithKeyboard() { mMediator.swapSheetWithKeyboard(); } - void registerActionProvider(PropertyProvider<KeyboardAccessoryData.Action[]> actionProvider) { + @Override + public void registerActionProvider( + PropertyProvider<KeyboardAccessoryData.Action[]> actionProvider) { mMediator.registerActionProvider(actionProvider); } - void registerPasswordProvider( + @Override + public void registerPasswordProvider( PropertyProvider<KeyboardAccessoryData.AccessorySheetData> sheetDataProvider) { mMediator.registerPasswordProvider(sheetDataProvider); } - void registerCreditCardProvider() { + @Override + public void registerCreditCardProvider() { mMediator.registerCreditCardProvider(); } - void registerAutofillProvider( + @Override + public void registerAutofillProvider( PropertyProvider<AutofillSuggestion[]> autofillProvider, AutofillDelegate delegate) { mMediator.registerAutofillProvider(autofillProvider, delegate); } - void showWhenKeyboardIsVisible() { + @Override + public void showWhenKeyboardIsVisible() { mMediator.showWhenKeyboardIsVisible(); } + @Override public void hide() { mMediator.hide(); } + @Override public void onResume() { mMediator.resume(); } + @Override public void onPause() { mMediator.pause(); } - /** - * Returns a size manager that allows to access the combined height of - * {@link KeyboardAccessoryCoordinator} and {@link AccessorySheetCoordinator}, and to be - * notified when it changes. - * @return A {@link KeyboardExtensionSizeManager}. - */ + @Override public KeyboardExtensionSizeManager getKeyboardExtensionSizeManager() { return mMediator.getKeyboardExtensionSizeManager(); } - /** - * Returns whether the Keyboard is replaced by an accessory sheet or is about to do so. - * @return True if an accessory sheet is (being) opened and replacing the keyboard. - * @param view A {@link View} that is used to find the window root. - */ + @Override public boolean isFillingViewShown(View view) { return mMediator.isFillingViewShown(view); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java index d64925b..15be3ca6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -65,8 +65,8 @@ private PropertyModel mModel = ManualFillingProperties.createFillingModel(); private WindowAndroid mWindowAndroid; private Supplier<InsetObserverView> mInsetObserverViewSupplier; - private final KeyboardExtensionSizeManager mKeyboardExtensionSizeManager = - new KeyboardExtensionSizeManager(); + private final KeyboardExtensionSizeManagerImpl mKeyboardExtensionSizeManager = + new KeyboardExtensionSizeManagerImpl(); private final ManualFillingStateCache mStateCache = new ManualFillingStateCache(); private final HashSet<Tab> mObservedTabs = new HashSet<>(); private KeyboardAccessoryCoordinator mKeyboardAccessory; @@ -485,8 +485,7 @@ @Nullable PasswordAccessorySheetCoordinator getOrCreatePasswordSheet() { if (!isInitialized()) return null; - if (!ChromeFeatureList.isEnabled(ChromeFeatureList.EXPERIMENTAL_UI) - && !ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) { + if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) { return null; } WebContents webContents = mActivity.getCurrentWebContents(); @@ -515,8 +514,7 @@ if (!ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID)) { return null; } - if (!ChromeFeatureList.isEnabled(ChromeFeatureList.EXPERIMENTAL_UI) - && !ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) { + if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) { return null; } WebContents webContents = mActivity.getCurrentWebContents();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java index 0404cdc..bad3caf5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -345,7 +345,7 @@ if (mBottomBarView == null) return; // Check bottom bar view but don't inflate it. // Hide the container of the bottom bar while the extension is showing. This doesn't // affect the content. - boolean keyboardExtensionHidesBottomBar = mActivity.getManualFillingController() + boolean keyboardExtensionHidesBottomBar = mActivity.getManualFillingComponent() .getKeyboardExtensionSizeManager() .getKeyboardExtensionHeight() > 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java index 9bfb5b14..7554c91b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBar.java
@@ -35,7 +35,7 @@ */ public class DataReductionPromoInfoBar extends ConfirmInfoBar { private static final String M48_STABLE_RELEASE_DATE = "2016-01-26"; - private static final String ENABLE_INFOBAR_SWITCH = "enable-data-reduction-promo-infobar"; + private static final String FORCE_INFOBAR_SWITCH = "force-data-reduction-promo-infobar"; private static final int NO_INSTALL_TIME = 0; private static Bitmap sIcon; @@ -44,21 +44,11 @@ private static String sPrimaryButtonText; private static String sSecondaryButtonText; - /** - * Launch the data reduction infobar promo, if it needs to be displayed. - * - * @param context An Android context. - * @param webContents The WebContents of the tab on which the infobar should show. - * @param url The URL of the page on which the infobar should show. - * @param isFragmentNavigation Whether the main frame navigation did not cause changes to the - * document (for example scrolling to a named anchor PopState). - * @param statusCode The HTTP status code of the navigation. - * @return boolean Whether the promo was launched. - */ - public static boolean maybeLaunchPromoInfoBar(Context context, - WebContents webContents, String url, boolean isErrorPage, boolean isFragmentNavigation, - int statusCode) { - ThreadUtils.assertOnUiThread(); + private static boolean shouldLaunchPromoInfoBar(Context context, WebContents webContents, + String url, boolean isErrorPage, boolean isFragmentNavigation, int statusCode) { + // This switch is only used for testing so let it override every other check. + if (CommandLine.getInstance().hasSwitch(FORCE_INFOBAR_SWITCH)) return true; + if (webContents.isIncognito()) return false; if (isErrorPage) return false; if (isFragmentNavigation) return false; @@ -104,22 +94,13 @@ // promo was displayed or the command line switch is on. If the last promo was shown // before M51 then |freOrSecondRunVersion| will be empty and it is safe to show the // infobar promo. - if (!CommandLine.getInstance().hasSwitch(ENABLE_INFOBAR_SWITCH) - && !freOrSecondRunVersion.isEmpty() - && currentMilestone < VersionNumberGetter - .getMilestoneFromVersionNumber(freOrSecondRunVersion) + 2) { + if (!freOrSecondRunVersion.isEmpty() + && currentMilestone < VersionNumberGetter.getMilestoneFromVersionNumber( + freOrSecondRunVersion) + + 2) { return false; } - DataReductionPromoInfoBar.launch(webContents, - BitmapFactory.decodeResource(context.getResources(), R.drawable.infobar_chrome), - context.getString(R.string.data_reduction_promo_infobar_title), - context.getString(R.string.data_reduction_promo_infobar_text), - context.getString( - DataReductionBrandingResourceProvider.getDataSaverBrandedString( - R.string.data_reduction_enable_button)), - context.getString(R.string.no_thanks)); - return true; } finally { StrictMode.setThreadPolicy(oldPolicy); @@ -127,6 +108,37 @@ } /** + * Launch the data reduction infobar promo, if it needs to be displayed. + * + * @param context An Android context. + * @param webContents The WebContents of the tab on which the infobar should show. + * @param url The URL of the page on which the infobar should show. + * @param isFragmentNavigation Whether the main frame navigation did not cause changes to the + * document (for example scrolling to a named anchor PopState). + * @param statusCode The HTTP status code of the navigation. + * @return boolean Whether the promo was launched. + */ + public static boolean maybeLaunchPromoInfoBar(Context context, WebContents webContents, + String url, boolean isErrorPage, boolean isFragmentNavigation, int statusCode) { + ThreadUtils.assertOnUiThread(); + + if (!shouldLaunchPromoInfoBar( + context, webContents, url, isErrorPage, isFragmentNavigation, statusCode)) { + return false; + } + + DataReductionPromoInfoBar.launch(webContents, + BitmapFactory.decodeResource(context.getResources(), R.drawable.infobar_chrome), + context.getString(R.string.data_reduction_promo_infobar_title), + context.getString(R.string.data_reduction_promo_infobar_text), + context.getString(DataReductionBrandingResourceProvider.getDataSaverBrandedString( + R.string.data_reduction_enable_button)), + context.getString(R.string.no_thanks)); + + return true; + } + + /** * Gets the time at which this app was first installed in milliseconds since January 1, 1970 * 00:00:00.0 UTC. *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/infobar/OWNERS index 982256d..f1bb951b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/OWNERS
@@ -1,6 +1,7 @@ mdjones@chromium.org per-file AppBannerInfoBar*=dominickn@chromium.org +per-file DataReductionPromoInfoBar*=file://components/data_reduction_proxy/OWNERS per-file GeneratedPasswordSavedInfoBar*=rouslan@chromium.org per-file InstallableAmbientBadgeInfoBar*=dominickn@chromium.org
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java index 232a659a..49c552739 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java
@@ -14,7 +14,6 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeFeatureList; @@ -105,7 +104,6 @@ */ @Test @MediumTest - @DisabledTest(message = "https://crbug.com/947696") public void testSelectSuggestionHidesKeyboardAccessory() throws ExecutionException, InterruptedException, TimeoutException { loadTestPage();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java index 17c4ea3..d16906a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -89,17 +89,13 @@ mHelper.loadTestPage(false); assertNotNull("Controller for Manual filling should be available.", - mActivityTestRule.getActivity().getManualFillingController()); + mHelper.getManualFillingCoordinator()); assertNotNull("Keyboard accessory should have an instance.", - mActivityTestRule.getActivity() - .getManualFillingController() + mHelper.getManualFillingCoordinator() .getMediatorForTesting() .getKeyboardAccessory()); assertNotNull("Accessory Sheet should have an instance.", - mActivityTestRule.getActivity() - .getManualFillingController() - .getMediatorForTesting() - .getAccessorySheet()); + mHelper.getManualFillingCoordinator().getMediatorForTesting().getAccessorySheet()); } @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java index dbccab5..2ffe243 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
@@ -104,19 +104,16 @@ TestThreadUtils.runOnUiThreadBlocking(() -> { ChromeTabbedActivity activity = mActivityTestRule.getActivity(); mWebContentsRef.set(activity.getActivityTab().getWebContents()); - activity.getManualFillingController() - .getMediatorForTesting() - .setInsetObserverViewSupplier( - () - -> getKeyboard().createInsetObserver( - activity.getInsetObserverView().getContext())); + getManualFillingCoordinator().getMediatorForTesting().setInsetObserverViewSupplier( + () + -> getKeyboard().createInsetObserver( + activity.getInsetObserverView().getContext())); // The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is // never brought up. final ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mWebContentsRef.get()); mInputMethodManagerWrapper = TestInputMethodManagerWrapper.create(imeAdapter); imeAdapter.setInputMethodManagerWrapper(mInputMethodManagerWrapper); - activity.getManualFillingController().registerPasswordProvider( - mSheetSuggestionsProvider); + getManualFillingCoordinator().registerPasswordProvider(mSheetSuggestionsProvider); }); if (waitForNode) DOMUtils.waitForNonZeroNodeBounds(mWebContentsRef.get(), PASSWORD_NODE_ID); cacheCredentials(new String[0], new String[0]); // This caches the empty state. @@ -135,6 +132,11 @@ return mWebContentsRef.get(); } + ManualFillingCoordinator getManualFillingCoordinator() { + return (ManualFillingCoordinator) mActivityTestRule.getActivity() + .getManualFillingComponent(); + } + public void focusPasswordField() throws TimeoutException, InterruptedException { DOMUtils.focusNode(mActivityTestRule.getWebContents(), PASSWORD_NODE_ID); TestThreadUtils.runOnUiThreadBlocking( @@ -152,10 +154,7 @@ DOMUtils.clickNode(mWebContentsRef.get(), USERNAME_NODE_ID); if (forceAccessory) { TestThreadUtils.runOnUiThreadBlocking(() -> { - mActivityTestRule.getActivity() - .getManualFillingController() - .getMediatorForTesting() - .showWhenKeyboardIsVisible(); + getManualFillingCoordinator().getMediatorForTesting().showWhenKeyboardIsVisible(); }); } getKeyboard().showKeyboard(mActivityTestRule.getActivity().getCurrentFocus()); @@ -190,10 +189,8 @@ public void waitForKeyboardAccessoryToDisappear() { CriteriaHelper.pollInstrumentationThread(() -> { - KeyboardAccessoryCoordinator accessory = mActivityTestRule.getActivity() - .getManualFillingController() - .getMediatorForTesting() - .getKeyboardAccessory(); + KeyboardAccessoryCoordinator accessory = + getManualFillingCoordinator().getMediatorForTesting().getKeyboardAccessory(); return accessory != null && !accessory.isShown(); }); CriteriaHelper.pollUiThread(() -> { @@ -204,10 +201,8 @@ public void waitForKeyboardAccessoryToBeShown() { CriteriaHelper.pollInstrumentationThread(() -> { - KeyboardAccessoryCoordinator accessory = mActivityTestRule.getActivity() - .getManualFillingController() - .getMediatorForTesting() - .getKeyboardAccessory(); + KeyboardAccessoryCoordinator accessory = + getManualFillingCoordinator().getMediatorForTesting().getKeyboardAccessory(); return accessory != null && accessory.isShown(); }); CriteriaHelper.pollUiThread(() -> { @@ -250,10 +245,7 @@ } public PasswordAccessorySheetCoordinator getOrCreatePasswordAccessorySheet() { - return mActivityTestRule.getActivity() - .getManualFillingController() - .getMediatorForTesting() - .getOrCreatePasswordSheet(); + return getManualFillingCoordinator().getMediatorForTesting().getOrCreatePasswordSheet(); } // ---------------------------------- @@ -413,8 +405,7 @@ public void addGenerationButton() { PropertyProvider<KeyboardAccessoryData.Action[]> generationActionProvider = new PropertyProvider<>(AccessoryAction.GENERATE_PASSWORD_AUTOMATIC); - mActivityTestRule.getActivity().getManualFillingController().registerActionProvider( - generationActionProvider); + getManualFillingCoordinator().registerActionProvider(generationActionProvider); TestThreadUtils.runOnUiThreadBlocking(() -> { generationActionProvider.notifyObservers(new KeyboardAccessoryData.Action[] { new KeyboardAccessoryData.Action("Generate Password",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java index 53a9c24..200f516 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
@@ -71,21 +71,8 @@ @Test @SmallTest - @EnableFeatures({ChromeFeatureList.EXPERIMENTAL_UI}) - public void testPasswordSheetIsAvailableInExperimentalUi() throws InterruptedException { - mHelper.loadTestPage(false); - - CriteriaHelper.pollUiThread(() - -> mHelper.getOrCreatePasswordAccessorySheet() != null, - "Password Sheet should be bound to accessory sheet."); - } - - @Test - @SmallTest - @DisableFeatures( - {ChromeFeatureList.EXPERIMENTAL_UI, ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY}) - public void - testPasswordSheetUnavailableWithoutFeature() throws InterruptedException { + @DisableFeatures({ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY}) + public void testPasswordSheetUnavailableWithoutFeature() throws InterruptedException { mHelper.loadTestPage(false); Assert.assertNull("Password Sheet should not have been created.",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/OWNERS new file mode 100644 index 0000000..ac64cd03 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/OWNERS
@@ -0,0 +1,4 @@ +file://ui/android/java/src/org/chromium/ui/modaldialog/OWNERS + +# COMPONENT: UI>Browser>Mobile +# OS: Android
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java index 4e0e5e98..6a20b33 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java
@@ -60,7 +60,6 @@ import org.chromium.chrome.browser.tab.Tab.TabHidingType; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.KeyboardVisibilityDelegate; @@ -79,7 +78,6 @@ @EnableFeatures({ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY, ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY, ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID}) -@DisableFeatures({ChromeFeatureList.EXPERIMENTAL_UI}) public class ManualFillingControllerTest { @Mock private ChromeWindow mMockWindow;
diff --git a/chrome/android/touchless/java/res/drawable/ic_apps_black_24dp.xml b/chrome/android/touchless/java/res/drawable/ic_apps_black_24dp.xml index 5c228efc..51d4fbf 100644 --- a/chrome/android/touchless/java/res/drawable/ic_apps_black_24dp.xml +++ b/chrome/android/touchless/java/res/drawable/ic_apps_black_24dp.xml
@@ -1,3 +1,6 @@ +<!-- 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" @@ -6,6 +9,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:fillColor="#4267B2" + android:fillColor="@color/default_icon_color_blue" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/> </vector>
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java index e7fa40b08a..2ab1aa3 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
@@ -48,6 +48,9 @@ private TooltipView mTooltipView; private ProgressBarView mProgressBarView; + /** The class that enables zooming for all websites and handles touchless zooming. */ + private TouchlessZoomHelper mTouchlessZoomHelper; + /** The class that controls the UI for touchless devices. */ private TouchlessUiController mUiController; @@ -129,6 +132,7 @@ new KeyFunctionsIPHCoordinator(mTooltipView, getActivityTabProvider()); mProgressBarCoordinator = new ProgressBarCoordinator(mProgressBarView, getActivityTabProvider()); + mTouchlessZoomHelper = new TouchlessZoomHelper(getActivityTabProvider()); // By this point if we were going to restore a URL from savedInstanceState we would already // have done so. @@ -238,6 +242,7 @@ super.onDestroyInternal(); if (mKeyFunctionsIPHCoordinator != null) mKeyFunctionsIPHCoordinator.destroy(); if (mProgressBarCoordinator != null) mProgressBarCoordinator.destroy(); + if (mTouchlessZoomHelper != null) mTouchlessZoomHelper.destroy(); if (mUiController != null) { mUiController.destroy(); mUiController = null;
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java index 8e2362782..f835517 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabView.java
@@ -38,6 +38,8 @@ mPlaceholder = findViewById(R.id.placeholder); mLastTabChip = findViewById(R.id.last_tab_chip); + // Allow the system ui to control our background. + mLastTabChip.setBackground(null); TextView primaryTextView = mLastTabChip.getPrimaryTextView(); TextView secondaryTextView = mLastTabChip.getSecondaryTextView();
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java index f0ebc57..f4029cf 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsAdapter.java
@@ -98,8 +98,9 @@ private TextView mTitleView; /** - * @param model the main property model coming from {@link SiteSuggestionsCoordinator}. - * @param iconGenerator an icon generator for creating icons. + * @param model The main property model coming from {@link SiteSuggestionsCoordinator}. Contains + * properties for a list of suggestions, number of items, and current focused index. + * @param iconGenerator An icon generator for creating icons. * @param navigationDelegate delegate for navigation controls * @param contextMenuManager handles context menu creation * @param layoutManager the layout manager controlling this recyclerview and adapter @@ -129,8 +130,7 @@ @Override public int getItemViewType(int position) { - int itemCount = mModel.get(ITEM_COUNT_KEY); - if (itemCount == 1 || position % itemCount == 0) return ViewType.ALL_APPS_TYPE; + if (isAllAppsPosition(position)) return ViewType.ALL_APPS_TYPE; return ViewType.SUGGESTION_TYPE; } @@ -155,9 +155,9 @@ WindowOpenDisposition.CURRENT_TAB, UrlConstants.EXPLORE_URL)); } else if (holder.getItemViewType() == ViewType.SUGGESTION_TYPE) { // If site suggestion, attach context menu handler; clicks navigate to site url. - int itemCount = mModel.get(ITEM_COUNT_KEY); // Subtract 1 from position % MAX_TILES to account for "all apps" taking up one space. - PropertyModel item = mModel.get(SUGGESTIONS_KEY).get((position % itemCount) - 1); + PropertyModel item = + mModel.get(SUGGESTIONS_KEY).get(getModelPositionFromAdapterPosition(position)); // Only update the icon for icon updates. if (payload == SiteSuggestionModel.ICON_KEY) { tile.updateIcon(item.get(SiteSuggestionModel.ICON_KEY), @@ -185,13 +185,12 @@ if (propertyKey == CURRENT_INDEX_KEY) { // When the current index changes, we want to scroll to position and update the title. int position = mModel.get(CURRENT_INDEX_KEY); - int itemCount = mModel.get(ITEM_COUNT_KEY); mLayoutManager.scrollToPosition(position); - if (itemCount == 1 || position % itemCount == 0) { + if (isAllAppsPosition(position)) { mTitleView.setText(R.string.ntp_all_apps); } else { mTitleView.setText(mModel.get(SUGGESTIONS_KEY) - .get(position % itemCount - 1) + .get(getModelPositionFromAdapterPosition(position)) .get(SiteSuggestionModel.TITLE_KEY)); } } @@ -214,7 +213,7 @@ public void notifyItemRangeRemoved(int index, int count) { if (mModel.get(SUGGESTIONS_KEY).size() == 0) { // When we removed the last item in the model, we would go from infinite scroll - // back to non-scrolling. Notify Recyclerview to remove everything. + // back to non-scrolling. Notify RecyclerView to remove everything. super.notifyItemRangeRemoved(1, Integer.MAX_VALUE - 1); } else { // Otherwise we are already infinite-scrolling, so just tell recyclerview that @@ -225,33 +224,21 @@ @Override public void notifyItemRangeChanged(int index, int count, @Nullable PropertyKey payload) { - if (count > 1) { - // If more than 1 item was changed, then assume everything was changed. This should - // only happen if we are infinite-scrolling. - super.notifyItemRangeChanged(0, Integer.MAX_VALUE, payload); - } else if (mModel.get(SUGGESTIONS_KEY).size() == 0) { - // If itemCount is 1, then notify super. - // This should only happen if "All apps" icon has changed in some way and we aren't - // infinite-scrolling. - super.notifyItemRangeChanged(index, count, payload); - } else { - // Otherwise, count = 1 and we have an infinite list. We will only notify that items - // near the currently visible area has changed. - // beginIndex at the layoutManager's firstVisibleItemPosition, with buffer. - int beginIndex = - mLayoutManager.findFirstVisibleItemPosition() - mModel.get(ITEM_COUNT_KEY); - // endIndex at the lastVisibleItemPosition, with buffer. - int endIndex = - mLayoutManager.findLastVisibleItemPosition() + mModel.get(ITEM_COUNT_KEY); - // Find elements between begin and end such that (i % itemCount) - 1 == index. - // Subtract 1 because itemRangeChanged is called from the listObserver which does not - // have "All apps". However, i is calculated from layoutManager, which includes "All - // apps" - for (int i = beginIndex; i < endIndex; i++) { - if (i % mModel.get(ITEM_COUNT_KEY) - 1 == index) { - super.notifyItemRangeChanged(i, 1, payload); - } - } - } + // When something has changed, assume everything has changed. + super.notifyItemRangeChanged(0, Integer.MAX_VALUE, payload); + } + + public void destroy() { + mModel.removeObserver(this); + mModel.get(SUGGESTIONS_KEY).removeObserver(this); + } + + private int getModelPositionFromAdapterPosition(int adapterPosition) { + return adapterPosition % mModel.get(ITEM_COUNT_KEY) - 1; + } + + private boolean isAllAppsPosition(int adapterPosition) { + return mModel.get(ITEM_COUNT_KEY) == 1 + || getModelPositionFromAdapterPosition(adapterPosition) < 0; } }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java index 5b6044e..ece6b74b 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsCoordinator.java
@@ -35,6 +35,7 @@ new PropertyModel.WritableIntPropertyKey(); private SiteSuggestionsMediator mMediator; + private SiteSuggestionsAdapter mAdapterDelegate; SiteSuggestionsCoordinator(View parentView, Profile profile, SuggestionsNavigationDelegate navigationDelegate, ContextMenuManager contextMenuManager, @@ -56,13 +57,13 @@ LinearLayoutManager layoutManager = new SiteSuggestionsLayoutManager(context); RecyclerView recyclerView = suggestionsView.findViewById(R.id.most_likely_launcher_recycler); - SiteSuggestionsAdapter adapterDelegate = new SiteSuggestionsAdapter(model, iconGenerator, - navigationDelegate, contextMenuManager, layoutManager, + mAdapterDelegate = new SiteSuggestionsAdapter(model, iconGenerator, navigationDelegate, + contextMenuManager, layoutManager, suggestionsView.findViewById(R.id.most_likely_web_title_text)); RecyclerViewAdapter<SiteSuggestionsViewHolderFactory.SiteSuggestionsViewHolder, PropertyKey> adapter = new RecyclerViewAdapter<>( - adapterDelegate, new SiteSuggestionsViewHolderFactory()); + mAdapterDelegate, new SiteSuggestionsViewHolderFactory()); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); @@ -72,5 +73,6 @@ public void destroy() { mMediator.destroy(); + mAdapterDelegate.destroy(); } }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java index 0dd75be..d97592ad 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java
@@ -64,6 +64,7 @@ protected Bitmap doInBackground() { return BitmapFactory.decodeFile(suggestion.whitelistIconPath); } + @Override protected void onPostExecute(Bitmap icon) { if (icon == null) makeIconRequest(siteSuggestion); @@ -75,7 +76,7 @@ // Total item count is 1 more than number of site suggestions to account for "all apps". mModel.set(SiteSuggestionsCoordinator.ITEM_COUNT_KEY, getItemCount() + 1); - // If we fetched site suggestions the first time, set initial scrolled position. + // If we fetched site suggestions for the first time, set initial scrolled position. // We don't want to set scrolled position if we've already set position before. if (siteSuggestions.size() > 0 && mModel.get(SiteSuggestionsCoordinator.CURRENT_INDEX_KEY) == 0) {
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java index f458c5c..d8a5440 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java
@@ -8,10 +8,10 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab.TabHidingType; import org.chromium.content_public.browser.NavigationHandle; -import org.chromium.ui.base.TouchlessEventHandler; +import org.chromium.ui.touchless.TouchlessEventHandler; /** - * Bridge to org.chromium.ui.base.TouchlessEventHandler + * Bridge to org.chromium.ui.touchless.TouchlessEventHandler */ public class TouchlessTabObserver extends EmptyTabObserver { @Override
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java new file mode 100644 index 0000000..8cebac2 --- /dev/null +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java
@@ -0,0 +1,49 @@ +// 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. + +package org.chromium.chrome.browser.touchless; + +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.ZoomController; +import org.chromium.chrome.browser.accessibility.FontSizePrefs; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.ui.touchless.TouchlessEventHandler; +import org.chromium.ui.touchless.TouchlessEventHandler.TouchlessZoomCallback; + +/** + * Enables zooming for all websites. Implements {@link TouchlessZoomCallback} and performs zoom in + * and zoom out upon {@link TouchlessEventHandler}'s request. + */ +public class TouchlessZoomHelper + implements TouchlessZoomCallback, ActivityTabProvider.ActivityTabObserver { + private Tab mCurrentTab; + private ActivityTabProvider mActivityTabProvider; + + public TouchlessZoomHelper(ActivityTabProvider tabProvider) { + mActivityTabProvider = tabProvider; + mActivityTabProvider.addObserverAndTrigger(this); + FontSizePrefs.getInstance(tabProvider.getActivityTab().getContext()).enableTouchlessMode(); + TouchlessEventHandler.setZoomCallback(this); + } + + @Override + public void onZoomInRequested() { + if (mCurrentTab != null) ZoomController.zoomIn(mCurrentTab.getWebContents()); + } + + @Override + public void onZoomOutRequested() { + if (mCurrentTab != null) ZoomController.zoomOut(mCurrentTab.getWebContents()); + } + + @Override + public void onActivityTabChanged(Tab tab, boolean hint) { + mCurrentTab = tab; + } + + public void destroy() { + TouchlessEventHandler.removeZoomCallback(this); + mActivityTabProvider.removeObserver(this); + } +}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java index 991ba67..418d8ca 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
@@ -9,9 +9,9 @@ import org.chromium.chrome.browser.native_page.NativePageFactory; import org.chromium.chrome.browser.tab.Tab; import org.chromium.content_public.browser.UiThreadTaskTraits; -import org.chromium.ui.base.CursorObserver; -import org.chromium.ui.base.TouchlessEventHandler; import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.touchless.CursorObserver; +import org.chromium.ui.touchless.TouchlessEventHandler; import java.util.concurrent.FutureTask;
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni index 949b41c..d08a7836 100644 --- a/chrome/android/touchless/touchless_java_sources.gni +++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -26,6 +26,7 @@ "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessNewTabPageTopLayout.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabObserver.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiController.java", + "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java", "touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java", "touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogProperties.java", "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 46350189..c74028d 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1785,6 +1785,12 @@ flag_descriptions::kEnableDataSaverLiteModeRebrandName, flag_descriptions::kEnableDataSaverLiteModeRebrandDescription, kOsAll, FEATURE_VALUE_TYPE(previews::features::kDataSaverLiteModeRebranding)}, +#if defined(OS_CHROMEOS) || defined(OS_LINUX) + {"enable-save-data", flag_descriptions::kEnableSaveDataName, + flag_descriptions::kEnableSaveDataDescription, kOsCrOS, + SINGLE_VALUE_TYPE( + data_reduction_proxy::switches::kEnableDataReductionProxy)}, +#endif // OS_CHROMEOS {"enable-client-lo-fi", flag_descriptions::kEnableClientLoFiName, flag_descriptions::kEnableClientLoFiDescription, kOsAll, FEATURE_VALUE_TYPE(previews::features::kClientLoFi)},
diff --git a/chrome/browser/chromeos/app_mode/arc/DEPS b/chrome/browser/chromeos/app_mode/arc/DEPS new file mode 100644 index 0000000..fa19670 --- /dev/null +++ b/chrome/browser/chromeos/app_mode/arc/DEPS
@@ -0,0 +1,6 @@ +specific_include_rules = { + # crbug.com/887156 + "arc_kiosk_app_launcher\.cc": [ + "+ash/shell.h", + ], +}
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc index 2862d18..099d05d 100644 --- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
@@ -9,6 +9,7 @@ #include "ash/public/cpp/window_properties.h" #include "ash/public/interfaces/window_pin_type.mojom.h" +#include "ash/shell.h" #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h" #include "components/arc/metrics/arc_metrics_constants.h" @@ -23,7 +24,8 @@ Delegate* delegate) : app_id_(app_id), prefs_(prefs), delegate_(delegate) { prefs_->AddObserver(this); - aura::Env::GetInstance()->AddObserver(this); + // crbug.com/887156 + ash::Shell::Get()->aura_env()->AddObserver(this); // Launching the app by app id in landscape mode and in non-touch mode. arc::LaunchApp(context, app_id_, ui::EF_NONE, arc::UserInteractionType::NOT_USER_INITIATED); @@ -89,7 +91,7 @@ } void ArcKioskAppLauncher::StopObserving() { - aura::Env::GetInstance()->RemoveObserver(this); + ash::Shell::Get()->aura_env()->AddObserver(this); for (auto* window : windows_) window->RemoveObserver(this); windows_.clear();
diff --git a/chrome/browser/client_hints/OWNERS b/chrome/browser/client_hints/OWNERS index 6ebcbe0..7ad3493f 100644 --- a/chrome/browser/client_hints/OWNERS +++ b/chrome/browser/client_hints/OWNERS
@@ -1,3 +1,4 @@ +yoavweiss@chromium.org tbansal@chromium.org ryansturm@chromium.org
diff --git a/chrome/browser/conflicts/incompatible_applications_browsertest.cc b/chrome/browser/conflicts/incompatible_applications_browsertest.cc index 11e6db08..512c86bb 100644 --- a/chrome/browser/conflicts/incompatible_applications_browsertest.cc +++ b/chrome/browser/conflicts/incompatible_applications_browsertest.cc
@@ -15,6 +15,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/test_reg_util_win.h" #include "base/threading/thread_restrictions.h" +#include "base/win/win_util.h" #include "base/win/windows_version.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/conflicts/incompatible_applications_updater_win.h" @@ -80,7 +81,8 @@ // The name of the application deemed incompatible. static constexpr wchar_t kApplicationName[] = L"FooBar123"; - IncompatibleApplicationsBrowserTest() = default; + IncompatibleApplicationsBrowserTest() : scoped_domain_(true) {} + ~IncompatibleApplicationsBrowserTest() override = default; void SetUp() override { @@ -170,6 +172,9 @@ ASSERT_TRUE(base::CopyFile(test_dll_path, dll_path)); } + // The feature is always disabled on domain-joined machines. + base::win::ScopedDomainStateForTesting scoped_domain_; + // Temp directory used to host the install directory and the module list. base::ScopedTempDir scoped_temp_dir_;
diff --git a/chrome/browser/conflicts/third_party_blocking_browsertest.cc b/chrome/browser/conflicts/third_party_blocking_browsertest.cc index 7bef32a2..1dc1f0d 100644 --- a/chrome/browser/conflicts/third_party_blocking_browsertest.cc +++ b/chrome/browser/conflicts/third_party_blocking_browsertest.cc
@@ -14,6 +14,7 @@ #include "base/test/test_reg_util_win.h" #include "base/win/registry.h" #include "base/win/win_util.h" +#include "base/win/windows_version.h" #include "chrome/browser/conflicts/module_blacklist_cache_updater_win.h" #include "chrome/browser/conflicts/module_blacklist_cache_util_win.h" #include "chrome/browser/conflicts/module_database_win.h" @@ -159,6 +160,9 @@ // browser launch. IN_PROC_BROWSER_TEST_F(ThirdPartyBlockingBrowserTest, CreateModuleBlacklistCache) { + if (base::win::GetVersion() < base::win::VERSION_WIN8) + return; + base::FilePath module_list_path; ASSERT_NO_FATAL_FAILURE(CreateModuleList(&module_list_path)); ASSERT_FALSE(module_list_path.empty());
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc index 260f582..cca5246 100644 --- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc +++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -776,30 +776,6 @@ BYPASS_EVENT_TYPE_MALFORMED_407, 1); } -IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, - ProxyBypassedForCurrentRequestOn502Error) { - base::HistogramTester histogram_tester; - net::EmbeddedTestServer test_server; - test_server.RegisterRequestHandler( - base::BindRepeating(&BasicResponse, kDummyBody)); - ASSERT_TRUE(test_server.Start()); - - SetStatusCode(net::HTTP_BAD_GATEWAY); - - ui_test_utils::NavigateToURL(browser(), - GetURLWithMockHost(test_server, "/echo")); - EXPECT_THAT(GetBody(), kDummyBody); - histogram_tester.ExpectUniqueSample( - "DataReductionProxy.BlockTypePrimary", - BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, 1); - - // Proxy should no longer be blocked, and use first proxy. - SetStatusCode(net::HTTP_OK); - ui_test_utils::NavigateToURL(browser(), - GetURLWithMockHost(test_server, "/echo")); - EXPECT_EQ(GetBody(), kPrimaryResponse); -} - // Tests that if using data reduction proxy results in redirect loop, then // the proxy is bypassed, and the request is fetched directly. IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, RedirectCycle) {
diff --git a/chrome/browser/download/download_frame_policy_browsertest.cc b/chrome/browser/download/download_frame_policy_browsertest.cc index b42f3b5..f668c38e 100644 --- a/chrome/browser/download/download_frame_policy_browsertest.cc +++ b/chrome/browser/download/download_frame_policy_browsertest.cc
@@ -32,6 +32,7 @@ #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/network/public/cpp/features.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/url_constants.h" @@ -136,10 +137,6 @@ class DownloadFramePolicyBrowserTest : public subresource_filter::SubresourceFilterBrowserTest { public: - DownloadFramePolicyBrowserTest() { - scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging); - } - ~DownloadFramePolicyBrowserTest() override {} void SetUpOnMainThread() override { @@ -281,7 +278,6 @@ std::string GetSubframeId() { return "test"; } private: - base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<base::HistogramTester> histogram_tester_; std::unique_ptr<content::DownloadTestObserver> download_observer_; std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter> @@ -619,6 +615,48 @@ SandboxOption::kAllowDownloadsWithoutUserActivation), ::testing::Bool())); +class DefaultBlockSandboxDownloadBrowserTest + : public DownloadFramePolicyBrowserTest { + public: + DefaultBlockSandboxDownloadBrowserTest() { + scoped_feature_list_.InitAndDisableFeature( + network::features::kNetworkService); + } + + ~DefaultBlockSandboxDownloadBrowserTest() override = default; + + void SetUpCommandLine(base::CommandLine* command_line) override { + SetRuntimeFeatureCommand( + true, "BlockingDownloadsInSandboxWithoutUserActivation", command_line); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +// To test PDF works fine when the policy disallows just download, which +// essentially tests that the appropriate ResourceInterceptPolicy +// |kAllowPluginOnly| is set for resource handling. +// +// When NetworkService is disabled and when ResourceInterceptPolicy has been +// incorrectly set to |kAllowNone|, no stream interceptor will be set up to +// handle the PDF content, and the content will instead be sent to the renderer +// where a UTF-8 GUID is expected to arrive. It'll then hit a DCHECK checking +// the UTF-8-ness of the GUID. +// +// TODO(yaoxia): Use a more straightforward approach to assert that the pdf +// content displays fine rather than relying on the DCHECK failure that would +// happen with an incorrect implementation. +IN_PROC_BROWSER_TEST_F(DefaultBlockSandboxDownloadBrowserTest, PdfNotBlocked) { + InitializeOneSubframeSetup( + SandboxOption::kDisallowDownloadsWithoutUserActivation, + false /* is_ad_frame */, false /* is_cross_origin */); + content::TestNavigationObserver navigation_observer(web_contents()); + EXPECT_TRUE(ExecuteScriptWithoutUserGesture(GetSubframeRfh(), + "top.location = 'test.pdf';")); + navigation_observer.Wait(); +} + // Download gets blocked when LoadPolicy is DISALLOW for the navigation to // download. This test is technically unrelated to policy on frame, but stays // here for convenience.
diff --git a/chrome/browser/download/download_service_factory.cc b/chrome/browser/download/download_service_factory.cc index 242fb363..8818a84 100644 --- a/chrome/browser/download/download_service_factory.cc +++ b/chrome/browser/download/download_service_factory.cc
@@ -21,7 +21,7 @@ #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_constants.h" -#include "components/download/content/factory/download_service_factory.h" +#include "components/download/content/factory/download_service_factory_helper.h" #include "components/download/public/background_service/clients.h" #include "components/download/public/background_service/download_service.h" #include "components/download/public/background_service/features.h"
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc index bc98590..03a0308 100644 --- a/chrome/browser/extensions/extension_disabled_ui.cc +++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -219,8 +219,8 @@ messages.push_back( l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO)); } else { - // TODO(treib): If NeedCustodianApprovalForPermissionIncrease, add an extra - // message for supervised users. crbug.com/461261 + // TODO(crbug.com/461261): If NeedCustodianApprovalForPermissionIncrease, + // add an extra message for supervised users. messages.push_back( l10n_util::GetStringUTF16(IDS_EXTENSION_DISABLED_ERROR_LABEL)); } @@ -233,8 +233,8 @@ base::string16 ExtensionDisabledGlobalError::GetBubbleViewAcceptButtonLabel() { if (util::IsExtensionSupervised(extension_, service_->profile())) { - // TODO(treib): Probably use a new string here once we get UX design. - // For now, just use "OK". crbug.com/461261 + // TODO(crbug.com/461261): Probably use a new string here once we get UX + // design. For now, just use "OK". return l10n_util::GetStringUTF16(IDS_OK); } if (is_remote_install_) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index ac135e5..addaf52 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -11,7 +11,8 @@ // used as a primary key. The value is a string. // // owners: the person(s) or team(s) responsible for this flag. The value is a -// nonempty list of strings, each of which is either: +// nonempty list of strings, in order of specificity (i.e., the first entry +// on the list is the best contact). Each entry is either: // // - A string containing '@', which is treated as an email address; // - A string beginning with '//', which is treated as a path to a file @@ -1576,6 +1577,11 @@ "expiry_milestone": 76 }, { + "name": "enable-save-data", + "owners": [ "//components/data_reduction_proxy/OWNERS" ], + "expiry_milestone": 77 + }, + { "name": "enable-scroll-anchor-serialization", "owners": [ "pnoland" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 09e6a5a0..16443a5 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -513,6 +513,11 @@ const char kEnableDataSaverLiteModeRebrandDescription[] = "Enable the Data Saver rebranding to Lite Mode."; +const char kEnableSaveDataName[] = "Enables save data feature"; +const char kEnableSaveDataDescription[] = + "Enables save data feature. May cause user's traffic to be proxied via " + "Google's data reduction proxy."; + const char kEnableClientLoFiName[] = "Client-side Lo-Fi previews"; const char kEnableClientLoFiDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index bf81bec..898d349 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -331,6 +331,9 @@ extern const char kEnableDataSaverLiteModeRebrandName[]; extern const char kEnableDataSaverLiteModeRebrandDescription[]; +extern const char kEnableSaveDataName[]; +extern const char kEnableSaveDataDescription[]; + extern const char kEnableClientLoFiName[]; extern const char kEnableClientLoFiDescription[];
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 249a433..e555b0c 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -1511,28 +1511,6 @@ NavigateToDestURL(); } -// Prerenders a page that contains an automatic download triggered through an -// iframe. This should not prerender successfully. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderDownloadIframe) { - PrerenderTestURL("/prerender/prerender_download_iframe.html", - FINAL_STATUS_DOWNLOAD, 0); -} - -// Prerenders a page that contains an automatic download triggered through -// Javascript changing the window.location. This should not prerender -// successfully -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderDownloadLocation) { - PrerenderTestURL(CreateClientRedirect("/download-test1.lib"), - FINAL_STATUS_DOWNLOAD, 1); -} - -// Prerenders a page that contains an automatic download triggered through a -// client-issued redirect. This should not prerender successfully. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderDownloadClientRedirect) { - PrerenderTestURL("/prerender/prerender_download_refresh.html", - FINAL_STATUS_DOWNLOAD, 1); -} - // Checks that the referrer is set when prerendering. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderReferrer) { PrerenderTestURL("/prerender/prerender_referrer.html", FINAL_STATUS_USED, 1); @@ -1915,12 +1893,6 @@ NavigateToDestURL(); } -// Checks that a prerender of a CRX will result in a cancellation due to -// download. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCrx) { - PrerenderTestURL("/prerender/extension.crx", FINAL_STATUS_DOWNLOAD, 0); -} - // Checks that xhr GET requests allow prerenders. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderXhrGet) { PrerenderTestURL("/prerender/prerender_xhr_get.html", FINAL_STATUS_USED, 1); @@ -2599,16 +2571,6 @@ NavigateToDestURL(); } -// Checks that non-http/https main page redirects cancel the prerender. -IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, - PrerenderCancelMainFrameRedirectUnsupportedScheme) { - // Disable load event checks because they race with cancellation. - DisableLoadEventCheck(); - GURL url = embedded_test_server()->GetURL( - CreateServerRedirect("invalidscheme://www.google.com/test.html")); - PrerenderTestURL(url, FINAL_STATUS_UNSUPPORTED_SCHEME, 0); -} - // Checks that media source video loads are deferred on prerendering. IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderHTML5MediaSourceVideo) { PrerenderTestURL("/prerender/prerender_html5_video_media_source.html",
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc index e6799b4..19fda2f8 100644 --- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc +++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -61,6 +61,11 @@ const char kExpectedPurposeHeaderOnPrefetch[] = "Purpose"; +std::string CreateServerRedirect(const std::string& dest_url) { + const char* const kServerRedirectBase = "/server-redirect?"; + return kServerRedirectBase + net::EscapeQueryParamValue(dest_url, false); +} + } // namespace namespace prerender { @@ -86,6 +91,7 @@ "/prerender/prefetch_response_csp.html"; const char kPrefetchScript[] = "/prerender/prefetch.js"; const char kPrefetchScript2[] = "/prerender/prefetch2.js"; +const char kPrefetchDownloadFile[] = "/download-test1.lib"; const char kPrefetchSubresourceRedirectPage[] = "/prerender/prefetch_subresource_redirect.html"; const char kServiceWorkerLoader[] = "/prerender/service_worker.html"; @@ -512,12 +518,20 @@ // Checks that a 301 redirect is followed. IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301Redirect) { - PrefetchFromFile( - "/server-redirect/?" + net::EscapeQueryParamValue(kPrefetchPage, false), - FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); + PrefetchFromFile(CreateServerRedirect(kPrefetchPage), + FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1); } +// Checks that non-HTTP(S) main resource redirects are marked as unsupported +// scheme. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, + PrefetchRedirectUnsupportedScheme) { + PrefetchFromFile( + CreateServerRedirect("invalidscheme://www.google.com/test.html"), + FINAL_STATUS_UNSUPPORTED_SCHEME); +} + // Checks that a 302 redirect is followed. IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch302Redirect) { PrefetchFromFile(k302RedirectPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); @@ -527,8 +541,7 @@ // Checks that the load flags are set correctly for all resources in a 301 // redirect chain. IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301LoadFlags) { - std::string redirect_path = - "/server-redirect/?" + net::EscapeQueryParamValue(kPrefetchPage, false); + std::string redirect_path = CreateServerRedirect(kPrefetchPage); GURL redirect_url = src_server()->GetURL(redirect_path); GURL page_url = src_server()->GetURL(kPrefetchPage); content::URLLoaderInterceptor interceptor(base::BindRepeating( @@ -559,13 +572,40 @@ FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); ui_test_utils::NavigateToURL(current_browser(), src_server()->GetURL(kPrefetchPage2)); - // A complete load of kPrefetchPage2 is used as a sentinal. Otherwise the test + // A complete load of kPrefetchPage2 is used as a sentinel. Otherwise the test // ends before script_counter would reliably see the load of kPrefetchScript, // were it to happen. WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1); WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0); } +// Prefetches a page that contains an automatic download triggered through an +// iframe. The request to download should not reach the server. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchDownloadIframe) { + PrefetchFromFile("/prerender/prerender_download_iframe.html", + FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); + // A complete load of kPrefetchPage2 is used as a sentinel as in test + // |PrefetchClientRedirect| above. + WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1); + WaitForRequestCount(src_server()->GetURL(kPrefetchDownloadFile), 0); +} + +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, + PrefetchDownloadViaClientRedirect) { + PrefetchFromFile("/prerender/prerender_download_refresh.html", + FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); + // A complete load of kPrefetchPage2 is used as a sentinel as in test + // |PrefetchClientRedirect| above. + WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1); + WaitForRequestCount(src_server()->GetURL(kPrefetchDownloadFile), 0); +} + +// Checks that a prefetch of a CRX will result in a cancellation due to +// download. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchCrx) { + PrefetchFromFile("/prerender/extension.crx", FINAL_STATUS_DOWNLOAD); +} + IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchHttps) { UseHttpsSrcServer(); PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc index defcbd10..2fc1cded 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
@@ -40,7 +40,7 @@ constexpr char kSRTPromptTrial[] = "SRTPromptFieldTrial"; const base::Feature kRebootPromptDialogFeature{ - "RebootPromptDialog", base::FEATURE_DISABLED_BY_DEFAULT}; + "RebootPromptDialog", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kChromeCleanupDistributionFeature{ "ChromeCleanupDistribution", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -111,7 +111,7 @@ return REBOOT_PROMPT_TYPE_OPEN_SETTINGS_PAGE; if (base::GetFieldTrialParamByFeatureAsBool(kRebootPromptDialogFeature, kIsModalParam, - /*default_value=*/false)) { + /*default_value=*/true)) { return REBOOT_PROMPT_TYPE_SHOW_MODAL_DIALOG; } else { return REBOOT_PROMPT_TYPE_SHOW_NON_MODAL_DIALOG;
diff --git a/chrome/browser/sync/test/integration/multi_client_status_change_checker.cc b/chrome/browser/sync/test/integration/multi_client_status_change_checker.cc index 7bade45..98f49f0b 100644 --- a/chrome/browser/sync/test/integration/multi_client_status_change_checker.cc +++ b/chrome/browser/sync/test/integration/multi_client_status_change_checker.cc
@@ -20,9 +20,3 @@ void MultiClientStatusChangeChecker::OnStateChanged(syncer::SyncService* sync) { CheckExitCondition(); } - -base::TimeDelta MultiClientStatusChangeChecker::GetTimeoutDuration() { - // TODO(crbug.com/802025): This increased timeout seems to have become - // necessary with kSyncUSSTypedURL. We should figure out why. - return base::TimeDelta::FromSeconds(90); -}
diff --git a/chrome/browser/sync/test/integration/multi_client_status_change_checker.h b/chrome/browser/sync/test/integration/multi_client_status_change_checker.h index 26e5260..97b98d4 100644 --- a/chrome/browser/sync/test/integration/multi_client_status_change_checker.h +++ b/chrome/browser/sync/test/integration/multi_client_status_change_checker.h
@@ -10,7 +10,6 @@ #include "base/compiler_specific.h" #include "base/scoped_observer.h" -#include "base/time/time.h" #include "chrome/browser/sync/test/integration/status_change_checker.h" #include "components/sync/driver/sync_service_observer.h" @@ -39,8 +38,6 @@ std::string GetDebugMessage() const override = 0; protected: - base::TimeDelta GetTimeoutDuration() override; - const std::vector<browser_sync::ProfileSyncService*>& services() { return services_; }
diff --git a/chrome/browser/sync/test/integration/status_change_checker.cc b/chrome/browser/sync/test/integration/status_change_checker.cc index 78fd447c..596970cb 100644 --- a/chrome/browser/sync/test/integration/status_change_checker.cc +++ b/chrome/browser/sync/test/integration/status_change_checker.cc
@@ -8,6 +8,13 @@ #include "base/logging.h" #include "base/run_loop.h" #include "base/timer/timer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +constexpr base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(30); + +} // namespace StatusChangeChecker::StatusChangeChecker() : run_loop_(base::RunLoop::Type::kNestableTasksAllowed), @@ -29,10 +36,6 @@ return timed_out_; } -base::TimeDelta StatusChangeChecker::GetTimeoutDuration() { - return base::TimeDelta::FromSeconds(45); -} - void StatusChangeChecker::StopWaiting() { if (run_loop_.running()) run_loop_.Quit(); @@ -50,7 +53,7 @@ DCHECK(!run_loop_.running()); base::OneShotTimer timer; - timer.Start(FROM_HERE, GetTimeoutDuration(), + timer.Start(FROM_HERE, kTimeout, base::BindRepeating(&StatusChangeChecker::OnTimeout, base::Unretained(this))); @@ -58,7 +61,7 @@ } void StatusChangeChecker::OnTimeout() { - DVLOG(1) << "Await -> Timed out: " << GetDebugMessage(); + ADD_FAILURE() << "Await -> Timed out: " << GetDebugMessage(); timed_out_ = true; StopWaiting(); }
diff --git a/chrome/browser/sync/test/integration/status_change_checker.h b/chrome/browser/sync/test/integration/status_change_checker.h index 71896dde..01017ba 100644 --- a/chrome/browser/sync/test/integration/status_change_checker.h +++ b/chrome/browser/sync/test/integration/status_change_checker.h
@@ -43,9 +43,6 @@ protected: virtual ~StatusChangeChecker(); - // Timeout length when blocking. - virtual base::TimeDelta GetTimeoutDuration(); - // Stop the nested running of the message loop started in StartBlockingWait(). void StopWaiting();
diff --git a/chrome/browser/ui/ash/tablet_mode_client_test_util.cc b/chrome/browser/ui/ash/tablet_mode_client_test_util.cc index 19131ed1..94f5945 100644 --- a/chrome/browser/ui/ash/tablet_mode_client_test_util.cc +++ b/chrome/browser/ui/ash/tablet_mode_client_test_util.cc
@@ -44,9 +44,9 @@ } // namespace -// Enables or disables the tablet mode and waits to until the change has made -// its way back into Chrome (from Ash). Should only be called to toggle the -// current mode. +// Enables or disables the tablet mode and waits until the change has made its +// way back into Chrome (from Ash). Should only be called to toggle the current +// mode. void SetAndWaitForTabletMode(bool enabled) { ASSERT_NE(enabled, TabletModeClient::Get()->tablet_mode_enabled());
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc index bead9066..15f3efa1 100644 --- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc +++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -329,7 +329,8 @@ } std::string HostedAppBrowserController::GetAppShortName() const { - return GetExtension()->short_name(); + const Extension* extension = GetExtension(); + return extension ? extension->short_name() : std::string(); } base::string16 HostedAppBrowserController::GetFormattedUrlOrigin() const {
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc index b8e5e35..182a3db6 100644 --- a/chrome/browser/ui/startup/bad_flags_prompt.cc +++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -22,6 +22,7 @@ #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/autofill/core/common/autofill_switches.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" #include "components/infobars/core/infobar_delegate.h" #include "components/infobars/core/simple_alert_infobar_delegate.h" #include "components/invalidation/impl/invalidation_switches.h" @@ -121,6 +122,10 @@ // UI websites can scan for bluetooth without user intervention. Show a // warning until the UI is complete. switches::kEnableWebBluetoothScanning, + + // Enables save data feature which can cause user traffic to be proxied via + // Google's data reduction proxy servers. + data_reduction_proxy::switches::kEnableDataReductionProxy, }; #endif // OS_ANDROID
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc index e7ef893..c92b6b5 100644 --- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc +++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc
@@ -20,7 +20,6 @@ #include "extensions/browser/app_window/app_window.h" #include "extensions/test/extension_test_message_listener.h" #include "ui/aura/window.h" -#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_types.h" #include "ui/views/view_observer.h" #include "ui/wm/core/window_util.h" @@ -30,7 +29,7 @@ class ViewBoundsChangeWaiter : public views::ViewObserver { public: static void VerifyY(views::View* view, int y) { - if (features::IsMultiProcessMash() && y != view->bounds().y()) + if (y != view->bounds().y()) ViewBoundsChangeWaiter(view).run_loop_.Run(); EXPECT_EQ(y, view->bounds().y());
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc index bf28dc82..3a550d0 100644 --- a/chrome/browser/ui/views/tabs/tab.cc +++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -694,10 +694,8 @@ if (old.pinned != data_.pinned) showing_alert_indicator_ = false; - if (data_.alert_state != old.alert_state || data_.title != old.title) { + if (data_.alert_state != old.alert_state || data_.title != old.title) TooltipTextChanged(); - controller_->UpdateHoverCard(this, mouse_hovered_); - } Layout(); SchedulePaint();
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 5821f1a..10039e9e 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -668,6 +668,9 @@ const bool pinned_state_changed = tab->data().pinned != data.pinned; tab->SetData(std::move(data)); + if (HoverCardIsShowingForTab(tab)) + UpdateHoverCard(tab, true); + if (pinned_state_changed) { if (touch_layout_) { int pinned_tab_count = 0; @@ -2718,6 +2721,14 @@ extra_vertical_space / 2, kHorizontalInset, 0, kHorizontalInset))); } +bool TabStrip::HoverCardIsShowingForTab(Tab* tab) { + if (!base::FeatureList::IsEnabled(features::kTabHoverCards)) + return false; + + return hover_card_ && hover_card_->GetWidget()->IsVisible() && + hover_card_->GetAnchorView() == tab; +} + void TabStrip::ButtonPressed(views::Button* sender, const ui::Event& event) { if (sender == new_tab_button_) { base::RecordAction(base::UserMetricsAction("NewTab_Button"));
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index f8e2519..8b58c6d7 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -613,6 +613,9 @@ // whenever any input of the computation of the border's sizing changes. void UpdateNewTabButtonBorder(); + // Returns true if the hover card is showing for the given tab. + bool HoverCardIsShowingForTab(Tab* tab); + // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/task_manager_view.cc b/chrome/browser/ui/views/task_manager_view.cc index 0a3a2ba..3e687e1 100644 --- a/chrome/browser/ui/views/task_manager_view.cc +++ b/chrome/browser/ui/views/task_manager_view.cc
@@ -339,6 +339,7 @@ table_model_.reset(new TaskManagerTableModel(this)); tab_table->SetModel(table_model_.get()); tab_table->SetGrouper(this); + tab_table->set_sort_on_paint(true); tab_table->set_observer(this); tab_table->set_context_menu_controller(this); set_context_menu_controller(this);
diff --git a/chrome/common/client_hints/OWNERS b/chrome/common/client_hints/OWNERS index 6ebcbe0..7ad3493f 100644 --- a/chrome/common/client_hints/OWNERS +++ b/chrome/common/client_hints/OWNERS
@@ -1,3 +1,4 @@ +yoavweiss@chromium.org tbansal@chromium.org ryansturm@chromium.org
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index acb917f..baf8d3f 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -672,7 +672,7 @@ {"hosts6", false}, // http://a.com -> http://a.com + http://a.co.uk {"permissions1", false}, // tabs -> tabs {"permissions2", true}, // tabs -> tabs,bookmarks - // TODO(treib): This is wrong, kAllHosts implies kTabs. crbug.com/512344 + // TODO(crbug.com/512344): This is wrong, kAllHosts implies kTabs. {"permissions3", true}, // http://*/* -> http://*/*,tabs {"permissions5", true}, // bookmarks -> bookmarks,history {"equivalent_warnings", false}, // tabs --> tabs, webNavigation
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java index a9faf5d..53cf717 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
@@ -8,7 +8,6 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.content.SharedPreferences; import android.support.v4.content.ContextCompat; import org.chromium.content_public.browser.test.util.Criteria; @@ -50,8 +49,6 @@ @SuppressLint({"ApplySharedPref", "CommitPrefEdits"}) @Override public boolean isSatisfied() { - SharedPreferences multidexPrefs = - targetContext.getSharedPreferences("multidex.version", 0); if (!mDataRemoved && !removeAppData(targetContext)) { return false; } @@ -60,11 +57,6 @@ // will try to create it otherwise and will fail for sandbox processes with // a NullPointerException. File cacheDir = new File(ContextCompat.getDataDir(targetContext), "cache"); - // Removing app data cleared out all shared prefs. Multidex uses shared - // prefs to cache hashes of the secondary dexes it has extracted; without - // them, it'll attempt to reextract the dexes the next time the tests - // start up. - multidexPrefs.edit().commit(); return cacheDir.exists() || cacheDir.mkdir(); } }, @@ -74,8 +66,6 @@ /** * Remove all files and directories under the given application directory, except 'lib'. * - * @param appDir the application directory to remove. - * * @return whether removal succeeded. */ private static boolean removeAppData(final Context targetContext) { @@ -84,15 +74,38 @@ File[] files = dataDir.listFiles(); if (files == null) return true; for (File file : files) { - if (!(file.getName().equals("lib") || file.getName().equals("incremental-install-files") - || file.getName().equals(codeCacheDir.getName())) - && !removeFile(file)) { + // Symlink to app's native libraries. + if (file.getName().equals("lib")) { + continue; + } + if (file.getName().equals("incremental-install-files")) { + continue; + } + if (file.getName().equals(codeCacheDir.getName())) { + continue; + } + // SharedPreferences are cached in memory, so clearing their files doesn't help anyways. + // Some preferences need to persist (e.g. multidex.version.xml). + if (file.getName().equals("shared_prefs")) { + removeSharedPrefs(file); + continue; + } + if (!removeFile(file)) { return false; } } return true; } + // TODO(agrieve): Use InMemorySharedPrefs rather than having to delete from disk. + private static void removeSharedPrefs(File sharedPrefsDir) { + for (File f : sharedPrefsDir.listFiles()) { + if (!f.getName().endsWith("multidex.version.xml")) { + f.delete(); + } + } + } + /** * Remove the given file or directory. *
diff --git a/chrome/test/data/prerender/prerender_download_iframe.html b/chrome/test/data/prerender/prerender_download_iframe.html index 9b31b551..66363db 100644 --- a/chrome/test/data/prerender/prerender_download_iframe.html +++ b/chrome/test/data/prerender/prerender_download_iframe.html
@@ -3,5 +3,6 @@ <body> <iframe name="download" src="../download-test1.lib" width="0" height="0" frameborder="0"></iframe> + <script src="prefetch2.js" type="text/javascript"></script> </body> </html>
diff --git a/chrome/test/data/prerender/prerender_download_refresh.html b/chrome/test/data/prerender/prerender_download_refresh.html index aea05e3..a8735bba 100644 --- a/chrome/test/data/prerender/prerender_download_refresh.html +++ b/chrome/test/data/prerender/prerender_download_refresh.html
@@ -2,5 +2,6 @@ <head><title>Prerender download test - refresh</title> <meta http-equiv="refresh" content="1;url=../download-test1.lib"/> </head> + <script src="prefetch2.js" type="text/javascript"></script> <body></body> </html>
diff --git a/components/autofill_assistant/browser/batch_element_checker.cc b/components/autofill_assistant/browser/batch_element_checker.cc index 9fbb8c5..b8f48b521 100644 --- a/components/autofill_assistant/browser/batch_element_checker.cc +++ b/components/autofill_assistant/browser/batch_element_checker.cc
@@ -84,7 +84,7 @@ const auto& call_arguments = entry.first; web_controller_->ElementCheck( - call_arguments.first, call_arguments.second, + call_arguments.first, call_arguments.second, /* strict= */ false, base::BindOnce( &BatchElementChecker::OnElementChecked, weak_ptr_factory_.GetWeakPtr(),
diff --git a/components/autofill_assistant/browser/mock_web_controller.h b/components/autofill_assistant/browser/mock_web_controller.h index 2c42fe9..e2d446a 100644 --- a/components/autofill_assistant/browser/mock_web_controller.h +++ b/components/autofill_assistant/browser/mock_web_controller.h
@@ -44,6 +44,7 @@ void ElementCheck(ElementCheckType check_type, const Selector& selector, + bool strict, base::OnceCallback<void(bool)> callback) override { OnElementCheck(check_type, selector, callback); }
diff --git a/components/autofill_assistant/browser/selector.cc b/components/autofill_assistant/browser/selector.cc index 6e41fa6..2b2ada2 100644 --- a/components/autofill_assistant/browser/selector.cc +++ b/components/autofill_assistant/browser/selector.cc
@@ -43,12 +43,75 @@ return this->selectors.empty(); } +std::ostream& operator<<(std::ostream& out, PseudoType pseudo_type) { +#ifdef NDEBUG + return out << static_cast<int>(pseudo_type); +#else + switch (pseudo_type) { + case UNDEFINED: + out << "UNDEFINED"; + break; + case FIRST_LINE: + out << "FIRST_LINE"; + break; + case FIRST_LETTER: + out << "FIRST_LETTER"; + break; + case BEFORE: + out << "BEFORE"; + break; + case AFTER: + out << "AFTER"; + break; + case BACKDROP: + out << "BACKDROP"; + break; + case SELECTION: + out << "SELECTION"; + break; + case FIRST_LINE_INHERITED: + out << "FIRST_LINE_INHERITED"; + break; + case SCROLLBAR: + out << "SCROLLBAR"; + break; + case SCROLLBAR_THUMB: + out << "SCROLLBAR_THUMB"; + break; + case SCROLLBAR_BUTTON: + out << "SCROLLBAR_BUTTON"; + break; + case SCROLLBAR_TRACK: + out << "SCROLLBAR_TRACK"; + break; + case SCROLLBAR_TRACK_PIECE: + out << "SCROLLBAR_TRACK_PIECE"; + break; + case SCROLLBAR_CORNER: + out << "SCROLLBAR_CORNER"; + break; + case RESIZER: + out << "RESIZER"; + break; + case INPUT_LIST_BUTTON: + out << "INPUT_LIST_BUTTON"; + break; + + // Intentionally no default case to make compilation fail if a new value + // was added to the enum but not to this list. + } + return out; +#endif +} std::ostream& operator<<(std::ostream& out, const Selector& selector) { #ifdef NDEBUG out << selector.selectors.size() << " element(s)"; return out; #else out << "elements=[" << base::JoinString(selector.selectors, ",") << "]"; + if (selector.pseudo_type != PseudoType::UNDEFINED) { + out << "::" << selector.pseudo_type; + } return out; #endif // NDEBUG }
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc index cad7111..90dd094 100644 --- a/components/autofill_assistant/browser/web_controller.cc +++ b/components/autofill_assistant/browser/web_controller.cc
@@ -142,8 +142,9 @@ }()) )"; -// Javascript code to query all elements for a selector. -const char* const kQuerySelectorAll = +// Javascript code to query an elements for a selector, either the first +// (non-strict) or a single (strict) element. +const char* const kQuerySelector = R"(function (selector, strictMode) { var found = this.querySelectorAll(selector); if(found.length == 1) @@ -153,6 +154,22 @@ return undefined; })"; +// Javascript code to query a visible elements for a selector, either the first +// (non-strict) or a single (strict) visible element.q +const char* const kQuerySelectorVisible = + R"(function (selector, strict) { + var found = this.querySelectorAll(selector); + var found_index = -1; + for (let i = 0; i < found.length; i++) { + if (found[i].getClientRects().length > 0) { + if (found_index != -1) return undefined; + found_index = i; + if (!strict) break; + } + } + return found_index == -1 ? undefined : found[found_index]; + })"; + // Javascript code to query whether the document is ready for interact. const char* const kIsDocumentReadyForInteract = R"(function () { @@ -412,6 +429,7 @@ ElementFinder(content::WebContents* web_contents_, DevtoolsClient* devtools_client, const Selector& selector, + ElementCheckType check_type, bool strict); ~ElementFinder() override; @@ -442,6 +460,7 @@ content::WebContents* const web_contents_; DevtoolsClient* const devtools_client_; const Selector selector_; + const ElementCheckType check_type_; const bool strict_; FindElementCallback callback_; std::unique_ptr<FindElementResult> element_result_; @@ -452,10 +471,12 @@ WebController::ElementFinder::ElementFinder(content::WebContents* web_contents, DevtoolsClient* devtools_client, const Selector& selector, + ElementCheckType check_type, bool strict) : web_contents_(web_contents), devtools_client_(devtools_client), selector_(selector), + check_type_(check_type), strict_(strict), element_result_(std::make_unique<FindElementResult>()), weak_ptr_factory_(this) {} @@ -499,15 +520,26 @@ .SetValue(base::Value::ToUniquePtrValue( base::Value(selector_.selectors[index]))) .Build()); + // For finding intermediate elements, strict mode would be more appropriate, + // as long as the logic does not support more than one intermediate match. + // + // TODO(b/129387787): first, add logging to figure out whether it matters and + // decide between strict mode and full support for multiple matching + // intermeditate elements. argument.emplace_back( runtime::CallArgument::Builder() .SetValue(base::Value::ToUniquePtrValue(base::Value(strict_))) .Build()); + const char* function = (index == (selector_.selectors.size() - 1) && + selector_.pseudo_type == PseudoType::UNDEFINED && + check_type_ == kVisibilityCheck) + ? kQuerySelectorVisible + : kQuerySelector; devtools_client_->GetRuntime()->CallFunctionOn( runtime::CallFunctionOnParams::Builder() .SetObjectId(object_id) .SetArguments(std::move(argument)) - .SetFunctionDeclaration(std::string(kQuerySelectorAll)) + .SetFunctionDeclaration(function) .Build(), base::BindOnce(&WebController::ElementFinder::OnQuerySelectorAll, weak_ptr_factory_.GetWeakPtr(), index)); @@ -736,7 +768,8 @@ const Selector& selector, base::OnceCallback<void(const ClientStatus&)> callback) { DCHECK(!selector.empty()); - FindElement(selector, /* strict_mode= */ true, + FindElement(selector, kVisibilityCheck, + /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForClickOrTap, weak_ptr_factory_.GetWeakPtr(), std::move(callback), /* is_a_click= */ true)); @@ -746,7 +779,8 @@ const Selector& selector, base::OnceCallback<void(const ClientStatus&)> callback) { DCHECK(!selector.empty()); - FindElement(selector, /* strict_mode= */ true, + FindElement(selector, kVisibilityCheck, + /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForClickOrTap, weak_ptr_factory_.GetWeakPtr(), std::move(callback), /* is_a_click= */ false)); @@ -931,49 +965,28 @@ void WebController::ElementCheck(ElementCheckType check_type, const Selector& selector, + bool strict, base::OnceCallback<void(bool)> callback) { DCHECK(!selector.empty()); - // We don't use strict_mode because we only check for the existence of at - // least one such element and we don't act on it. - FindElement(selector, /* strict_mode= */ false, - base::BindOnce(&WebController::OnFindElementForCheck, - weak_ptr_factory_.GetWeakPtr(), check_type, - std::move(callback))); -} - -void WebController::OnFindElementForCheck( - ElementCheckType check_type, - base::OnceCallback<void(bool)> callback, - const ClientStatus& status, - std::unique_ptr<FindElementResult> result) { - if (!status.ok()) { - std::move(callback).Run(false); - return; - } - if (check_type == kExistenceCheck) { - std::move(callback).Run(true); - return; - } - DCHECK_EQ(check_type, kVisibilityCheck); - - devtools_client_->GetDOM()->GetBoxModel( - dom::GetBoxModelParams::Builder().SetObjectId(result->object_id).Build(), - base::BindOnce(&WebController::OnGetBoxModelForVisible, + FindElement( + selector, check_type, strict, + base::BindOnce(&WebController::OnFindElementForCheck, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void WebController::OnGetBoxModelForVisible( +void WebController::OnFindElementForCheck( base::OnceCallback<void(bool)> callback, - std::unique_ptr<dom::GetBoxModelResult> result) { - std::move(callback).Run(result && result->GetModel() && - result->GetModel()->GetContent()); + const ClientStatus& status, + std::unique_ptr<FindElementResult> result) { + std::move(callback).Run(status.ok()); } void WebController::FindElement(const Selector& selector, + ElementCheckType check_type, bool strict_mode, FindElementCallback callback) { auto finder = std::make_unique<ElementFinder>( - web_contents_, devtools_client_.get(), selector, strict_mode); + web_contents_, devtools_client_.get(), selector, check_type, strict_mode); ElementFinder* ptr = finder.get(); pending_workers_[ptr] = std::move(finder); ptr->Start(base::BindOnce(&WebController::OnFindElementResult, @@ -1051,7 +1064,7 @@ auto data_to_autofill = std::make_unique<FillFormInputData>(); data_to_autofill->profile = std::make_unique<autofill::AutofillProfile>(*profile); - FindElement(selector, + FindElement(selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForFillingForm, weak_ptr_factory_.GetWeakPtr(), @@ -1126,7 +1139,7 @@ auto data_to_autofill = std::make_unique<FillFormInputData>(); data_to_autofill->card = std::move(card); data_to_autofill->cvc = cvc; - FindElement(selector, + FindElement(selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForFillingForm, weak_ptr_factory_.GetWeakPtr(), @@ -1139,7 +1152,7 @@ const std::string& selected_option, base::OnceCallback<void(const ClientStatus&)> callback) { DVLOG(3) << __func__ << " " << selector << ", option=" << selected_option; - FindElement(selector, + FindElement(selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForSelectOption, weak_ptr_factory_.GetWeakPtr(), selected_option, @@ -1191,7 +1204,7 @@ base::OnceCallback<void(const ClientStatus&)> callback) { DVLOG(3) << __func__ << " " << selector; FindElement( - selector, + selector, kVisibilityCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForHighlightElement, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); @@ -1241,8 +1254,8 @@ DVLOG(3) << __func__ << " " << selector; DCHECK(!selector.empty()); FindElement( - selector, - /* strict_mode= */ true, + selector, kVisibilityCheck, + /* strict_mode= */ false, base::BindOnce(&WebController::OnFindElementForFocusElement, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } @@ -1251,7 +1264,7 @@ const Selector& selector, base::OnceCallback<void(bool, const std::string&)> callback) { FindElement( - selector, + selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForGetFieldValue, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); @@ -1332,7 +1345,7 @@ const Selector& selector, const std::string& value, base::OnceCallback<void(const ClientStatus&)> callback) { - FindElement(selector, + FindElement(selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForSetFieldValue, weak_ptr_factory_.GetWeakPtr(), value, @@ -1452,7 +1465,7 @@ DCHECK(!selector.empty()); DCHECK_GT(attribute.size(), 0u); - FindElement(selector, + FindElement(selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForSetAttribute, weak_ptr_factory_.GetWeakPtr(), attribute, value, @@ -1509,7 +1522,7 @@ DVLOG(3) << __func__ << " " << selector << ", input=" << base::JoinString(utf8_chars, ""); DCHECK(!selector.empty()); - FindElement(selector, + FindElement(selector, kVisibilityCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForSendKeyboardInput, weak_ptr_factory_.GetWeakPtr(), selector, @@ -1538,7 +1551,7 @@ callback) { DVLOG(3) << __func__ << " " << selector; FindElement( - selector, + selector, kExistenceCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForGetOuterHtml, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); @@ -1553,7 +1566,7 @@ const Selector& selector, base::OnceCallback<void(bool, const RectF&)> callback) { FindElement( - selector, /* strict_mode= */ true, + selector, kVisibilityCheck, /* strict_mode= */ true, base::BindOnce(&WebController::OnFindElementForPosition, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); }
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h index 3a603cf..ef52d75 100644 --- a/components/autofill_assistant/browser/web_controller.h +++ b/components/autofill_assistant/browser/web_controller.h
@@ -166,9 +166,12 @@ // kVisibilityCheck: Checks whether at least on element given by |selector| // is visible on the web page. // + // If strict, there must be exactly one element. + // // Normally done through BatchElementChecker. virtual void ElementCheck(ElementCheckType type, const Selector& selector, + bool strict, base::OnceCallback<void(bool)> callback); // Get the value of |selector| and return the result through |callback|. The @@ -289,17 +292,15 @@ void OnDispatchTouchEventEnd( base::OnceCallback<void(const ClientStatus&)> callback, std::unique_ptr<input::DispatchTouchEventResult> result); - void OnFindElementForCheck(ElementCheckType check_type, - base::OnceCallback<void(bool)> callback, + void OnFindElementForCheck(base::OnceCallback<void(bool)> callback, const ClientStatus& status, std::unique_ptr<FindElementResult> result); - void OnGetBoxModelForVisible(base::OnceCallback<void(bool)> callback, - std::unique_ptr<dom::GetBoxModelResult> result); // Find the element given by |selector|. If multiple elements match // |selector| and if |strict_mode| is false, return the first one that is // found. Otherwise if |strict-mode| is true, do not return any. void FindElement(const Selector& selector, + ElementCheckType check_type, bool strict_mode, FindElementCallback callback); void OnFindElementResult(ElementFinder* finder_to_release,
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc index bb46c82..b447f275 100644 --- a/components/autofill_assistant/browser/web_controller_browsertest.cc +++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -12,11 +12,18 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" namespace autofill_assistant { +using ::testing::IsEmpty; + const char* kTargetWebsitePath = "/autofill_assistant_target_website.html"; +std::ostream& operator<<(std::ostream& out, ElementCheckType check_type) { + return out << (check_type == kVisibilityCheck ? "visibility" : "existence"); +} + class WebControllerBrowserTest : public content::ContentBrowserTest, public content::WebContentsObserver { public: @@ -66,7 +73,29 @@ } while (page_is_loading || paint_occurred_during_last_loop_); } + void RunStrictElementCheck(ElementCheckType check_type, + const Selector& selector, + bool result) { + RunElementCheck(check_type, /* strict= */ true, selector, result); + } + + void RunLaxElementCheck(ElementCheckType check_type, + const Selector& selector, + bool result) { + RunElementCheck(check_type, /* strict= */ false, selector, result); + } + + void RunElementCheck(ElementCheckType check_type, + bool strict, + const Selector& selector, + bool result) { + std::vector<Selector> selectors{selector}; + std::vector<bool> results{result}; + RunElementChecks(check_type, strict, selectors, results); + } + void RunElementChecks(ElementCheckType check_type, + bool strict, const std::vector<Selector>& selectors, const std::vector<bool> results) { base::RunLoop run_loop; @@ -74,19 +103,23 @@ size_t pending_number_of_checks = selectors.size(); for (size_t i = 0; i < selectors.size(); i++) { web_controller_->ElementCheck( - check_type, selectors[i], + check_type, selectors[i], strict, base::BindOnce(&WebControllerBrowserTest::CheckElementVisibleCallback, base::Unretained(this), run_loop.QuitClosure(), - &pending_number_of_checks, results[i])); + selectors[i], check_type, &pending_number_of_checks, + results[i])); } run_loop.Run(); } void CheckElementVisibleCallback(const base::Closure& done_callback, + const Selector& selector, + ElementCheckType check_type, size_t* pending_number_of_checks_output, bool expected_result, bool result) { - ASSERT_EQ(expected_result, result); + EXPECT_EQ(expected_result, result) + << "selector: " << selector << " " << check_type; *pending_number_of_checks_output -= 1; if (*pending_number_of_checks_output == 0) { done_callback.Run(); @@ -125,7 +158,7 @@ void WaitForElementRemove(const Selector& selector) { base::RunLoop run_loop; web_controller_->ElementCheck( - kExistenceCheck, selector, + kExistenceCheck, selector, /* strict= */ false, base::BindOnce(&WebControllerBrowserTest::OnWaitForElementRemove, base::Unretained(this), run_loop.QuitClosure(), selector)); @@ -217,35 +250,68 @@ done_callback.Run(); } - void FindElement(const Selector& selector, - size_t expected_index, - bool is_main_frame) { + void FindElement(ElementCheckType check_type, + const Selector& selector, + ClientStatus* status_out, + WebController::FindElementResult* result_out) { base::RunLoop run_loop; web_controller_->FindElement( - selector, - /* strict_mode= */ true, + selector, check_type, /* strict_mode= */ true, base::BindOnce(&WebControllerBrowserTest::OnFindElement, base::Unretained(this), run_loop.QuitClosure(), - expected_index, is_main_frame)); + base::Unretained(status_out), + base::Unretained(result_out))); run_loop.Run(); } void OnFindElement(const base::Closure& done_callback, - size_t expected_index, - bool is_main_frame, + ClientStatus* status_out, + WebController::FindElementResult* result_out, const ClientStatus& status, std::unique_ptr<WebController::FindElementResult> result) { + ASSERT_TRUE(result); done_callback.Run(); + if (status_out) + *status_out = status; + + if (result_out) + *result_out = *result; + } + + void FindElementAndCheck(ElementCheckType check_type, + const Selector& selector, + size_t expected_index, + bool is_main_frame) { + SCOPED_TRACE(::testing::Message() << selector << " strict, " << check_type); + ClientStatus status; + WebController::FindElementResult result; + FindElement(check_type, selector, &status, &result); EXPECT_EQ(ACTION_APPLIED, status.proto_status()); + CheckFindElementResult(result, expected_index, is_main_frame); + } + + void FindElementExpectEmptyResult(ElementCheckType check_type, + const Selector& selector) { + SCOPED_TRACE(::testing::Message() << selector << " strict, " << check_type); + ClientStatus status; + WebController::FindElementResult result; + FindElement(check_type, selector, &status, &result); + EXPECT_EQ(ELEMENT_RESOLUTION_FAILED, status.proto_status()); + EXPECT_THAT(result.object_id, IsEmpty()); + } + + void CheckFindElementResult(const WebController::FindElementResult& result, + size_t expected_index, + bool is_main_frame) { if (is_main_frame) { EXPECT_EQ(shell()->web_contents()->GetMainFrame(), - result->container_frame_host); + result.container_frame_host); } else { EXPECT_NE(shell()->web_contents()->GetMainFrame(), - result->container_frame_host); + result.container_frame_host); } - EXPECT_EQ(result->container_frame_selector_index, expected_index); - EXPECT_FALSE(result->object_id.empty()); + EXPECT_EQ(result.container_frame_selector_index, expected_index); + EXPECT_FALSE(result.object_id.empty()); } void GetFieldsValue(const std::vector<Selector>& selectors, @@ -378,7 +444,72 @@ DISALLOW_COPY_AND_ASSIGN(WebControllerBrowserTest); }; -IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ConcurrentElementsVisible) { +IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExistenceCheck) { + // A visible element + RunLaxElementCheck(kExistenceCheck, Selector({"#button"}), true); + + // A hidden element. + RunLaxElementCheck(kExistenceCheck, Selector({"#hidden"}), true); + + // A nonexistent element. + RunLaxElementCheck(kExistenceCheck, Selector({"#doesnotexist"}), false); + + // A pseudo-element + RunLaxElementCheck(kExistenceCheck, + Selector({"#terms-and-conditions"}, BEFORE), true); + + // An invisible pseudo-element + // + // TODO(b/129461999): This is wrong; it should exist. Fix it. + RunLaxElementCheck(kExistenceCheck, Selector({"#button"}, BEFORE), false); + + // A non-existent pseudo-element + RunLaxElementCheck(kExistenceCheck, Selector({"#button"}, AFTER), false); +} + +IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementVisibilityCheck) { + // A visible element + RunLaxElementCheck(kVisibilityCheck, Selector({"#button"}), true); + + // A hidden element. + RunLaxElementCheck(kVisibilityCheck, Selector({"#hidden"}), false); + + // A non-existent element + RunLaxElementCheck(kVisibilityCheck, Selector({"#doesnotexist"}), false); + + // A pseudo-element + RunLaxElementCheck(kVisibilityCheck, + Selector({"#terms-and-conditions"}, BEFORE), true); + + // An invisible pseudo-element + RunLaxElementCheck(kVisibilityCheck, Selector({"#button"}, BEFORE), false); + + // A non-existent pseudo-element + RunLaxElementCheck(kVisibilityCheck, Selector({"#button"}, AFTER), false); +} + +IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, MultipleVisibleElementCheck) { + // both visible + RunLaxElementCheck(kVisibilityCheck, Selector({"#button,#select"}), true); + RunStrictElementCheck(kVisibilityCheck, Selector({"#button,#select"}), false); + + // one visible (first non-visible) + RunLaxElementCheck(kVisibilityCheck, Selector({"#hidden,#select"}), true); + RunStrictElementCheck(kVisibilityCheck, Selector({"#hidden,#select"}), true); + + // one visible (first visible) + RunLaxElementCheck(kVisibilityCheck, Selector({"#button,#hidden"}), true); + RunStrictElementCheck(kVisibilityCheck, Selector({"#hidden,#select"}), true); + + // one invisible, one non-existent + RunLaxElementCheck(kVisibilityCheck, Selector({"#doesnotexist,#hidden"}), + false); + RunStrictElementCheck(kVisibilityCheck, Selector({"#doesnotexist,#hidden"}), + false); +} + +IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, + ConcurrentElementsVisibilityCheck) { std::vector<Selector> selectors; std::vector<bool> results; @@ -438,33 +569,7 @@ selectors.emplace_back(a_selector); results.emplace_back(false); - RunElementChecks(kVisibilityCheck, selectors, results); -} - -IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ElementExists) { - std::vector<Selector> selectors; - std::vector<bool> results; - - Selector a_selector; - - // A visible element - a_selector.selectors.emplace_back("#button"); - selectors.emplace_back(a_selector); - results.emplace_back(true); - - // A hidden element. - a_selector.selectors.clear(); - a_selector.selectors.emplace_back("#hidden"); - selectors.emplace_back(a_selector); - results.emplace_back(true); - - // A nonexistent element. - a_selector.selectors.clear(); - a_selector.selectors.emplace_back("#doesnotexist"); - selectors.emplace_back(a_selector); - results.emplace_back(false); - - RunElementChecks(kExistenceCheck, selectors, results); + RunElementChecks(kVisibilityCheck, /* strict= */ false, selectors, results); } IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ClickElement) { @@ -544,25 +649,38 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElement) { Selector selector; selector.selectors.emplace_back("#button"); - FindElement(selector, 0, true); + FindElementAndCheck(kExistenceCheck, selector, 0, true); + FindElementAndCheck(kVisibilityCheck, selector, 0, true); // IFrame. selector.selectors.clear(); selector.selectors.emplace_back("#iframe"); selector.selectors.emplace_back("#button"); - FindElement(selector, 0, false); + FindElementAndCheck(kExistenceCheck, selector, 0, false); + FindElementAndCheck(kVisibilityCheck, selector, 0, false); selector.selectors.clear(); selector.selectors.emplace_back("#iframe"); selector.selectors.emplace_back("[name=name]"); - FindElement(selector, 0, false); + FindElementAndCheck(kExistenceCheck, selector, 0, false); + FindElementAndCheck(kVisibilityCheck, selector, 0, false); // IFrame inside IFrame. selector.selectors.clear(); selector.selectors.emplace_back("#iframe"); selector.selectors.emplace_back("#iframe"); selector.selectors.emplace_back("#button"); - FindElement(selector, 1, false); + FindElementAndCheck(kExistenceCheck, selector, 1, false); + FindElementAndCheck(kVisibilityCheck, selector, 1, false); +} + +IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FindElementNotFound) { + FindElementExpectEmptyResult(kExistenceCheck, Selector({"#notfound"})); + FindElementExpectEmptyResult(kVisibilityCheck, Selector({"#hidden"})); + FindElementExpectEmptyResult(kExistenceCheck, + Selector({"#iframe", "#iframe", "#notfound"})); + FindElementExpectEmptyResult(kVisibilityCheck, + Selector({"#iframe", "#iframe", "#hidden"})); } IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FocusElement) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc index 0000693..cd6eade 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -643,227 +643,382 @@ int expected_duration; DataReductionProxyBypassType expected_bypass_type; } tests[] = { - // Valid data reduction proxy response with no bypass message. - { - "GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - false, - false, - 0u, - true, - -1, - BYPASS_EVENT_TYPE_MAX, - }, - // Response error does not result in bypass. - { - "GET", - "Not an HTTP response", - false, - true, - 0u, - true, - -1, - BYPASS_EVENT_TYPE_MAX, - }, - // Valid data reduction proxy response with chained via header, - // no bypass message. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n", - false, false, 0u, true, -1, BYPASS_EVENT_TYPE_MAX}, - // Valid data reduction proxy response with a bypass message. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Valid data reduction proxy response with a bypass message. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=1\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 1, BYPASS_EVENT_TYPE_SHORT}, - // Same as above with the OPTIONS method, which is idempotent. - {"OPTIONS", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Same as above with the HEAD method, which is idempotent. - {"HEAD", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, false, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Same as above with the PUT method, which is idempotent. - {"PUT", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Same as above with the DELETE method, which is idempotent. - {"DELETE", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Same as above with the TRACE method, which is idempotent. - {"TRACE", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // 500 responses should be bypassed. - {"GET", - "HTTP/1.1 500 Internal Server Error\r\n" - "Server: proxy\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, - BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR}, - // 502 responses should be bypassed. - {"GET", - "HTTP/1.1 502 Internal Server Error\r\n" - "Server: proxy\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY}, - // 503 responses should be bypassed. - {"GET", - "HTTP/1.1 503 Internal Server Error\r\n" - "Server: proxy\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 0, - BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE}, - // Invalid data reduction proxy 4xx response. Missing Via header. - {"GET", - "HTTP/1.1 404 Not Found\r\n" - "Server: proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX}, - // Invalid data reduction proxy response. Missing Via header. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER}, - // Invalid data reduction proxy response. Wrong Via header. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Via: 1.0 some-other-proxy\r\n\r\n", - true, false, 1u, true, 0, BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER}, - // Valid data reduction proxy response. 304 missing Via header. - {"GET", - "HTTP/1.1 304 Not Modified\r\n" - "Server: proxy\r\n\r\n", - false, false, 0u, false, 0, BYPASS_EVENT_TYPE_MAX}, - // Valid data reduction proxy response with a bypass message. It will - // not be retried because the request is non-idempotent. - {"POST", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=0\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - false, false, 1u, true, 0, BYPASS_EVENT_TYPE_MEDIUM}, - // Valid data reduction proxy response with block message. Both proxies - // should be on the retry list when it completes. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block=1\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 2u, true, 1, BYPASS_EVENT_TYPE_SHORT}, - // Valid data reduction proxy response with a block-once message. It will - // be - // retried, and there will be no proxies on the retry list since - // block-once - // only affects the current request. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Same as above with the OPTIONS method, which is idempotent. - {"OPTIONS", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Same as above with the HEAD method, which is idempotent. - {"HEAD", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, false, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Same as above with the PUT method, which is idempotent. - {"PUT", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Same as above with the DELETE method, which is idempotent. - {"DELETE", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Same as above with the TRACE method, which is idempotent. - {"TRACE", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Valid Data Reduction Proxy response with a block-once message. It will - // be retried because block-once indicates that request did not reach the - // origin and client should retry. Only current request is retried direct, - // so there should be no proxies on the retry list. - {"POST", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 0u, true, 0, BYPASS_EVENT_TYPE_CURRENT}, - // Valid Data Reduction Proxy response with a bypass message. It will - // not be retried because the request is non-idempotent. Both proxies - // should be on the retry list for 1 second. - {"POST", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block=1\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - false, false, 2u, true, 1, BYPASS_EVENT_TYPE_SHORT}, - // Valid data reduction proxy response with block and block-once messages. - // The block message will override the block-once message, so both proxies - // should be on the retry list when it completes. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: block=1, block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 2u, true, 1, BYPASS_EVENT_TYPE_SHORT}, - // Valid data reduction proxy response with bypass and block-once - // messages. - // The bypass message will override the block-once message, so one proxy - // should be on the retry list when it completes. - {"GET", - "HTTP/1.1 200 OK\r\n" - "Server: proxy\r\n" - "Chrome-Proxy: bypass=1, block-once\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, 1u, true, 1, BYPASS_EVENT_TYPE_SHORT}, + // Valid data reduction proxy response with no bypass message. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + false, + false, + 0u, + true, + -1, + BYPASS_EVENT_TYPE_MAX, + }, + // Response error does not result in bypass. + { "GET", + "Not an HTTP response", + false, + true, + 0u, + true, + -1, + BYPASS_EVENT_TYPE_MAX, + }, + // Valid data reduction proxy response with chained via header, + // no bypass message. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n", + false, + false, + 0u, + true, + -1, + BYPASS_EVENT_TYPE_MAX + }, + // Valid data reduction proxy response with a bypass message. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Valid data reduction proxy response with a bypass message. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=1\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, + // Same as above with the OPTIONS method, which is idempotent. + { "OPTIONS", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Same as above with the HEAD method, which is idempotent. + { "HEAD", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + false, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Same as above with the PUT method, which is idempotent. + { "PUT", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Same as above with the DELETE method, which is idempotent. + { "DELETE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Same as above with the TRACE method, which is idempotent. + { "TRACE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // 500 responses should be bypassed. + { "GET", + "HTTP/1.1 500 Internal Server Error\r\n" + "Server: proxy\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR + }, + // 502 responses should be bypassed. + { "GET", + "HTTP/1.1 502 Internal Server Error\r\n" + "Server: proxy\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY + }, + // 503 responses should be bypassed. + { "GET", + "HTTP/1.1 503 Internal Server Error\r\n" + "Server: proxy\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE + }, + // Invalid data reduction proxy 4xx response. Missing Via header. + { "GET", + "HTTP/1.1 404 Not Found\r\n" + "Server: proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX + }, + // Invalid data reduction proxy response. Missing Via header. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER + }, + // Invalid data reduction proxy response. Wrong Via header. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Via: 1.0 some-other-proxy\r\n\r\n", + true, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER + }, + // Valid data reduction proxy response. 304 missing Via header. + { "GET", + "HTTP/1.1 304 Not Modified\r\n" + "Server: proxy\r\n\r\n", + false, + false, + 0u, + false, + 0, + BYPASS_EVENT_TYPE_MAX + }, + // Valid data reduction proxy response with a bypass message. It will + // not be retried because the request is non-idempotent. + { "POST", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=0\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + false, + false, + 1u, + true, + 0, + BYPASS_EVENT_TYPE_MEDIUM + }, + // Valid data reduction proxy response with block message. Both proxies + // should be on the retry list when it completes. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block=1\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 2u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, + // Valid data reduction proxy response with a block-once message. It will be + // retried, and there will be no proxies on the retry list since block-once + // only affects the current request. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the OPTIONS method, which is idempotent. + { "OPTIONS", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the HEAD method, which is idempotent. + { "HEAD", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + false, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the PUT method, which is idempotent. + { "PUT", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the DELETE method, which is idempotent. + { "DELETE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the TRACE method, which is idempotent. + { "TRACE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Valid Data Reduction Proxy response with a block-once message. It will + // be retried because block-once indicates that request did not reach the + // origin and client should retry. Only current request is retried direct, + // so there should be no proxies on the retry list. + { "POST", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Valid Data Reduction Proxy response with a bypass message. It will + // not be retried because the request is non-idempotent. Both proxies + // should be on the retry list for 1 second. + { "POST", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block=1\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + false, + false, + 2u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, + // Valid data reduction proxy response with block and block-once messages. + // The block message will override the block-once message, so both proxies + // should be on the retry list when it completes. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block=1, block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 2u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, + // Valid data reduction proxy response with bypass and block-once messages. + // The bypass message will override the block-once message, so one proxy + // should be on the retry list when it completes. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=1, block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + false, + 1u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, }; test_context_->config()->test_params()->UseNonSecureProxiesForHttp(); std::string primary = test_context_->config() @@ -1017,14 +1172,14 @@ {"HTTP/1.1 502 Bad Gateway\r\n" "Server: proxy\r\n" "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", - true, false, BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY}, + true, true, BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY}, {"HTTP/1.1 200 OK\r\n" "Server: proxy\r\n" "Chrome-Proxy: block=0\r\n\r\n", false, false, BYPASS_EVENT_TYPE_MAX}, {"HTTP/1.1 502 Bad Gateway\r\n" "Server: proxy\r\n\r\n", - true, false, BYPASS_EVENT_TYPE_MAX}, + false, false, BYPASS_EVENT_TYPE_MAX}, }; for (const auto& test : test_cases) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc index 4f929ba..bdb54b35 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
@@ -473,22 +473,6 @@ histogram_tester, "DataReductionProxy.BypassedBytes.ProxyOverridden"); } -TEST_F(DataReductionProxyBypassStatsEndToEndTest, NoChromeProxy502AreBypassed) { - InitializeContext(); - base::HistogramTester histogram_tester; - CreateAndExecuteRequest(GURL("http://foo.com"), net::LOAD_NORMAL, net::OK, - "HTTP/1.1 502 Bad Gateway\r\n" - "Via: 1.1 Chrome-Compression-Proxy\r\n" - "Chrome-Proxy: block-once\r\n\r\n", - kErrorBody.c_str(), "HTTP/1.1 200 OK\r\n\r\n", - kBody.c_str()); - - histogram_tester.ExpectUniqueSample( - "DataReductionProxy.BypassedBytes.Current", kBody.size(), 1); - ExpectOtherBypassedBytesHistogramsEmpty( - histogram_tester, "DataReductionProxy.BypassedBytes.Current"); -} - TEST_F(DataReductionProxyBypassStatsEndToEndTest, BypassedBytesCurrent) { InitializeContext(); struct TestCase { @@ -626,6 +610,9 @@ { "DataReductionProxy.BypassedBytes.Status500HttpInternalServerError", "HTTP/1.1 500 Internal Server Error\r\n\r\n", }, + { "DataReductionProxy.BypassedBytes.Status502HttpBadGateway", + "HTTP/1.1 502 Bad Gateway\r\n\r\n", + }, { "DataReductionProxy.BypassedBytes.Status503HttpServiceUnavailable", "HTTP/1.1 503 Service Unavailable\r\n\r\n", },
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc index 3c234c3..1834dee 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
@@ -46,10 +46,5 @@ "DataReductionProxyEnabledWithNetworkService", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enables block-once action when 502 is received with no Chrome-Proxy header. -const base::Feature kDataReductionProxyBlockOnceOnBadGatewayResponse{ - "DataReductionProxyBlockOnceOnBadGatewayResponse", - base::FEATURE_ENABLED_BY_DEFAULT}; - } // namespace features } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h index 65802a2..3347846 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
@@ -17,7 +17,6 @@ extern const base::Feature kDataSaverSiteBreakdownUsingPageLoadMetrics; extern const base::Feature kDataReductionProxyEnabledWithNetworkService; extern const base::Feature kDataSaverUseOnDeviceSafeBrowsing; -extern const base::Feature kDataReductionProxyBlockOnceOnBadGatewayResponse; } // namespace features } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc index 305286e..2764f52 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
@@ -418,16 +418,8 @@ // Fall back if a 500, 502 or 503 is returned. if (headers.response_code() == net::HTTP_INTERNAL_SERVER_ERROR) return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR; - if (headers.response_code() == net::HTTP_BAD_GATEWAY) { - if (base::FeatureList::IsEnabled( - features::kDataReductionProxyBlockOnceOnBadGatewayResponse)) { - data_reduction_proxy_info->bypass_all = true; - data_reduction_proxy_info->mark_proxies_as_bad = false; - data_reduction_proxy_info->bypass_duration = base::TimeDelta(); - data_reduction_proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; - } + if (headers.response_code() == net::HTTP_BAD_GATEWAY) return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY; - } if (headers.response_code() == net::HTTP_SERVICE_UNAVAILABLE) return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE; // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
diff --git a/components/download/content/factory/BUILD.gn b/components/download/content/factory/BUILD.gn index 43275b0..d9eb53fa 100644 --- a/components/download/content/factory/BUILD.gn +++ b/components/download/content/factory/BUILD.gn
@@ -4,8 +4,8 @@ source_set("factory") { sources = [ - "download_service_factory.cc", - "download_service_factory.h", + "download_service_factory_helper.cc", + "download_service_factory_helper.h", "navigation_monitor_factory.cc", "navigation_monitor_factory.h", ]
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory_helper.cc similarity index 99% rename from components/download/content/factory/download_service_factory.cc rename to components/download/content/factory/download_service_factory_helper.cc index 571a6d9..b7a53200 100644 --- a/components/download/content/factory/download_service_factory.cc +++ b/components/download/content/factory/download_service_factory_helper.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/download/content/factory/download_service_factory.h" +#include "components/download/content/factory/download_service_factory_helper.h" #include "base/files/file_path.h" #include "build/build_config.h"
diff --git a/components/download/content/factory/download_service_factory.h b/components/download/content/factory/download_service_factory_helper.h similarity index 96% rename from components/download/content/factory/download_service_factory.h rename to components/download/content/factory/download_service_factory_helper.h index a619eb0..b09e038 100644 --- a/components/download/content/factory/download_service_factory.h +++ b/components/download/content/factory/download_service_factory_helper.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_ -#define COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_ +#ifndef COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_HELPER_H_ +#define COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_HELPER_H_ #include <memory> @@ -24,7 +24,7 @@ namespace network { class NetworkConnectionTracker; class SharedURLLoaderFactory; -} +} // namespace network namespace download { @@ -64,4 +64,4 @@ } // namespace download -#endif // COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_H_ +#endif // COMPONENTS_DOWNLOAD_CONTENT_FACTORY_DOWNLOAD_SERVICE_FACTORY_HELPER_H_
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc index 8803c62..afc35b88 100644 --- a/components/exo/client_controlled_shell_surface.cc +++ b/components/exo/client_controlled_shell_surface.cc
@@ -518,6 +518,10 @@ GetFrameView()->GetHeaderView()->GetFrameHeader()->SetFrameTextOverride( extra_title); + if (wide_frame_) { + wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride( + extra_title); + } } void ClientControlledShellSurface::SetOrientationLock( @@ -1002,7 +1006,13 @@ wide_frame_ = std::make_unique<ash::WideFrameView>(widget_); ash::ImmersiveFullscreenController::EnableForWidget(widget_, false); wide_frame_->Init(immersive_fullscreen_controller_.get()); + wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride( + GetFrameView() + ->GetHeaderView() + ->GetFrameHeader() + ->frame_text_override()); wide_frame_->GetWidget()->Show(); + // Restoring window targeter replaced by ImmersiveFullscreenController. InstallCustomWindowTargeter();
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader.h b/components/offline_pages/core/prefetch/prefetch_downloader.h index 349509fe..ca1a31944a 100644 --- a/components/offline_pages/core/prefetch/prefetch_downloader.h +++ b/components/offline_pages/core/prefetch/prefetch_downloader.h
@@ -59,6 +59,9 @@ // Called when a download fails. virtual void OnDownloadFailed(const std::string& download_id) = 0; + + // Returns the maximum number of downloads allowed. + virtual int GetMaxConcurrentDownloads() = 0; }; } // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc b/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc index dfb00b55..45fdaf99 100644 --- a/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc +++ b/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc
@@ -12,6 +12,7 @@ #include "base/trace_event/trace_event.h" #include "components/download/public/background_service/download_params.h" #include "components/download/public/background_service/download_service.h" +#include "components/download/public/background_service/service_config.h" #include "components/offline_pages/core/offline_clock.h" #include "components/offline_pages/core/offline_event_logger.h" #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h" @@ -217,6 +218,10 @@ NotifyDispatcher(prefetch_service_, result); } +int PrefetchDownloaderImpl::GetMaxConcurrentDownloads() { + return download_service_->GetConfig().GetMaxConcurrentDownloads(); +} + void PrefetchDownloaderImpl::OnStartDownload( const std::string& download_id, download::DownloadParams::StartResult result) {
diff --git a/components/offline_pages/core/prefetch/prefetch_downloader_impl.h b/components/offline_pages/core/prefetch/prefetch_downloader_impl.h index b4bc0352..c76e6fc 100644 --- a/components/offline_pages/core/prefetch/prefetch_downloader_impl.h +++ b/components/offline_pages/core/prefetch/prefetch_downloader_impl.h
@@ -52,6 +52,7 @@ const base::FilePath& file_path, int64_t file_size) override; void OnDownloadFailed(const std::string& download_id) override; + int GetMaxConcurrentDownloads() override; private: enum class DownloadServiceStatus {
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task.cc b/components/offline_pages/core/prefetch/tasks/download_archives_task.cc index a52c5e6..c24d3bc 100644 --- a/components/offline_pages/core/prefetch/tasks/download_archives_task.cc +++ b/components/offline_pages/core/prefetch/tasks/download_archives_task.cc
@@ -11,6 +11,7 @@ #include "components/offline_pages/core/offline_page_feature.h" #include "components/offline_pages/core/offline_store_utils.h" #include "components/offline_pages/core/prefetch/prefetch_downloader.h" +#include "components/offline_pages/core/prefetch/prefetch_prefs.h" #include "components/offline_pages/core/prefetch/prefetch_types.h" #include "components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h" #include "components/offline_pages/core/prefetch/store/prefetch_store.h" @@ -19,10 +20,6 @@ #include "sql/transaction.h" namespace offline_pages { -namespace prefetch_prefs { -bool IsLimitlessPrefetchingEnabled(PrefService*); -} - namespace { using DownloadItem = DownloadArchivesTask::DownloadItem; @@ -30,10 +27,10 @@ ItemsToDownload FindItemsReadyForDownload(sql::Database* db) { static const char kSql[] = - "SELECT offline_id, archive_body_name, operation_name," - " archive_body_length" + "SELECT offline_id,archive_body_name,operation_name," + "archive_body_length" " FROM prefetch_items" - " WHERE state = ?" + " WHERE state=?" " ORDER BY creation_time DESC"; sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt(0, static_cast<int>(PrefetchItemState::RECEIVED_BUNDLE)); @@ -53,7 +50,7 @@ std::unique_ptr<int> CountDownloadsInProgress(sql::Database* db) { static const char kSql[] = - "SELECT COUNT(offline_id) FROM prefetch_items WHERE state = ?"; + "SELECT COUNT(offline_id) FROM prefetch_items WHERE state=?"; sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt(0, static_cast<int>(PrefetchItemState::DOWNLOADING)); if (!statement.Step()) @@ -69,12 +66,11 @@ // the item longer than that, if we keep on retrying it. static const char kSql[] = "UPDATE prefetch_items" - " SET state = ?," - " guid = ?," - " freshness_time = CASE WHEN download_initiation_attempts = 0 THEN ?" - " ELSE freshness_time END," - " download_initiation_attempts = download_initiation_attempts + 1" - " WHERE offline_id = ?"; + " SET state=?,guid=?," + "freshness_time=CASE WHEN download_initiation_attempts=0 THEN ? " + "ELSE freshness_time END," + "download_initiation_attempts=download_initiation_attempts+1" + " WHERE offline_id=?"; sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); statement.BindInt(0, static_cast<int>(PrefetchItemState::DOWNLOADING)); statement.BindString(1, guid); @@ -85,16 +81,12 @@ std::unique_ptr<ItemsToDownload> SelectAndMarkItemsForDownloadSync( bool is_limitless_prefetching_enabled, + int max_concurrent_downloads, sql::Database* db) { sql::Transaction transaction(db); if (!transaction.Begin()) return nullptr; - const int max_concurrent_downloads = - is_limitless_prefetching_enabled - ? DownloadArchivesTask::kMaxConcurrentDownloadsForLimitless - : DownloadArchivesTask::kMaxConcurrentDownloads; - // Get current count of concurrent downloads and bail early if we are already // downloading more than we can. std::unique_ptr<int> concurrent_downloads(CountDownloadsInProgress(db)); @@ -153,16 +145,6 @@ } // namespace -// static -const int DownloadArchivesTask::kMaxConcurrentDownloads = 2; - -// This value matches the maximum number of concurrent downloads allowed by the -// downloads service. -// TODO(https://crbug.com/793158): obtain this value directly from the downloads -// service once it is exposed. -// static -const int DownloadArchivesTask::kMaxConcurrentDownloadsForLimitless = 4; - DownloadArchivesTask::DownloadItem::DownloadItem() = default; DownloadArchivesTask::DownloadItem::DownloadItem(const DownloadItem& other) = default; @@ -188,10 +170,17 @@ TaskComplete(); return; } + const bool is_limitless_prefetching_enabled = + prefetch_prefs::IsLimitlessPrefetchingEnabled(prefs_); + const int max_concurrent_downloads = + is_limitless_prefetching_enabled + ? prefetch_downloader_->GetMaxConcurrentDownloads() + : DownloadArchivesTask::kMaxConcurrentDownloads; prefetch_store_->Execute( base::BindOnce(&SelectAndMarkItemsForDownloadSync, - prefetch_prefs::IsLimitlessPrefetchingEnabled(prefs_)), + is_limitless_prefetching_enabled, + max_concurrent_downloads), base::BindOnce(&DownloadArchivesTask::SendItemsToPrefetchDownloader, weak_ptr_factory_.GetWeakPtr()), std::unique_ptr<ItemsToDownload>());
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task.h b/components/offline_pages/core/prefetch/tasks/download_archives_task.h index e699652..102215a 100644 --- a/components/offline_pages/core/prefetch/tasks/download_archives_task.h +++ b/components/offline_pages/core/prefetch/tasks/download_archives_task.h
@@ -26,10 +26,7 @@ class DownloadArchivesTask : public Task { public: // Maximum number of parallel downloads. - static const int kMaxConcurrentDownloads; - - // Maximum number of parallel downloads when limitless prefetching is enabled. - static const int kMaxConcurrentDownloadsForLimitless; + static constexpr int kMaxConcurrentDownloads = 2; // Represents item to be downloaded as a result of running the task. struct DownloadItem {
diff --git a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc index c4e89204..0c4a0711 100644 --- a/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc +++ b/components/offline_pages/core/prefetch/tasks/download_archives_task_unittest.cc
@@ -307,15 +307,15 @@ // Enable limitless prefetching. prefetch_prefs::SetLimitlessPrefetchingEnabled(prefs(), true); - // Check the concurrent downloads limit is greater for limitless. - ASSERT_GT(DownloadArchivesTask::kMaxConcurrentDownloadsForLimitless, - DownloadArchivesTask::kMaxConcurrentDownloads); + // Configure max concurrent downloads. + const size_t limitless_max_concurrent_downloads = + DownloadArchivesTask::kMaxConcurrentDownloads + 1; + prefetch_downloader()->SetMaxConcurrentDownloads( + limitless_max_concurrent_downloads); // Create more archives than we allow to download in parallel with limitless // and put them in the fresher |item_ids| in front. - const size_t max_concurrent_downloads = base::checked_cast<size_t>( - DownloadArchivesTask::kMaxConcurrentDownloadsForLimitless); - const size_t total_items = max_concurrent_downloads + 2; + const size_t total_items = limitless_max_concurrent_downloads + 1; std::vector<int64_t> item_ids; for (size_t i = 0; i < total_items; ++i) item_ids.insert(item_ids.begin(), InsertItemToDownload(kLargeArchiveSize)); @@ -332,11 +332,11 @@ const TestPrefetchDownloader::RequestMap& requested_downloads = prefetch_downloader()->requested_downloads(); - EXPECT_EQ(max_concurrent_downloads, requested_downloads.size()); + EXPECT_EQ(limitless_max_concurrent_downloads, requested_downloads.size()); - // The freshest |kMaxConcurrentDownloadsForLimitless| items should be started + // The freshest |limitless_max_concurrent_downloads| items should be started // as with limitless enabled there's no download size restrictions. - for (size_t i = 0; i < max_concurrent_downloads; ++i) { + for (size_t i = 0; i < limitless_max_concurrent_downloads; ++i) { const PrefetchItem* download_item = FindPrefetchItemByOfflineId(items_after_run, item_ids[i]); ASSERT_TRUE(download_item); @@ -348,7 +348,7 @@ } // Remaining items shouldn't have been started. - for (size_t i = max_concurrent_downloads; i < total_items; ++i) { + for (size_t i = limitless_max_concurrent_downloads; i < total_items; ++i) { const PrefetchItem* download_item_before = FindPrefetchItemByOfflineId(items_before_run, item_ids[i]); const PrefetchItem* download_item_after = @@ -361,7 +361,7 @@ histogram_tester.ExpectUniqueSample( "OfflinePages.Prefetching.DownloadExpectedFileSize", - kLargeArchiveSize / 1024, max_concurrent_downloads); + kLargeArchiveSize / 1024, limitless_max_concurrent_downloads); } TEST_F(DownloadArchivesTaskTest, SingleArchiveSecondAttempt) {
diff --git a/components/offline_pages/core/prefetch/test_prefetch_downloader.cc b/components/offline_pages/core/prefetch/test_prefetch_downloader.cc index 2216c30..410ba30 100644 --- a/components/offline_pages/core/prefetch/test_prefetch_downloader.cc +++ b/components/offline_pages/core/prefetch/test_prefetch_downloader.cc
@@ -41,6 +41,10 @@ void TestPrefetchDownloader::OnDownloadFailed(const std::string& download_id) {} +int TestPrefetchDownloader::GetMaxConcurrentDownloads() { + return max_concurrent_downloads_; +} + void TestPrefetchDownloader::Reset() { requested_downloads_.clear(); }
diff --git a/components/offline_pages/core/prefetch/test_prefetch_downloader.h b/components/offline_pages/core/prefetch/test_prefetch_downloader.h index 48d863b..bb2b8a88a 100644 --- a/components/offline_pages/core/prefetch/test_prefetch_downloader.h +++ b/components/offline_pages/core/prefetch/test_prefetch_downloader.h
@@ -39,13 +39,19 @@ const base::FilePath& file_path, int64_t file_size) override; void OnDownloadFailed(const std::string& download_id) override; + int GetMaxConcurrentDownloads() override; void Reset(); const RequestMap& requested_downloads() const { return requested_downloads_; } + void SetMaxConcurrentDownloads(int max_concurrent_downloads) { + max_concurrent_downloads_ = max_concurrent_downloads; + } + private: RequestMap requested_downloads_; + int max_concurrent_downloads_ = 4; }; } // namespace offline_pages
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc index 8fa16dd..e10f49a2 100644 --- a/components/omnibox/browser/document_provider.cc +++ b/components/omnibox/browser/document_provider.cc
@@ -540,6 +540,7 @@ // We aim to prevent duplicate Drive URLs to appear between the Drive document // search provider and history/bookmark entries. // Drive URLs take on two core forms, and may have request parameters. + // Additionally, we may have redirector URLs which wrap a drive URL. // All URLs are canonicalized to a GURL form only used for deduplication and // not guaranteed to be usable for navigation. // URLs of the following forms are handled: @@ -547,6 +548,7 @@ // https://docs.google.com/document/d/(id)/edit // https://docs.google.com/spreadsheets/d/(id)/edit#gid=12345 // https://docs.google.com/presentation/d/(id)/edit#slide=id.g12345a_0_26 + // https://www.google.com/url?[...]url=https://drive.google.com/a/google.com/open?id%3D1fkxx6KYRYnSqljThxShJVliQJLdKzuJBnzogzL3n8rE&[...] // where id is comprised of characters in [0-9A-Za-z\-_] = [\w\-] std::string id; if (url.host() == "drive.google.com" && url.path() == "/open") { @@ -555,6 +557,12 @@ static re2::LazyRE2 doc_link_regex = { "^/(?:document|spreadsheets|presentation|forms)/d/([\\w-]+)/"}; RE2::PartialMatch(url.path(), *doc_link_regex, &id); + } else if (url.host() == "www.google.com" && url.path() == "/url") { + // Redirect links wrapping a drive.google.com/open?id= link. + static re2::LazyRE2 redirect_link_regex = { + "^[^#]*url=https://drive\\.google\\.com/(?:a/[\\w\\.]+/" + ")?open\\?id%3D([^#&]*)"}; + RE2::PartialMatch(url.query(), *redirect_link_regex, &id); } if (id.empty()) {
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc index 0c1a00c..4377e769 100644 --- a/components/omnibox/browser/document_provider_unittest.cc +++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -558,9 +558,31 @@ CheckDeduper( "https://docs.google.com/spreadsheets/d/the_doc-id/preview?x=1#y=2", "the_doc-id"); + CheckDeduper( + "https://www.google.com/" + "url?sa=t&rct=j&esrc=s&source=appssearch&uact=8&cd=0&cad=rja&q&sig2=sig&" + "url=https://drive.google.com/a/google.com/" + "open?id%3D1fkxx6KYRYnSqljThxShJVliQJLdKzuJBnzogzL3n8rE&usg=X", + "1fkxx6KYRYnSqljThxShJVliQJLdKzuJBnzogzL3n8rE"); + CheckDeduper( + "https://www.google.com/url?url=https://drive.google.com/a/google.com/" + "open?id%3Dthe_doc_id", + "the_doc_id"); + CheckDeduper( + "https://www.google.com/url?url=https://drive.google.com/a/foo.edu/" + "open?id%3Dthe_doc_id", + "the_doc_id"); + CheckDeduper( + "https://www.google.com/url?url=https://drive.google.com/" + "open?id%3Dthe_doc_id", + "the_doc_id"); // URLs that do not represent documents: CheckDeduper("https://docs.google.com/help?id=d123", ""); CheckDeduper("https://www.google.com", ""); CheckDeduper("https://docs.google.com/kittens/d/d123/preview?x=1#y=2", ""); + CheckDeduper( + "https://www.google.com/url?url=https://drive.google.com/homepage", ""); + CheckDeduper("https://www.google.com/url?url=https://www.youtube.com/view", + ""); }
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc index 29d0b28..2f627d41 100644 --- a/components/password_manager/core/browser/sync/password_sync_bridge.cc +++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -12,6 +12,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/common/password_form.h" +#include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/sync/model/metadata_batch.h" #include "components/sync/model/metadata_change_list.h" @@ -274,6 +275,7 @@ password_store_sync_->ReadAllLogins(&key_to_local_form_map); if (read_result == FormRetrievalResult::kDbError) { + metrics_util::LogPasswordSyncState(metrics_util::NOT_SYNCING_FAILED_READ); return syncer::ModelError(FROM_HERE, "Failed to load entries from password store."); } @@ -291,6 +293,7 @@ // Clean up done successfully, try to read again. read_result = password_store_sync_->ReadAllLogins(&key_to_local_form_map); if (read_result != FormRetrievalResult::kSuccess) { + metrics_util::LogPasswordSyncState(metrics_util::NOT_SYNCING_FAILED_READ); return syncer::ModelError( FROM_HERE, "Failed to load entries from password store after cleanup."); @@ -454,6 +457,7 @@ password_store_sync_->NotifyLoginsChanged(password_store_changes); } + metrics_util::LogPasswordSyncState(metrics_util::SYNCING_OK); return error; } @@ -639,10 +643,14 @@ case DatabaseCleanupResult::kSuccess: break; case DatabaseCleanupResult::kEncryptionUnavailable: + metrics_util::LogPasswordSyncState( + metrics_util::NOT_SYNCING_FAILED_DECRYPTION); return syncer::ModelError( FROM_HERE, "Failed to get encryption key during database cleanup."); case DatabaseCleanupResult::kItemFailure: case DatabaseCleanupResult::kDatabaseUnavailable: + metrics_util::LogPasswordSyncState( + metrics_util::NOT_SYNCING_FAILED_CLEANUP); return syncer::ModelError(FROM_HERE, "Failed to cleanup database."); } return base::nullopt;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index ebf6fdf..4183893 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -425,7 +425,7 @@ 'caption': '''Startup pages''', 'desc': '''Allows you to configure the pages that are loaded on startup. - The contents of the list 'URLs to open at startup' are ignored unless you select 'Open a list of URLs' in 'Action on startup'.''', + The policy 'URLs to open on startup' is ignored unless you select 'Open a list of URLs' in 'Action on startup'.''', 'policies': [ 'RestoreOnStartup', 'RestoreOnStartupURLs',
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc index 56d859f..e47fae5 100644 --- a/components/sync/driver/about_sync_util.cc +++ b/components/sync/driver/about_sync_util.cc
@@ -331,7 +331,7 @@ Stat<std::string>* username = section_identity->AddStringStat("Username"); Stat<bool>* user_is_primary = section_identity->AddBoolStat("Is Primary"); Stat<std::string>* auth_error = section_identity->AddStringStat("Auth Error"); - // TODO(treib): Add the *time* of the auth error? + // TODO(crbug.com/948074): Add the time of the auth error. Section* section_credentials = section_list.AddSection("Credentials"); Stat<std::string>* request_token_time =
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc index 763a0a9..593bb12 100644 --- a/components/sync/driver/profile_sync_service.cc +++ b/components/sync/driver/profile_sync_service.cc
@@ -526,11 +526,11 @@ ReportPreviousSessionMemoryWarningCount(); - // TODO(treib): Consider kicking off an access token fetch here. Currently, - // the flow goes as follows: The SyncEngine tries to connect to the server, - // but has no access token, so it ends up calling OnConnectionStatusChange( - // syncer::CONNECTION_AUTH_ERROR) which in turn causes SyncAuthManager to - // request a new access token. That seems needlessly convoluted. + // TODO(crbug.com/948148): Consider kicking off an access token fetch here. + // Currently, the flow goes as follows: The SyncEngine tries to connect to the + // server, but has no access token, so it ends up calling + // OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR) which in turn + // causes SyncAuthManager to request a new access token. } void ProfileSyncService::Shutdown() {
diff --git a/components/sync/model_impl/processor_entity.cc b/components/sync/model_impl/processor_entity.cc index f497dc4..28261929 100644 --- a/components/sync/model_impl/processor_entity.cc +++ b/components/sync/model_impl/processor_entity.cc
@@ -24,10 +24,12 @@ // Used for E2E latency measurements with UMA. const size_t kMaxTrackedCommittedServerVersions = 20; -void HashSpecifics(const sync_pb::EntitySpecifics& specifics, - std::string* hash) { +std::string HashSpecifics(const sync_pb::EntitySpecifics& specifics) { DCHECK_GT(specifics.ByteSize(), 0); - base::Base64Encode(base::SHA1HashString(specifics.SerializeAsString()), hash); + std::string hash; + base::Base64Encode(base::SHA1HashString(specifics.SerializeAsString()), + &hash); + return hash; } } // namespace @@ -110,9 +112,7 @@ if (data.is_deleted() || metadata_.base_specifics_hash().empty()) { return false; } - std::string hash; - HashSpecifics(data.specifics, &hash); - return hash == metadata_.base_specifics_hash(); + return HashSpecifics(data.specifics) == metadata_.base_specifics_hash(); } bool ProcessorEntity::IsUnsynced() const { @@ -327,15 +327,13 @@ const sync_pb::EntitySpecifics& specifics) const { DCHECK(!metadata_.is_deleted()); DCHECK_GT(specifics.ByteSize(), 0); - std::string hash; - HashSpecifics(specifics, &hash); - return hash == metadata_.specifics_hash(); + return HashSpecifics(specifics) == metadata_.specifics_hash(); } void ProcessorEntity::UpdateSpecificsHash( const sync_pb::EntitySpecifics& specifics) { if (specifics.ByteSize() > 0) { - HashSpecifics(specifics, metadata_.mutable_specifics_hash()); + *metadata_.mutable_specifics_hash() = HashSpecifics(specifics); } else { metadata_.clear_specifics_hash(); }
diff --git a/components/sync_sessions/favicon_cache.cc b/components/sync_sessions/favicon_cache.cc index ca58d61f..ce4170b2d 100644 --- a/components/sync_sessions/favicon_cache.cc +++ b/components/sync_sessions/favicon_cache.cc
@@ -226,6 +226,7 @@ history::HistoryService* history_service, int max_sync_favicon_limit) : favicon_service_(favicon_service), + history_service_(history_service), max_sync_favicon_limit_(max_sync_favicon_limit), history_service_observer_(this), weak_ptr_factory_(this) { @@ -236,6 +237,16 @@ FaviconCache::~FaviconCache() {} +void FaviconCache::WaitUntilReadyToSync(base::OnceClosure done) { + if (history_service_->backend_loaded()) { + std::move(done).Run(); + } else { + // Wait until HistoryService's backend loads, reported via + // OnHistoryServiceLoaded(). + wait_until_ready_to_sync_cb_.push_back(std::move(done)); + } +} + syncer::SyncMergeResult FaviconCache::MergeDataAndStartSyncing( syncer::ModelType type, const syncer::SyncDataList& initial_sync_data, @@ -1016,4 +1027,16 @@ } } +void FaviconCache::OnHistoryServiceLoaded( + history::HistoryService* history_service) { + // Make a copy before iterating over, in case triggering the callback has side + // effects. + std::vector<base::OnceClosure> callbacks = + std::move(wait_until_ready_to_sync_cb_); + wait_until_ready_to_sync_cb_.clear(); + + for (auto& cb : callbacks) + std::move(cb).Run(); +} + } // namespace sync_sessions
diff --git a/components/sync_sessions/favicon_cache.h b/components/sync_sessions/favicon_cache.h index 08946fb..4d2c456 100644 --- a/components/sync_sessions/favicon_cache.h +++ b/components/sync_sessions/favicon_cache.h
@@ -66,6 +66,7 @@ ~FaviconCache() override; // SyncableService implementation. + void WaitUntilReadyToSync(base::OnceClosure done) override; syncer::SyncMergeResult MergeDataAndStartSyncing( syncer::ModelType type, const syncer::SyncDataList& initial_sync_data, @@ -198,8 +199,11 @@ // history::HistoryServiceObserver: void OnURLsDeleted(history::HistoryService* history_service, const history::DeletionInfo& deletion_info) override; + void OnHistoryServiceLoaded( + history::HistoryService* history_service) override; - favicon::FaviconService* favicon_service_; + favicon::FaviconService* const favicon_service_; + history::HistoryService* const history_service_; // Task tracker for loading favicons. base::CancelableTaskTracker cancelable_task_tracker_; @@ -226,6 +230,10 @@ // Maximum number of favicons to sync. 0 means no limit. const size_t max_sync_favicon_limit_; + // A vector is needed to support concurrent calls to WaitUntilReadyToSync() + // because this class powers two datatypes. + std::vector<base::OnceClosure> wait_until_ready_to_sync_cb_; + ScopedObserver<history::HistoryService, history::HistoryServiceObserver> history_service_observer_;
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/autofill_assistant_target_website.html index 65df22a..55e279b 100644 --- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html +++ b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
@@ -56,6 +56,15 @@ left: -9999px; } + #terms-and-conditions::before { + content: "before"; + } + + #button::before { + content: "before"; + display: none; + } + label[for="terms-and-conditions"] { padding-left: 48px; position: relative;
diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h index f516309..e2f3fbc 100644 --- a/components/viz/common/quads/compositor_frame_metadata.h +++ b/components/viz/common/quads/compositor_frame_metadata.h
@@ -60,8 +60,7 @@ CompositorFrameMetadata Clone() const; - // The device scale factor used to generate this compositor frame. Must be - // greater than zero. + // The device scale factor used to generate this compositor frame. float device_scale_factor = 0.f; // Scroll offset and scale of the root layer. This can be used for tasks
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 76cf2c9d..e8106a4 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -74,6 +74,13 @@ SkPoint points[4]; }; +// Additional YUV information to skia renderer to draw 9- and 10- bits color. +struct YUVInput { + YUVInput() { memset(this, 0, sizeof(*this)); } + float offset; + float multiplier; +}; + SkDrawRegion::SkDrawRegion(const gfx::QuadF& draw_region) { points[0] = gfx::PointFToSkPoint(draw_region.p1()); points[1] = gfx::PointFToSkPoint(draw_region.p2()); @@ -771,6 +778,9 @@ case DrawQuad::DEBUG_BORDER: DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad), params); break; + case DrawQuad::PICTURE_CONTENT: + DrawPictureQuad(PictureDrawQuad::MaterialCast(quad), params); + break; case DrawQuad::SOLID_COLOR: DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), params); break; @@ -786,6 +796,19 @@ case DrawQuad::YUV_VIDEO_CONTENT: DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad), params); break; + case DrawQuad::INVALID: + DrawUnsupportedQuad(quad, params); + NOTREACHED(); + break; + case DrawQuad::VIDEO_HOLE: + // VideoHoleDrawQuad should only be used by Cast, and should + // have been replaced by cast-specific OverlayProcessor before + // reach here. In non-cast build, an untrusted render could send such + // Quad and the quad would then reach here unexpectedly. Therefore + // we should skip NOTREACHED() so an untrusted render is not capable + // of causing a crash. + DrawUnsupportedQuad(quad, params); + break; default: // If we've reached here, the quad's type hasn't been updated to be // batch aware yet. @@ -1028,6 +1051,68 @@ current_canvas_->drawPath(path, paint); } +void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad, + const DrawQuadParams& params) { + DCHECK(batched_quads_.empty()); + TRACE_EVENT0("viz", "SkiaRenderer::DrawPictureQuad"); + + // If the layer is transparent or needs a non-SrcOver blend mode, saveLayer + // must be used so that the display list is drawn into a transient image and + // then blended as a single layer at the end. + const bool needs_transparency = + params.opacity < 1.f || params.blend_mode != SkBlendMode::kSrcOver; + const bool disable_image_filtering = + disable_picture_quad_image_filtering_ || + params.filter_quality == kNone_SkFilterQuality; + + SkAutoCanvasRestore acr(current_canvas_, true /* do_save */); + PrepareCanvas(params.has_scissor_rect ? &scissor_rect_ : nullptr, + ¶ms.content_device_transform); + + // Unlike other quads which draw visible_rect or draw_region as their geometry + // these represent the valid windows of content to show for the display list, + // so they need to be used as a clip in Skia. + SkRect visible_rect = gfx::RectFToSkRect(params.visible_rect); + SkPaint paint = params.paint(); + if (params.draw_region.has_value()) { + SkPath clip; + clip.addPoly(params.draw_region->points, 4, true /* close */); + current_canvas_->clipPath(clip, paint.isAntiAlias()); + } else { + current_canvas_->clipRect(visible_rect, paint.isAntiAlias()); + } + + if (needs_transparency) { + // Use the DrawQuadParams' paint for the layer, since that will affect the + // final draw of the backing image. + current_canvas_->saveLayer(&visible_rect, &paint); + } + + SkCanvas* raster_canvas = current_canvas_; + base::Optional<skia::OpacityFilterCanvas> opacity_canvas; + if (disable_image_filtering) { + // TODO(vmpstr): Fold this canvas into playback and have raster source + // accept a set of settings on playback that will determine which canvas to + // apply. (http://crbug.com/594679) + // saveLayer applies the opacity, this filter is only used for quality + // overriding in the display list, hence the fixed 1.f for alpha. + opacity_canvas.emplace(raster_canvas, 1.f, disable_image_filtering); + raster_canvas = &*opacity_canvas; + } + + // Treat all subnormal values as zero for performance. + cc::ScopedSubnormalFloatDisabler disabler; + + raster_canvas->concat(SkMatrix::MakeRectToRect( + gfx::RectFToSkRect(quad->tex_coord_rect), gfx::RectToSkRect(quad->rect), + SkMatrix::kFill_ScaleToFit)); + + raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y()); + raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect)); + raster_canvas->scale(quad->contents_scale, quad->contents_scale); + quad->display_item_list->Raster(raster_canvas); +} + void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad, const DrawQuadParams& params) { DrawColoredQuad(params, quad->color); @@ -1194,7 +1279,8 @@ gfx::ColorSpace dst_color_space = current_frame()->current_render_pass->color_space; sk_sp<SkColorFilter> color_filter = - GetColorFilter(src_color_space, dst_color_space); + GetColorFilter(src_color_space, dst_color_space, quad->resource_offset, + quad->resource_multiplier); DCHECK(resource_provider_); ScopedYUVSkImageBuilder builder(this, quad, dst_color_space.ToSkColorSpace(), @@ -1214,6 +1300,15 @@ DrawSingleImage(params, image, visible_tex_coord_rect, &paint); } +void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad, + const DrawQuadParams& params) { +#ifdef NDEBUG + DrawColoredQuad(params, SK_ColorWHITE); +#else + DrawColoredQuad(params, SK_ColorMAGENTA); +#endif +} + void SkiaRenderer::DoSingleDrawQuad(const DrawQuad* quad, const gfx::QuadF* draw_region) { base::Optional<SkAutoCanvasRestore> auto_canvas_restore; @@ -1248,7 +1343,7 @@ NOTREACHED(); break; case DrawQuad::PICTURE_CONTENT: - DrawPictureQuad(PictureDrawQuad::MaterialCast(quad), &paint); + NOTREACHED(); break; case DrawQuad::RENDER_PASS: DrawRenderPassQuad(RenderPassDrawQuad::MaterialCast(quad), &paint); @@ -1274,17 +1369,8 @@ NOTREACHED(); break; case DrawQuad::INVALID: - DrawUnsupportedQuad(quad, &paint); - NOTREACHED(); - break; case DrawQuad::VIDEO_HOLE: - // VideoHoleDrawQuad should only be used by Cast, and should - // have been replaced by cast-specific OverlayProcessor before - // reach here. In non-cast build, an untrusted render could send such - // Quad and the quad would then reach here unexpectedly. Therefore - // we should skip NOTREACHED() so an untrusted render is not capable - // of causing a crash. - DrawUnsupportedQuad(quad, &paint); + NOTREACHED(); break; } @@ -1317,49 +1403,10 @@ } } -void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad, - SkPaint* paint) { - DCHECK(paint); - SkMatrix content_matrix; - content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect), - gfx::RectToSkRect(quad->rect), - SkMatrix::kFill_ScaleToFit); - current_canvas_->concat(content_matrix); - - const bool needs_transparency = - SkScalarRoundToInt(quad->shared_quad_state->opacity * 255) < 255; - const bool disable_image_filtering = - disable_picture_quad_image_filtering_ || quad->nearest_neighbor; - - TRACE_EVENT0("viz", "SkiaRenderer::DrawPictureQuad"); - - SkCanvas* raster_canvas = current_canvas_; - - base::Optional<skia::OpacityFilterCanvas> opacity_canvas; - if (needs_transparency || disable_image_filtering) { - // TODO(aelias): This isn't correct in all cases. We should detect these - // cases and fall back to a persistent bitmap backing - // (http://crbug.com/280374). - // TODO(vmpstr): Fold this canvas into playback and have raster source - // accept a set of settings on playback that will determine which canvas to - // apply. (http://crbug.com/594679) - opacity_canvas.emplace(raster_canvas, quad->shared_quad_state->opacity, - disable_image_filtering); - raster_canvas = &*opacity_canvas; - } - - // Treat all subnormal values as zero for performance. - cc::ScopedSubnormalFloatDisabler disabler; - - SkAutoCanvasRestore auto_canvas_restore(raster_canvas, true /* do_save */); - raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y()); - raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect)); - raster_canvas->scale(quad->contents_scale, quad->contents_scale); - quad->display_item_list->Raster(raster_canvas); -} - sk_sp<SkColorFilter> SkiaRenderer::GetColorFilter(const gfx::ColorSpace& src, - const gfx::ColorSpace& dst) { + const gfx::ColorSpace& dst, + float resource_offset, + float resource_multiplier) { sk_sp<SkColorFilter>& color_filter = color_filter_cache_[dst][src]; if (!color_filter) { std::unique_ptr<gfx::ColorTransform> transform = @@ -1369,8 +1416,20 @@ // COLOR_CONVERSION_MODE_LUT). if (!transform->CanGetShaderSource()) return nullptr; + + YUVInput input; + input.offset = resource_offset; + input.multiplier = resource_multiplier; + sk_sp<SkData> data = SkData::MakeWithCopy(&input, sizeof(input)); + const char* hdr = R"( +in uniform float offset; +in uniform float multiplier; + void main(inout half4 color) { + color.rgb -= half(offset); + color.rgb *= half(multiplier); + // un-premultiply alpha if (color.a > 0) color.rgb /= color.a; @@ -1382,9 +1441,10 @@ )"; std::string shader = hdr + transform->GetSkShaderSource() + ftr; + color_filter = SkRuntimeColorFilterFactory(SkString(shader.c_str(), shader.size())) - .make(SkData::MakeEmpty()); + .make(data); } return color_filter; } @@ -1625,19 +1685,6 @@ paint); } -void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad, SkPaint* paint) { - DCHECK(paint); - // TODO(weiliangc): Make sure unsupported quads work. (crbug.com/644851) - NOTIMPLEMENTED(); -#ifdef NDEBUG - paint->setColor(SK_ColorWHITE); -#else - paint->setColor(SK_ColorMAGENTA); -#endif - paint->setAlpha(quad->shared_quad_state->opacity * 255); - current_canvas_->drawRect(gfx::RectToSkRect(quad->rect), *paint); -} - void SkiaRenderer::CopyDrawnRenderPass( const copy_output::RenderPassGeometry& geometry, std::unique_ptr<CopyOutputRequest> request) {
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h index a166816..2715f4a 100644 --- a/components/viz/service/display/skia_renderer.h +++ b/components/viz/service/display/skia_renderer.h
@@ -142,7 +142,8 @@ // the texture will have advanced paint effects (rpdq) void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad, const DrawQuadParams& params); - void DrawPictureQuad(const PictureDrawQuad* quad, SkPaint* paint); + void DrawPictureQuad(const PictureDrawQuad* quad, + const DrawQuadParams& params); void DrawRenderPassQuad(const RenderPassDrawQuad* quad, SkPaint* paint); void DrawRenderPassQuadInternal(const RenderPassDrawQuad* quad, sk_sp<SkImage> content_image, @@ -158,7 +159,8 @@ void DrawTileDrawQuad(const TileDrawQuad* quad, const DrawQuadParams& params); void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, const DrawQuadParams& params); - void DrawUnsupportedQuad(const DrawQuad* quad, SkPaint* paint); + void DrawUnsupportedQuad(const DrawQuad* quad, const DrawQuadParams& params); + bool CalculateRPDQParams(sk_sp<SkImage> src_image, const RenderPassDrawQuad* quad, DrawRenderPassDrawQuadParams* params); @@ -174,7 +176,9 @@ bool is_using_ddl() const { return draw_mode_ == DrawMode::DDL; } sk_sp<SkColorFilter> GetColorFilter(const gfx::ColorSpace& src, - const gfx::ColorSpace& dst); + const gfx::ColorSpace& dst, + float resource_offset, + float resource_multiplier); // A map from RenderPass id to the texture used to draw the RenderPass from. struct RenderPassBacking { sk_sp<SkSurface> render_pass_surface;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index 4a729685a..70a6aab 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -456,11 +456,10 @@ last_created_local_surface_id.parent_sequence_number() || child_initiated_synchronization_event); - DCHECK(surface_info.is_valid()); - if (!monotonically_increasing_id) { - TRACE_EVENT_INSTANT0("viz", "LocalSurfaceId decreased", + if (!surface_info.is_valid() || !monotonically_increasing_id) { + TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation", TRACE_EVENT_SCOPE_THREAD); - return SubmitResult::SURFACE_ID_DECREASED; + return SubmitResult::SURFACE_INVARIANTS_VIOLATION; } // If the last Surface doesn't have a dependent frame, and this frame @@ -481,9 +480,9 @@ } current_surface = CreateSurface(surface_info, block_activation_on_parent); if (!current_surface) { - TRACE_EVENT_INSTANT0("viz", "Surface belongs to another client", + TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation", TRACE_EVENT_SCOPE_THREAD); - return SubmitResult::SURFACE_OWNED_BY_ANOTHER_CLIENT; + return SubmitResult::SURFACE_INVARIANTS_VIOLATION; } last_created_surface_id_ = SurfaceId(frame_sink_id_, local_surface_id); @@ -508,7 +507,7 @@ weak_factory_.GetWeakPtr(), frame.metadata.frame_token)); if (!result) { TRACE_EVENT_INSTANT0("viz", "QueueFrame failed", TRACE_EVENT_SCOPE_THREAD); - return SubmitResult::SIZE_MISMATCH; + return SubmitResult::SURFACE_INVARIANTS_VIOLATION; } if (begin_frame_source_) @@ -731,12 +730,8 @@ return "Accepted"; case SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED: return "CopyOutputRequests not allowed"; - case SubmitResult::SIZE_MISMATCH: - return "CompositorFrame size doesn't match surface size"; - case SubmitResult::SURFACE_ID_DECREASED: - return "LocalSurfaceId sequence numbers decreased"; - case SubmitResult::SURFACE_OWNED_BY_ANOTHER_CLIENT: - return "Surface belongs to another client"; + case SubmitResult::SURFACE_INVARIANTS_VIOLATION: + return "Surface invariants violation"; } NOTREACHED(); return nullptr;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h index 521cb34..d2a4a937 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -40,12 +40,9 @@ enum class SubmitResult { ACCEPTED = 0, COPY_OUTPUT_REQUESTS_NOT_ALLOWED = 1, - // SURFACE_INVARIANTS_VIOLATION = 2 is deprecated. - SIZE_MISMATCH = 3, - SURFACE_ID_DECREASED = 4, - SURFACE_OWNED_BY_ANOTHER_CLIENT = 5, + SURFACE_INVARIANTS_VIOLATION = 2, // Magic constant used by the histogram macros. - kMaxValue = SURFACE_OWNED_BY_ANOTHER_CLIENT, + kMaxValue = SURFACE_INVARIANTS_VIOLATION, }; class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index 6824d9d..cd2618a 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -630,17 +630,19 @@ mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::ACCEPTED, result); - // LocalSurfaceId(5, 3): Submit rejected because not monotonically increasing. + // LocalSurfaceId(5, 3): Surface Invariants Violation. Not monotonically + // increasing. result = support->MaybeSubmitCompositorFrame( local_surface_id4, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::SURFACE_ID_DECREASED, result); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); - // LocalSurfaceId(8, 1): Submit rejected because not monotonically increasing. + // LocalSurfaceId(8, 1): Surface Invariants Violation. Not monotonically + // increasing. result = support->MaybeSubmitCompositorFrame( local_surface_id5, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::SURFACE_ID_DECREASED, result); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); // LocalSurfaceId(9, 3): Parent AND child-initiated synchronization. result = support->MaybeSubmitCompositorFrame( @@ -874,6 +876,21 @@ surface_observer_.last_surface_info().size_in_pixels()); } +// Check that if a CompositorFrame is received with device scale factor of 0, we +// don't create a Surface for it. +TEST_F(CompositorFrameSinkSupportTest, ZeroDeviceScaleFactor) { + SurfaceId id(support_->frame_sink_id(), local_surface_id_); + auto frame = CompositorFrameBuilder() + .AddDefaultRenderPass() + .SetDeviceScaleFactor(0.f) + .Build(); + const auto result = support_->MaybeSubmitCompositorFrame( + local_surface_id_, std::move(frame), base::nullopt, 0, + mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); + EXPECT_FALSE(GetSurfaceForId(id)); +} + // Check that if the size of a CompositorFrame doesn't match the size of the // Surface it's being submitted to, we skip the frame. TEST_F(CompositorFrameSinkSupportTest, FrameSizeMismatch) { @@ -902,7 +919,7 @@ local_surface_id_, std::move(frame), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::SIZE_MISMATCH, result); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); // All the resources in the rejected frame should have been returned. CheckReturnedResourcesMatchExpected(frame_resource_ids, @@ -935,7 +952,7 @@ result = support_->MaybeSubmitCompositorFrame( local_surface_id_, std::move(frame), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::SIZE_MISMATCH, result); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); } TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) { @@ -1209,7 +1226,7 @@ result = support->MaybeSubmitCompositorFrame( local_surface_id, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::SURFACE_OWNED_BY_ANOTHER_CLIENT, result); + EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); } } // namespace viz
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc index 1bf3164..c5ec129 100644 --- a/content/browser/android/synchronous_compositor_host.cc +++ b/content/browser/android/synchronous_compositor_host.cc
@@ -10,7 +10,8 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" -#include "base/memory/shared_memory.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/writable_shared_memory_region.h" #include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "base/trace_event/traced_value.h" @@ -286,7 +287,7 @@ }; struct SynchronousCompositorHost::SharedMemoryWithSize { - base::SharedMemory shm; + base::WritableSharedMemoryMapping shared_memory; const size_t stride; const size_t buffer_size; @@ -343,8 +344,10 @@ UpdateFrameMetaData(metadata_version, std::move(*metadata)); SkBitmap bitmap; - if (!bitmap.installPixels(info, software_draw_shm_->shm.memory(), stride)) + if (!bitmap.installPixels(info, software_draw_shm_->shared_memory.memory(), + stride)) { return false; + } { TRACE_EVENT0("browser", "DrawBitmap"); @@ -364,20 +367,20 @@ software_draw_shm_->buffer_size == buffer_size) return; software_draw_shm_.reset(); - std::unique_ptr<SharedMemoryWithSize> software_draw_shm( - new SharedMemoryWithSize(stride, buffer_size)); + auto software_draw_shm = + std::make_unique<SharedMemoryWithSize>(stride, buffer_size); + base::WritableSharedMemoryRegion shm_region; { TRACE_EVENT1("browser", "AllocateSharedMemory", "buffer_size", buffer_size); - if (!software_draw_shm->shm.CreateAndMapAnonymous(buffer_size)) + shm_region = base::WritableSharedMemoryRegion::Create(buffer_size); + if (!shm_region.IsValid()) + return; + + software_draw_shm->shared_memory = shm_region.Map(); + if (!software_draw_shm->shared_memory.IsValid()) return; } - SyncCompositorSetSharedMemoryParams set_shm_params; - set_shm_params.buffer_size = buffer_size; - set_shm_params.shm_handle = software_draw_shm->shm.handle().Duplicate(); - if (!set_shm_params.shm_handle.IsValid()) - return; - bool success = false; SyncCompositorCommonRendererParams common_renderer_params; { @@ -385,8 +388,8 @@ base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_base_sync_primitives; if (!IsReadyForSynchronousCall() || - !GetSynchronousCompositor()->SetSharedMemory(set_shm_params, &success, - &common_renderer_params) || + !GetSynchronousCompositor()->SetSharedMemory( + std::move(shm_region), &success, &common_renderer_params) || !success) { return; }
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc index 55f1920f..8154ea9c 100644 --- a/content/browser/child_process_security_policy_impl.cc +++ b/content/browser/child_process_security_policy_impl.cc
@@ -552,37 +552,19 @@ DCHECK(browser_context); DCHECK_CURRENTLY_ON(BrowserThread::UI); base::AutoLock lock(lock_); - AddChild(child_id, browser_context); + if (security_state_.count(child_id) != 0) { + NOTREACHED() << "Add child process at most once."; + return; + } + + security_state_[child_id] = + base::MakeRefCounted<SecurityState>(browser_context); } void ChildProcessSecurityPolicyImpl::Remove(int child_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::AutoLock lock(lock_); - - auto state = security_state_.find(child_id); - if (state == security_state_.end()) - return; - - // Moving the existing SecurityState object into a pending map so - // that we can preserve permission state and avoid mutations to this - // state after Remove() has been called. - pending_remove_state_[child_id] = std::move(state->second); security_state_.erase(child_id); - - // |child_id| could be inside tasks that are on the IO thread task queues. We - // need to keep the |pending_remove_state_| entry around until we have - // successfully executed a task on the IO thread. This should ensure that any - // pending tasks on the IO thread will have completed before we remove the - // entry. - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, - base::BindOnce( - [](ChildProcessSecurityPolicyImpl* policy, int child_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::AutoLock lock(policy->lock_); - policy->pending_remove_state_.erase(child_id); - }, - base::Unretained(this), child_id)); } void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( @@ -675,19 +657,19 @@ base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; if (origin.opaque()) { // If it's impossible to grant commit rights to just the origin (among other // things, URLs with non-standard schemes will be treated as opaque // origins), then grant access to commit all URLs of that scheme. - state->second->GrantCommitScheme(url.scheme()); + state->GrantCommitScheme(url.scheme()); } else { // When the child process has been commanded to request this scheme, grant // it the capability to request all URLs of that scheme. - state->second->GrantRequestScheme(url.scheme()); + state->GrantRequestScheme(url.scheme()); } } @@ -699,15 +681,15 @@ { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; // When the child process has been commanded to request a file:// URL, // then we grant it the capability for that URL only. base::FilePath path; if (net::FileURLToFilePath(url, &path)) - state->second->GrantRequestOfSpecificFile(path); + state->GrantRequestOfSpecificFile(path); } } @@ -735,22 +717,22 @@ int child_id, const base::FilePath& file, int permissions) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantPermissionsForFile(file, permissions); + state->GrantPermissionsForFile(file, permissions); } void ChildProcessSecurityPolicyImpl::RevokeAllPermissionsForFile( int child_id, const base::FilePath& file) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->RevokeAllPermissionsForFile(file); + state->RevokeAllPermissionsForFile(file); } void ChildProcessSecurityPolicyImpl::GrantReadFileSystem( @@ -787,11 +769,11 @@ void ChildProcessSecurityPolicyImpl::GrantSendMidiSysExMessage(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantPermissionForMidiSysEx(); + state->GrantPermissionForMidiSysEx(); } void ChildProcessSecurityPolicyImpl::GrantCommitOrigin( @@ -799,11 +781,11 @@ const url::Origin& origin) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantCommitOrigin(origin); + state->GrantCommitOrigin(origin); } void ChildProcessSecurityPolicyImpl::GrantRequestOrigin( @@ -811,11 +793,11 @@ const url::Origin& origin) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantRequestOrigin(origin); + state->GrantRequestOrigin(origin); } void ChildProcessSecurityPolicyImpl::GrantRequestScheme( @@ -823,11 +805,11 @@ const std::string& scheme) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantRequestScheme(scheme); + state->GrantRequestScheme(scheme); } void ChildProcessSecurityPolicyImpl::GrantWebUIBindings(int child_id, @@ -838,37 +820,37 @@ base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantBindings(bindings); + state->GrantBindings(bindings); // Web UI bindings need the ability to request chrome: URLs. - state->second->GrantRequestScheme(kChromeUIScheme); + state->GrantRequestScheme(kChromeUIScheme); // Web UI pages can contain links to file:// URLs. - state->second->GrantRequestScheme(url::kFileScheme); + state->GrantRequestScheme(url::kFileScheme); } void ChildProcessSecurityPolicyImpl::GrantReadRawCookies(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantReadRawCookies(); + state->GrantReadRawCookies(); } void ChildProcessSecurityPolicyImpl::RevokeReadRawCookies(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->RevokeReadRawCookies(); + state->RevokeReadRawCookies(); } bool ChildProcessSecurityPolicyImpl::CanRequestURL( @@ -906,13 +888,13 @@ { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; // Otherwise, we consult the child process's security state to see if it is // allowed to request the URL. - if (state->second->CanRequestURL(url)) + if (state->CanRequestURL(url)) return true; } @@ -994,13 +976,13 @@ if (base::ContainsKey(schemes_okay_to_commit_in_any_process_, scheme)) return true; - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; // Otherwise, we consult the child process's security state to see if it is // allowed to commit the URL. - return state->second->CanCommitURL(url); + return state->CanCommitURL(url); } } @@ -1256,41 +1238,29 @@ bool ChildProcessSecurityPolicyImpl::HasWebUIBindings(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; - return state->second->has_web_ui_bindings(); + return state->has_web_ui_bindings(); } bool ChildProcessSecurityPolicyImpl::CanReadRawCookies(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; - return state->second->can_read_raw_cookies(); -} - -void ChildProcessSecurityPolicyImpl::AddChild(int child_id, - BrowserContext* browser_context) { - DCHECK(browser_context); - if (security_state_.count(child_id) != 0) { - NOTREACHED() << "Add child process at most once."; - return; - } - - security_state_[child_id] = - base::MakeRefCounted<SecurityState>(browser_context); + return state->can_read_raw_cookies(); } bool ChildProcessSecurityPolicyImpl::ChildProcessHasPermissionsForFile( int child_id, const base::FilePath& file, int permissions) { - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; - return state->second->HasPermissionsForFile(file, permissions); + return state->HasPermissionsForFile(file, permissions); } bool ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin( @@ -1369,27 +1339,27 @@ #endif base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - DCHECK(state != security_state_.end()); - state->second->LockToOrigin(gurl, context.browsing_instance_id()); + auto* state = GetSecurityState(child_id); + DCHECK(state); + state->LockToOrigin(gurl, context.browsing_instance_id()); } ChildProcessSecurityPolicyImpl::CheckOriginLockResult ChildProcessSecurityPolicyImpl::CheckOriginLock(int child_id, const GURL& site_url) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return ChildProcessSecurityPolicyImpl::CheckOriginLockResult::NO_LOCK; - return state->second->CheckOriginLock(site_url); + return state->CheckOriginLock(site_url); } GURL ChildProcessSecurityPolicyImpl::GetOriginLock(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return GURL(); - return state->second->origin_lock(); + return state->origin_lock(); } void ChildProcessSecurityPolicyImpl::GrantPermissionsForFileSystem( @@ -1398,10 +1368,10 @@ int permission) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return; - state->second->GrantPermissionsForFileSystem(filesystem_id, permission); + state->GrantPermissionsForFileSystem(filesystem_id, permission); } bool ChildProcessSecurityPolicyImpl::HasPermissionsForFileSystem( @@ -1410,10 +1380,10 @@ int permission) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; - return state->second->HasPermissionsForFileSystem(filesystem_id, permission); + return state->HasPermissionsForFileSystem(filesystem_id, permission); } void ChildProcessSecurityPolicyImpl::RegisterFileSystemPermissionPolicy( @@ -1426,11 +1396,11 @@ bool ChildProcessSecurityPolicyImpl::CanSendMidiSysExMessage(int child_id) { base::AutoLock lock(lock_); - auto state = security_state_.find(child_id); - if (state == security_state_.end()) + auto* state = GetSecurityState(child_id); + if (!state) return false; - return state->second->can_send_midi_sysex(); + return state->can_send_midi_sysex(); } void ChildProcessSecurityPolicyImpl::AddIsolatedOrigins( @@ -1524,10 +1494,6 @@ for (auto& itr : security_state_) { itr.second->ClearMatchingContexts(&browser_context); } - - for (auto& itr : pending_remove_state_) { - itr.second->ClearMatchingContexts(&browser_context); - } } base::AutoLock isolated_origins_lock(isolated_origins_lock_); @@ -1670,15 +1636,6 @@ if (itr != security_state_.end()) return itr->second.get(); - // Check to see if |child_id| is in the pending removal map since this - // may be a call that was already on the IO thread's task queue when the - // Remove() call occurred. - if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - itr = pending_remove_state_.find(child_id); - if (itr != pending_remove_state_.end()) - return itr->second.get(); - } - return nullptr; }
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h index c98f180..2cf8c742 100644 --- a/content/browser/child_process_security_policy_impl.h +++ b/content/browser/child_process_security_policy_impl.h
@@ -178,13 +178,6 @@ // Upon destruction, child processes should unregister themselves by calling // this method exactly once. This call must be made on the UI thread. - // - // Note: Pre-Remove() permissions remain in effect on the IO thread until - // the task posted to the IO thread by this call runs and removes the entry - // from |pending_remove_state_|. - // This UI -> IO task sequence ensures that any pending tasks, on the IO - // thread, for this |child_id| are allowed to run before access is completely - // revoked. void Remove(int child_id); // Whenever the browser processes commands the child process to commit a URL, @@ -427,10 +420,6 @@ ChildProcessSecurityPolicyImpl(); friend struct base::DefaultSingletonTraits<ChildProcessSecurityPolicyImpl>; - // Adds child process during registration. - void AddChild(int child_id, BrowserContext* browser_context) - EXCLUSIVE_LOCKS_REQUIRED(lock_); - // Determines if certain permissions were granted for a file to given child // process. |permissions| is an internally defined bit-set. bool ChildProcessHasPermissionsForFile(int child_id, @@ -499,14 +488,6 @@ // not escape this class. SecurityStateMap security_state_ GUARDED_BY(lock_); - // This map holds the SecurityState for a child process after Remove() - // is called on the UI thread. An entry stays in this map until a task has - // run on the IO thread. This is necessary to provide consistent security - // decisions and avoid races between the UI & IO threads during child process - // shutdown. This separate map is used to preserve SecurityState info AND - // preventing mutation of that state after Remove() is called. - SecurityStateMap pending_remove_state_ GUARDED_BY(lock_); - FileSystemPermissionPolicyMap file_system_policy_map_ GUARDED_BY(lock_); // You must acquire this lock before reading or writing isolated_origins_.
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc index 301cae01..32e3ca2 100644 --- a/content/browser/child_process_security_policy_unittest.cc +++ b/content/browser/child_process_security_policy_unittest.cc
@@ -1075,120 +1075,6 @@ EXPECT_FALSE(p->HasWebUIBindings(kRendererID)); } -// Tests behavior of CanAccessDataForOrigin() during race conditions that -// can occur during Remove(). It verifies that permissions for a child ID are -// preserved after a Remove() call until the task, that Remove() has posted to -// the IO thread, has run. -// -// We use a combination of waitable events and extra tasks posted to the -// threads to capture permission state from the UI & IO threads during the -// removal process. It is intended to simulate pending tasks that could be -// run on each thread during removal. -TEST_F(ChildProcessSecurityPolicyTest, RemoveRace_CanAccessDataForOrigin) { - ChildProcessSecurityPolicyImpl* p = - ChildProcessSecurityPolicyImpl::GetInstance(); - - GURL url("file:///etc/passwd"); - - p->Add(kRendererID, browser_context()); - - base::WaitableEvent ready_for_remove_event; - base::WaitableEvent remove_called_event; - base::WaitableEvent pending_remove_complete_event; - - // Keep track of the return value for CanAccessDataForOrigin at various - // points in time during the test. - bool io_before_remove = false; - bool io_while_io_task_pending = false; - bool io_after_io_task_completed = false; - bool ui_before_remove = false; - bool ui_while_io_task_pending = false; - bool ui_after_io_task_completed = false; - - // Post a task that will run on the IO thread before the task that - // Remove() will post to the IO thread. - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() { - // Capture state on the IO thread before Remove() is called. - io_before_remove = p->CanAccessDataForOrigin(kRendererID, url); - - // Tell the UI thread we are ready for Remove() to be called. - ready_for_remove_event.Signal(); - - // Wait for Remove() to be called on the UI thread. - remove_called_event.Wait(); - - // Capture state after Remove() is called, but before its task on - // the IO thread runs. - io_while_io_task_pending = p->CanAccessDataForOrigin(kRendererID, url); - })); - - ready_for_remove_event.Wait(); - - ui_before_remove = p->CanAccessDataForOrigin(kRendererID, url); - - p->Remove(kRendererID); - - // Post a task to run after the task Remove() posted on the IO thread. - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO}, - base::BindLambdaForTesting([&]() { - io_after_io_task_completed = - p->CanAccessDataForOrigin(kRendererID, url); - - // Tell the UI thread that the task from Remove() - // has completed on the IO thread. - pending_remove_complete_event.Signal(); - })); - - // Capture state after Remove() has been called, but before its IO thread - // task has run. We know the IO thread task hasn't run yet because the - // task we posted before the Remove() call is waiting for us to signal - // |remove_called_event|. - ui_while_io_task_pending = p->CanAccessDataForOrigin(kRendererID, url); - - // Unblock the IO thread so the pending remove events can run. - remove_called_event.Signal(); - - pending_remove_complete_event.Wait(); - - // Capture state after IO thread task has run. - ui_after_io_task_completed = p->CanAccessDataForOrigin(kRendererID, url); - - // Run pending UI thread tasks. - base::RunLoop run_loop; - run_loop.RunUntilIdle(); - - bool ui_after_remove_complete = p->CanAccessDataForOrigin(kRendererID, url); - bool io_after_remove_complete = false; - base::WaitableEvent after_remove_complete_event; - - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() { - io_after_remove_complete = p->CanAccessDataForOrigin(kRendererID, url); - - // Tell the UI thread that this task has - // has completed on the IO thread. - after_remove_complete_event.Signal(); - })); - - // Wait for the task we just posted to the IO thread to complete. - after_remove_complete_event.Wait(); - - // Verify expected states at various parts of the removal. - // Note: IO thread is expected to keep pre-Remove() permissions until - // the task Remove() posted runs on the IO thread. - EXPECT_TRUE(io_before_remove); - EXPECT_TRUE(io_while_io_task_pending); - EXPECT_FALSE(io_after_io_task_completed); - - EXPECT_TRUE(ui_before_remove); - EXPECT_FALSE(ui_while_io_task_pending); - EXPECT_FALSE(ui_after_io_task_completed); - - EXPECT_FALSE(ui_after_remove_complete); - EXPECT_FALSE(io_after_remove_complete); -} - TEST_F(ChildProcessSecurityPolicyTest, CanAccessDataForOrigin) { ChildProcessSecurityPolicyImpl* p = ChildProcessSecurityPolicyImpl::GetInstance(); @@ -1719,11 +1605,9 @@ // Keep track of the return value for HasSecurityState() at various // points in time during the test. bool io_before_remove = false; - bool io_while_io_task_pending = false; - bool io_after_io_task_completed = false; + bool io_after_remove = false; bool ui_before_remove = false; - bool ui_while_io_task_pending = false; - bool ui_after_io_task_completed = false; + bool ui_after_remove = false; // Post a task that will run on the IO thread before the task that // Remove() will post to the IO thread. @@ -1740,7 +1624,9 @@ // Capture state after Remove() is called, but before its task on // the IO thread runs. - io_while_io_task_pending = p->HasSecurityState(kRendererID); + io_after_remove = p->HasSecurityState(kRendererID); + + pending_remove_complete_event.Signal(); })); ready_for_remove_event.Wait(); @@ -1749,63 +1635,19 @@ p->Remove(kRendererID); - // Post a task to run after the task Remove() posted on the IO thread. - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() { - io_after_io_task_completed = p->HasSecurityState(kRendererID); + ui_after_remove = p->HasSecurityState(kRendererID); - // Tell the UI thread that the task from Remove() - // has completed on the IO thread. - pending_remove_complete_event.Signal(); - })); - - // Capture state after Remove() has been called, but before its IO thread - // task has run. We know the IO thread task hasn't run yet because the - // task we posted before the Remove() call is waiting for us to signal - // |remove_called_event|. - ui_while_io_task_pending = p->HasSecurityState(kRendererID); - - // Unblock the IO thread so the pending remove events can run. + // Unblock the IO thread so it can collect post-Remove() state. remove_called_event.Signal(); pending_remove_complete_event.Wait(); - // Capture state after IO thread task has run. - ui_after_io_task_completed = p->HasSecurityState(kRendererID); - - // Run pending UI thread tasks. - base::RunLoop run_loop; - run_loop.RunUntilIdle(); - - bool ui_after_remove_complete = p->HasSecurityState(kRendererID); - bool io_after_remove_complete = false; - base::WaitableEvent after_remove_complete_event; - - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, base::BindLambdaForTesting([&]() { - io_after_remove_complete = p->HasSecurityState(kRendererID); - - // Tell the UI thread that this task has - // has completed on the IO thread. - after_remove_complete_event.Signal(); - })); - - // Wait for the task we just posted to the IO thread to complete. - after_remove_complete_event.Wait(); - // Verify expected states at various parts of the removal. - // Note: IO thread is expected to keep pre-Remove() permissions until - // the task Remove() posted runs on the IO thread. EXPECT_TRUE(io_before_remove); - EXPECT_TRUE(io_while_io_task_pending); - EXPECT_FALSE(io_after_io_task_completed); + EXPECT_FALSE(io_after_remove); EXPECT_TRUE(ui_before_remove); - EXPECT_FALSE(ui_while_io_task_pending); - EXPECT_FALSE(ui_after_io_task_completed); - - EXPECT_FALSE(ui_after_remove_complete); - EXPECT_FALSE(io_after_remove_complete); + EXPECT_FALSE(ui_after_remove); } } // namespace content
diff --git a/content/browser/devtools/devtools_background_services_context.cc b/content/browser/devtools/devtools_background_services_context.cc index a3d728b7..d46e3d6 100644 --- a/content/browser/devtools/devtools_background_services_context.cc +++ b/content/browser/devtools/devtools_background_services_context.cc
@@ -56,6 +56,12 @@ for (const auto& expiration_time : expiration_times) { DCHECK(devtools::proto::BackgroundService_IsValid(expiration_time.first)); expiration_times_[expiration_time.first] = expiration_time.second; + + auto service = + static_cast<devtools::proto::BackgroundService>(expiration_time.first); + // If the recording permission for |service| has expired, set it to null. + if (IsRecordingExpired(service)) + expiration_times_[expiration_time.first] = base::Time(); } } @@ -75,8 +81,6 @@ devtools::proto::BackgroundService service) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!IsRecording(service)); - // TODO(rayankans): Make the time delay finch configurable. base::Time expiration_time = base::Time::Now() + base::TimeDelta::FromDays(3); expiration_times_[service] = expiration_time; @@ -92,9 +96,7 @@ devtools::proto::BackgroundService service) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(IsRecording(service)); expiration_times_[service] = base::Time(); - GetContentClient()->browser()->UpdateDevToolsBackgroundServiceExpiration( browser_context_, service, base::Time()); @@ -104,7 +106,16 @@ bool DevToolsBackgroundServicesContext::IsRecording( devtools::proto::BackgroundService service) { - return base::Time::Now() < expiration_times_[service]; + // Returns whether |service| has been enabled. When the expiration time has + // been met it will be lazily updated to be null. + return !expiration_times_[service].is_null(); +} + +bool DevToolsBackgroundServicesContext::IsRecordingExpired( + devtools::proto::BackgroundService service) { + // Copy the expiration time to avoid data races. + const base::Time expiration_time = expiration_times_[service]; + return !expiration_time.is_null() && expiration_time < base::Time::Now(); } void DevToolsBackgroundServicesContext::GetLoggedBackgroundServiceEvents( @@ -198,6 +209,17 @@ if (!IsRecording(service)) return; + if (IsRecordingExpired(service)) { + // We should stop recording because of the expiration time. We should + // also inform the observers that we stopped recording. + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce( + &DevToolsBackgroundServicesContext::OnRecordingTimeExpired, + weak_ptr_factory_.GetWeakPtr(), service)); + return; + } + devtools::proto::BackgroundServiceEvent event; event.set_timestamp( base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds()); @@ -228,4 +250,14 @@ observer.OnEventReceived(event); } +void DevToolsBackgroundServicesContext::OnRecordingTimeExpired( + devtools::proto::BackgroundService service) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // This could have been stopped by the user in the meanwhile, or we + // received duplicate time expiry events. + if (IsRecordingExpired(service)) + StopRecording(service); +} + } // namespace content
diff --git a/content/browser/devtools/devtools_background_services_context.h b/content/browser/devtools/devtools_background_services_context.h index 8a6f36d6..a7d8d07 100644 --- a/content/browser/devtools/devtools_background_services_context.h +++ b/content/browser/devtools/devtools_background_services_context.h
@@ -100,6 +100,9 @@ friend class base::RefCountedThreadSafe<DevToolsBackgroundServicesContext>; ~DevToolsBackgroundServicesContext(); + // Whether |service| has an expiration time and it was exceeded. + bool IsRecordingExpired(devtools::proto::BackgroundService service); + void GetLoggedBackgroundServiceEventsOnIO( devtools::proto::BackgroundService service, GetLoggedBackgroundServiceEventsCallback callback); @@ -115,6 +118,8 @@ void NotifyEventObservers( const devtools::proto::BackgroundServiceEvent& event); + void OnRecordingTimeExpired(devtools::proto::BackgroundService service); + BrowserContext* browser_context_; scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
diff --git a/content/browser/devtools/devtools_background_services_context_unittest.cc b/content/browser/devtools/devtools_background_services_context_unittest.cc index b160c14..cc072b6 100644 --- a/content/browser/devtools/devtools_background_services_context_unittest.cc +++ b/content/browser/devtools/devtools_background_services_context_unittest.cc
@@ -343,6 +343,24 @@ SimulateOneWeekPassing(); EXPECT_FALSE(GetExpirationTime().is_null()); + + // Recording should be true, with an expired value. + EXPECT_TRUE(IsRecording()); + + // Logging should not happen. + EXPECT_CALL(*this, OnEventReceived(_)).Times(0); + LogTestBackgroundServiceEvent("f1"); + + // Observers should be informed that recording stopped. + EXPECT_CALL( + *this, + OnRecordingStateChanged( + false, devtools::proto::BackgroundService::TEST_BACKGROUND_SERVICE)); + + thread_bundle_.RunUntilIdle(); + + // The expiration time entry should be cleared. + EXPECT_TRUE(GetExpirationTime().is_null()); EXPECT_FALSE(IsRecording()); }
diff --git a/content/browser/webrtc/webrtc_internals_browsertest.cc b/content/browser/webrtc/webrtc_internals_browsertest.cc index 8b324eb..2cd0a8d7 100644 --- a/content/browser/webrtc/webrtc_internals_browsertest.cc +++ b/content/browser/webrtc/webrtc_internals_browsertest.cc
@@ -155,9 +155,11 @@ MAYBE_WebRtcInternalsBrowserTest() {} ~MAYBE_WebRtcInternalsBrowserTest() override {} + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(switches::kUseFakeUIForMediaStream); + } + void SetUpOnMainThread() override { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kUseFakeUIForMediaStream); ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseFakeDeviceForMediaStream)); }
diff --git a/content/common/input/sync_compositor_messages.cc b/content/common/input/sync_compositor_messages.cc index 51de727..68c6ea7 100644 --- a/content/common/input/sync_compositor_messages.cc +++ b/content/common/input/sync_compositor_messages.cc
@@ -18,9 +18,6 @@ SyncCompositorDemandDrawHwParams::~SyncCompositorDemandDrawHwParams() {} -SyncCompositorSetSharedMemoryParams::SyncCompositorSetSharedMemoryParams() - : buffer_size(0u) {} - SyncCompositorDemandDrawSwParams::SyncCompositorDemandDrawSwParams() {} SyncCompositorDemandDrawSwParams::~SyncCompositorDemandDrawSwParams() {}
diff --git a/content/common/input/sync_compositor_messages.h b/content/common/input/sync_compositor_messages.h index f9d291fd..e1a3d2e 100644 --- a/content/common/input/sync_compositor_messages.h +++ b/content/common/input/sync_compositor_messages.h
@@ -7,7 +7,6 @@ #include <stddef.h> -#include "base/memory/shared_memory_handle.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h" #include "ipc/ipc_message_macros.h" @@ -33,13 +32,6 @@ gfx::Transform transform_for_tile_priority; }; -struct SyncCompositorSetSharedMemoryParams { - SyncCompositorSetSharedMemoryParams(); - - uint32_t buffer_size; - base::SharedMemoryHandle shm_handle; -}; - struct SyncCompositorDemandDrawSwParams { SyncCompositorDemandDrawSwParams(); ~SyncCompositorDemandDrawSwParams(); @@ -82,11 +74,6 @@ IPC_STRUCT_TRAITS_MEMBER(transform_for_tile_priority) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorSetSharedMemoryParams) - IPC_STRUCT_TRAITS_MEMBER(buffer_size) - IPC_STRUCT_TRAITS_MEMBER(shm_handle) -IPC_STRUCT_TRAITS_END() - IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorDemandDrawSwParams) IPC_STRUCT_TRAITS_MEMBER(size) IPC_STRUCT_TRAITS_MEMBER(clip)
diff --git a/content/common/input/synchronous_compositor.mojom b/content/common/input/synchronous_compositor.mojom index d6d8564..aa01db13 100644 --- a/content/common/input/synchronous_compositor.mojom +++ b/content/common/input/synchronous_compositor.mojom
@@ -4,6 +4,7 @@ module content.mojom; +import "mojo/public/mojom/base/shared_memory.mojom"; import "mojo/public/mojom/base/time.mojom"; import "services/viz/public/interfaces/compositing/begin_frame_args.mojom"; import "services/viz/public/interfaces/compositing/compositor_frame.mojom"; @@ -16,9 +17,6 @@ struct SyncCompositorDemandDrawHwParams; [Native] -struct SyncCompositorSetSharedMemoryParams; - -[Native] struct SyncCompositorDemandDrawSwParams; [Native] @@ -49,7 +47,8 @@ // drawing. This mode just has the renderer send over a single bitmap of the // final frame, rather than sending over individual tiles (ie. resources) // that are then composited by the browser. - [Sync] SetSharedMemory(SyncCompositorSetSharedMemoryParams params) => + [Sync] + SetSharedMemory(mojo_base.mojom.WritableSharedMemoryRegion shm_region) => (bool success, SyncCompositorCommonRendererParams result); // Synchronously does a software based draw.
diff --git a/content/common/navigation_params.cc b/content/common/navigation_params.cc index 485f143..5e5d9c6 100644 --- a/content/common/navigation_params.cc +++ b/content/common/navigation_params.cc
@@ -73,10 +73,12 @@ ResourceInterceptPolicy NavigationDownloadPolicy::GetResourceInterceptPolicy() const { - // Note: Will need to check each NavigationDownloadType case by case if some - // correspond to the |kAllowPluginOnly| policy. Currently every single - // NavigationDownloadType corresponds to |kAllowNone|, so it's fine to just - // check whether |disallowed_types| contains any bit at all. + if (disallowed_types.test( + static_cast<size_t>(NavigationDownloadType::kSandboxNoGesture)) || + disallowed_types.test( + static_cast<size_t>(NavigationDownloadType::kOpenerCrossOrigin))) { + return ResourceInterceptPolicy::kAllowPluginOnly; + } return disallowed_types.any() ? ResourceInterceptPolicy::kAllowNone : ResourceInterceptPolicy::kAllowAll; }
diff --git a/content/public/test/content_browser_test.cc b/content/public/test/content_browser_test.cc index 0d3f512b..a19fc29 100644 --- a/content/public/test/content_browser_test.cc +++ b/content/public/test/content_browser_test.cc
@@ -20,6 +20,7 @@ #include "content/shell/common/shell_switches.h" #include "content/shell/renderer/web_test/web_test_content_renderer_client.h" #include "content/test/test_content_client.h" +#include "ui/events/platform/platform_event_source.h" #if defined(OS_ANDROID) #include "content/shell/app/shell_main_delegate.h" @@ -103,6 +104,8 @@ ui::InitializeInputMethodForTesting(); #endif + ui::PlatformEventSource::SetIgnoreNativePlatformEvents(true); + BrowserTestBase::SetUp(); }
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index 8a0a5ca..1f1eeb0 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -375,8 +375,8 @@ WebAXObject anchor_object, focus_object; int anchor_offset, focus_offset; ax::mojom::TextAffinity anchor_affinity, focus_affinity; - root().Selection(anchor_object, anchor_offset, anchor_affinity, focus_object, - focus_offset, focus_affinity); + root().SelectionDeprecated(anchor_object, anchor_offset, anchor_affinity, + focus_object, focus_offset, focus_affinity); if (!anchor_object.IsNull() && !focus_object.IsNull() && anchor_offset >= 0 && focus_offset >= 0) { int32_t anchor_id = anchor_object.AxID(); @@ -996,9 +996,9 @@ if (src.IsControl() && !src.IsRichlyEditable()) { // Only for simple input controls -- rich editable areas use AXTreeData dst->AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, - src.SelectionStart()); + src.SelectionStartDeprecated()); dst->AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, - src.SelectionEnd()); + src.SelectionEndDeprecated()); } }
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc index 85ac303..b21c58b5 100644 --- a/content/renderer/accessibility/render_accessibility_impl.cc +++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -704,7 +704,8 @@ WebPoint(data.target_point.x(), data.target_point.y())); break; case ax::mojom::Action::kSetSelection: - anchor.SetSelection(anchor, data.anchor_offset, focus, data.focus_offset); + anchor.SetSelectionDeprecated(anchor, data.anchor_offset, focus, + data.focus_offset); HandleAXEvent(root, ax::mojom::Event::kLayoutComplete); break; case ax::mojom::Action::kSetSequentialFocusNavigationStartingPoint:
diff --git a/content/renderer/android/synchronous_compositor_proxy.cc b/content/renderer/android/synchronous_compositor_proxy.cc index 862acfa..4940d75a 100644 --- a/content/renderer/android/synchronous_compositor_proxy.cc +++ b/content/renderer/android/synchronous_compositor_proxy.cc
@@ -160,12 +160,15 @@ } struct SynchronousCompositorProxy::SharedMemoryWithSize { - base::SharedMemory shm; + base::WritableSharedMemoryMapping shared_memory; const size_t buffer_size; bool zeroed; - SharedMemoryWithSize(base::SharedMemoryHandle shm_handle, size_t buffer_size) - : shm(shm_handle, false), buffer_size(buffer_size), zeroed(true) {} + SharedMemoryWithSize(base::WritableSharedMemoryMapping shm_mapping, + size_t buffer_size) + : shared_memory(std::move(shm_mapping)), + buffer_size(buffer_size), + zeroed(true) {} }; void SynchronousCompositorProxy::ZeroSharedMemory() { @@ -175,7 +178,8 @@ if (software_draw_shm_->zeroed) return; - memset(software_draw_shm_->shm.memory(), 0, software_draw_shm_->buffer_size); + memset(software_draw_shm_->shared_memory.memory(), 0, + software_draw_shm_->buffer_size); software_draw_shm_->zeroed = true; } @@ -219,8 +223,10 @@ DCHECK_EQ(software_draw_shm_->buffer_size, buffer_size); SkBitmap bitmap; - if (!bitmap.installPixels(info, software_draw_shm_->shm.memory(), stride)) + if (!bitmap.installPixels(info, software_draw_shm_->shared_memory.memory(), + stride)) { return; + } SkCanvas canvas(bitmap); canvas.clipRect(gfx::RectToSkRect(params.clip)); canvas.concat(params.transform.matrix()); @@ -319,15 +325,15 @@ } void SynchronousCompositorProxy::SetSharedMemory( - const SyncCompositorSetSharedMemoryParams& params, + base::WritableSharedMemoryRegion shm_region, SetSharedMemoryCallback callback) { bool success = false; SyncCompositorCommonRendererParams common_renderer_params; - if (base::SharedMemory::IsHandleValid(params.shm_handle)) { - software_draw_shm_.reset( - new SharedMemoryWithSize(params.shm_handle, params.buffer_size)); - if (software_draw_shm_->shm.Map(params.buffer_size)) { - DCHECK(software_draw_shm_->shm.memory()); + if (shm_region.IsValid()) { + base::WritableSharedMemoryMapping shm_mapping = shm_region.Map(); + if (shm_mapping.IsValid()) { + software_draw_shm_ = std::make_unique<SharedMemoryWithSize>( + std::move(shm_mapping), shm_mapping.size()); PopulateCommonParams(&common_renderer_params); success = true; }
diff --git a/content/renderer/android/synchronous_compositor_proxy.h b/content/renderer/android/synchronous_compositor_proxy.h index 4eb5123b..21c401f 100644 --- a/content/renderer/android/synchronous_compositor_proxy.h +++ b/content/renderer/android/synchronous_compositor_proxy.h
@@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/writable_shared_memory_region.h" #include "base/optional.h" #include "components/viz/common/presentation_feedback_map.h" #include "content/common/input/synchronous_compositor.mojom.h" @@ -30,7 +31,6 @@ struct SyncCompositorCommonRendererParams; struct SyncCompositorDemandDrawHwParams; struct SyncCompositorDemandDrawSwParams; -struct SyncCompositorSetSharedMemoryParams; class SynchronousCompositorProxy : public ui::SynchronousInputHandler, public SynchronousLayerTreeFrameSinkClient, @@ -75,7 +75,7 @@ const SyncCompositorDemandDrawHwParams& draw_params) final; void DemandDrawHw(const SyncCompositorDemandDrawHwParams& params, DemandDrawHwCallback callback) final; - void SetSharedMemory(const SyncCompositorSetSharedMemoryParams& params, + void SetSharedMemory(base::WritableSharedMemoryRegion shm_region, SetSharedMemoryCallback callback) final; void DemandDrawSw(const SyncCompositorDemandDrawSwParams& params, DemandDrawSwCallback callback) final;
diff --git a/content/shell/browser/web_test/web_test_background_fetch_delegate.cc b/content/shell/browser/web_test/web_test_background_fetch_delegate.cc index 0c2ccb2b..8667c860 100644 --- a/content/shell/browser/web_test/web_test_background_fetch_delegate.cc +++ b/content/shell/browser/web_test/web_test_background_fetch_delegate.cc
@@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "base/task/post_task.h" #include "base/test/scoped_feature_list.h" -#include "components/download/content/factory/download_service_factory.h" +#include "components/download/content/factory/download_service_factory_helper.h" #include "components/download/public/background_service/clients.h" #include "components/download/public/background_service/download_metadata.h" #include "components/download/public/background_service/download_params.h"
diff --git a/content/test/data/font_src_local_matching.html b/content/test/data/font_src_local_matching.html index c4f02b9..0051529 100644 --- a/content/test/data/font_src_local_matching.html +++ b/content/test/data/font_src_local_matching.html
@@ -112,7 +112,7 @@ return fontName.replace(/\s+/g, ''); } -function addTestNodes() { +async function addTestNodes() { var containerDiv = document.createElement("div"); var fontFaceDeclarations = ""; for (font_name_and_testchars of getFontsWithTestCharsForOS()) { @@ -129,6 +129,7 @@ } fontfaces.innerText = fontFaceDeclarations; document.body.appendChild(containerDiv); + await document.fonts.ready; // Force layout so that the DevTools side of the test can start accessing // nodes' font information reliably. document.body.offsetTop;
diff --git a/fuchsia/engine/browser/message_port_impl.h b/fuchsia/engine/browser/message_port_impl.h index 8ec47a6..b2f3e90 100644 --- a/fuchsia/engine/browser/message_port_impl.h +++ b/fuchsia/engine/browser/message_port_impl.h
@@ -6,9 +6,9 @@ #define FUCHSIA_ENGINE_BROWSER_MESSAGE_PORT_IMPL_H_ #include <lib/fidl/cpp/binding.h> -#include <deque> #include <memory> +#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "fuchsia/fidl/chromium/web/cpp/fidl.h" @@ -61,7 +61,7 @@ // mojo::MessageReceiver implementation. bool Accept(mojo::Message* message) override; - std::deque<chromium::web::WebMessage> message_queue_; + base::circular_deque<chromium::web::WebMessage> message_queue_; ReceiveMessageCallback pending_client_read_cb_; std::unique_ptr<mojo::Connector> connector_;
diff --git a/fuchsia/runners/cast/cast_channel_bindings.h b/fuchsia/runners/cast/cast_channel_bindings.h index a0199f03..c4db948f 100644 --- a/fuchsia/runners/cast/cast_channel_bindings.h +++ b/fuchsia/runners/cast/cast_channel_bindings.h
@@ -5,10 +5,10 @@ #ifndef FUCHSIA_RUNNERS_CAST_CAST_CHANNEL_BINDINGS_H_ #define FUCHSIA_RUNNERS_CAST_CAST_CHANNEL_BINDINGS_H_ -#include <deque> #include <string> #include "base/callback.h" +#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/strings/string_piece.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h" @@ -55,7 +55,7 @@ NamedMessagePortConnector* const connector_; // A queue of channels waiting to be sent the Cast Channel FIDL service. - std::deque<chromium::web::MessagePortPtr> connected_channel_queue_; + base::circular_deque<chromium::web::MessagePortPtr> connected_channel_queue_; // A long-lived port, used to receive new Cast Channel ports when they are // opened. Should be automatically populated by the
diff --git a/gpu/command_buffer/service/external_vk_image_backing.cc b/gpu/command_buffer/service/external_vk_image_backing.cc index 278ba4ca..8459bed 100644 --- a/gpu/command_buffer/service/external_vk_image_backing.cc +++ b/gpu/command_buffer/service/external_vk_image_backing.cc
@@ -69,9 +69,9 @@ bool ExternalVkImageBacking::BeginAccess( bool readonly, - std::vector<base::ScopedFD>* semaphore_fds) { - DCHECK(semaphore_fds); - DCHECK(semaphore_fds->empty()); + std::vector<SemaphoreHandle>* semaphore_handles) { + DCHECK(semaphore_handles); + DCHECK(semaphore_handles->empty()); if (is_write_in_progress_) { LOG(ERROR) << "Unable to begin read or write access because another write " "access is in progress"; @@ -88,31 +88,31 @@ ++reads_in_progress_; // A semaphore will become unsignaled, when it has been signaled and waited, // so it is not safe to reuse it. - semaphore_fds->push_back(std::move(write_semaphore_fd_)); + semaphore_handles->push_back(std::move(write_semaphore_handle_)); } else { is_write_in_progress_ = true; - *semaphore_fds = std::move(read_semaphore_fds_); - read_semaphore_fds_.clear(); - if (write_semaphore_fd_.is_valid()) - semaphore_fds->push_back(std::move(write_semaphore_fd_)); + *semaphore_handles = std::move(read_semaphore_handles_); + read_semaphore_handles_.clear(); + if (write_semaphore_handle_.is_valid()) + semaphore_handles->push_back(std::move(write_semaphore_handle_)); } return true; } void ExternalVkImageBacking::EndAccess(bool readonly, - base::ScopedFD semaphore_fd) { - DCHECK(semaphore_fd.is_valid()); + SemaphoreHandle semaphore_handle) { + DCHECK(semaphore_handle.is_valid()); if (readonly) { DCHECK_GT(reads_in_progress_, 0u); --reads_in_progress_; - read_semaphore_fds_.push_back(std::move(semaphore_fd)); + read_semaphore_handles_.push_back(std::move(semaphore_handle)); } else { DCHECK(is_write_in_progress_); - DCHECK(!write_semaphore_fd_.is_valid()); - DCHECK(read_semaphore_fds_.empty()); + DCHECK(!write_semaphore_handle_.is_valid()); + DCHECK(read_semaphore_handles_.empty()); is_write_in_progress_ = false; - write_semaphore_fd_ = std::move(semaphore_fd); + write_semaphore_handle_ = std::move(semaphore_handle); } }
diff --git a/gpu/command_buffer/service/external_vk_image_backing.h b/gpu/command_buffer/service/external_vk_image_backing.h index fa65e914..b740065 100644 --- a/gpu/command_buffer/service/external_vk_image_backing.h +++ b/gpu/command_buffer/service/external_vk_image_backing.h
@@ -8,11 +8,11 @@ #include <memory> #include <vector> -#include "base/files/scoped_file.h" #include "components/viz/common/gpu/vulkan_context_provider.h" #include "gpu/command_buffer/service/shared_context_state.h" #include "gpu/command_buffer/service/shared_image_backing.h" #include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/vulkan/semaphore_handle.h" #include "gpu/vulkan/vulkan_device_queue.h" namespace gpu { @@ -47,13 +47,14 @@ // Notifies the backing that an access will start. Return false if there is // currently any other conflict access in progress. Otherwise, returns true - // and semaphore fds which will ne be waited on before accessing. - bool BeginAccess(bool readonly, std::vector<base::ScopedFD>* semaphore_fds); + // and semaphore handles which will be waited on before accessing. + bool BeginAccess(bool readonly, + std::vector<SemaphoreHandle>* semaphore_handles); // Notifies the backing that an access has ended. The representation must - // provide a semaphore fd that has been signalled at the end of the write + // provide a semaphore handle that has been signaled at the end of the write // access. - void EndAccess(bool readonly, base::ScopedFD semaphore_fd); + void EndAccess(bool readonly, SemaphoreHandle semaphore_handle); // SharedImageBacking implementation. bool IsCleared() const override; @@ -78,8 +79,8 @@ SharedContextState* const context_state_; VkImage image_; VkDeviceMemory memory_; - base::ScopedFD write_semaphore_fd_; - std::vector<base::ScopedFD> read_semaphore_fds_; + SemaphoreHandle write_semaphore_handle_; + std::vector<SemaphoreHandle> read_semaphore_handles_; size_t memory_size_; bool is_cleared_ = false; VkFormat vk_format_;
diff --git a/gpu/command_buffer/service/external_vk_image_factory.cc b/gpu/command_buffer/service/external_vk_image_factory.cc index 927ae1c..751e1ce 100644 --- a/gpu/command_buffer/service/external_vk_image_factory.cc +++ b/gpu/command_buffer/service/external_vk_image_factory.cc
@@ -154,13 +154,13 @@ ExternalVkImageBacking* vk_backing = static_cast<ExternalVkImageBacking*>(backing.get()); - std::vector<base::ScopedFD> fds; - if (!vk_backing->BeginAccess(false /* readonly */, &fds)) { + std::vector<SemaphoreHandle> handles; + if (!vk_backing->BeginAccess(false /* readonly */, &handles)) { LOG(ERROR) << "Failed to request write access of backing."; return nullptr; } - DCHECK(fds.empty()); + DCHECK(handles.empty()); // Create backend render target from the VkImage. GrVkAlloc alloc(vk_backing->memory(), 0 /* offset */, @@ -179,14 +179,15 @@ surface->writePixels(pixmap, 0, 0); VkSemaphore semaphore = vk_backing->CreateExternalVkSemaphore(); - base::ScopedFD fd; auto* vk_implementation = context_state_->vk_context_provider()->GetVulkanImplementation(); VkDevice device = context_state_->vk_context_provider() ->GetDeviceQueue() ->GetVulkanDevice(); - if (!vk_implementation->GetSemaphoreFdKHR(device, semaphore, &fd)) { - LOG(ERROR) << "GetSemaphoreFdKHR failed.."; + SemaphoreHandle semaphore_handle = + vk_implementation->GetSemaphoreHandle(device, semaphore); + if (!semaphore_handle.is_valid()) { + LOG(ERROR) << "GetSemaphoreHandle() failed."; vkDestroySemaphore(device, semaphore, nullptr /* pAllocator */); return nullptr; } @@ -199,7 +200,7 @@ vkDestroySemaphore(device, semaphore, nullptr /* pAllocator */); return nullptr; } - vk_backing->EndAccess(false /* readonly */, std::move(fd)); + vk_backing->EndAccess(false /* readonly */, std::move(semaphore_handle)); VkQueue queue = context_state_->vk_context_provider()->GetDeviceQueue()->GetVulkanQueue(); // TODO(https://crbug.com/932260): avoid blocking CPU thread.
diff --git a/gpu/command_buffer/service/external_vk_image_gl_representation.cc b/gpu/command_buffer/service/external_vk_image_gl_representation.cc index 2103d35..addff9d 100644 --- a/gpu/command_buffer/service/external_vk_image_gl_representation.cc +++ b/gpu/command_buffer/service/external_vk_image_gl_representation.cc
@@ -47,13 +47,13 @@ mode == GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM); const bool readonly = (mode == GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - std::vector<base::ScopedFD> fds; + std::vector<SemaphoreHandle> handles; - if (!backing_impl()->BeginAccess(readonly, &fds)) + if (!backing_impl()->BeginAccess(readonly, &handles)) return false; - for (auto& fd : fds) { - GLuint gl_semaphore = ImportVkSemaphoreIntoGL(std::move(fd)); + for (auto& handle : handles) { + GLuint gl_semaphore = ImportVkSemaphoreIntoGL(std::move(handle)); if (gl_semaphore) { GLenum src_layout = GL_LAYOUT_COLOR_ATTACHMENT_EXT; api()->glWaitSemaphoreEXTFn(gl_semaphore, 0, nullptr, 1, @@ -91,23 +91,23 @@ return; } - base::ScopedFD fd; - bool result = - vk_implementation()->GetSemaphoreFdKHR(vk_device(), semaphore, &fd); + SemaphoreHandle semaphore_handle = + vk_implementation()->GetSemaphoreHandle(vk_device(), semaphore); vkDestroySemaphore(backing_impl()->device(), semaphore, nullptr); - if (!result) { + if (!semaphore_handle.is_valid()) { LOG(FATAL) << "Unable to export VkSemaphore into GL in " << "ExternalVkImageGlRepresentation for synchronization with " << "Vulkan"; return; } - base::ScopedFD dup_fd(HANDLE_EINTR(dup(fd.get()))); - GLuint gl_semaphore = ImportVkSemaphoreIntoGL(std::move(dup_fd)); + SemaphoreHandle dup_semaphore_handle = semaphore_handle.Duplicate(); + GLuint gl_semaphore = + ImportVkSemaphoreIntoGL(std::move(dup_semaphore_handle)); if (!gl_semaphore) { - // TODO(crbug.com/933452): We should be able to handle this failure more - // gracefully rather than shutting down the whole process. + // TODO(crbug.com/933452): We should be able to semaphore_handle this + // failure more gracefully rather than shutting down the whole process. LOG(FATAL) << "Unable to export VkSemaphore into GL in " << "ExternalVkImageGlRepresentation for synchronization with " << "Vulkan"; @@ -118,13 +118,14 @@ api()->glSignalSemaphoreEXTFn(gl_semaphore, 0, nullptr, 1, &texture_service_id_, &dst_layout); api()->glDeleteSemaphoresEXTFn(1, &gl_semaphore); - backing_impl()->EndAccess(readonly, std::move(fd)); + backing_impl()->EndAccess(readonly, std::move(semaphore_handle)); } GLuint ExternalVkImageGlRepresentation::ImportVkSemaphoreIntoGL( - base::ScopedFD fd) { - if (!fd.is_valid()) + SemaphoreHandle handle) { + if (!handle.is_valid()) return 0; + base::ScopedFD fd = handle.TakeHandle(); gl::GLApi* api = gl::g_current_gl_context; GLuint gl_semaphore; api->glGenSemaphoresEXTFn(1, &gl_semaphore);
diff --git a/gpu/command_buffer/service/external_vk_image_gl_representation.h b/gpu/command_buffer/service/external_vk_image_gl_representation.h index ac2c585..13a5e32 100644 --- a/gpu/command_buffer/service/external_vk_image_gl_representation.h +++ b/gpu/command_buffer/service/external_vk_image_gl_representation.h
@@ -55,7 +55,7 @@ gl::GLApi* api() { return gl::g_current_gl_context; } - GLuint ImportVkSemaphoreIntoGL(base::ScopedFD fd); + GLuint ImportVkSemaphoreIntoGL(SemaphoreHandle handle); void DestroyEndAccessSemaphore(); gles2::Texture* texture_ = nullptr;
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/external_vk_image_skia_representation.cc index 7c08268..34b802d2 100644 --- a/gpu/command_buffer/service/external_vk_image_skia_representation.cc +++ b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
@@ -92,14 +92,13 @@ DestroySemaphores(std::move(begin_access_semaphores_), begin_access_fence_); begin_access_semaphores_.clear(); - std::vector<base::ScopedFD> fds; - if (!backing_impl()->BeginAccess(readonly, &fds)) + std::vector<SemaphoreHandle> handles; + if (!backing_impl()->BeginAccess(readonly, &handles)) return nullptr; - for (auto& fd : fds) { - VkSemaphore semaphore = VK_NULL_HANDLE; - vk_implementation()->ImportSemaphoreFdKHR(vk_device(), std::move(fd), - &semaphore); + for (auto& handle : handles) { + VkSemaphore semaphore = vk_implementation()->ImportSemaphoreHandle( + vk_device(), std::move(handle)); if (semaphore != VK_NULL_HANDLE) begin_access_semaphores_.push_back(semaphore); } @@ -146,14 +145,14 @@ end_access_semaphore_ = VK_NULL_HANDLE; } - base::ScopedFD fd; + SemaphoreHandle handle; if (end_access_semaphore_ != VK_NULL_HANDLE) { - if (!vk_implementation()->GetSemaphoreFdKHR(vk_device(), - end_access_semaphore_, &fd)) { - LOG(FATAL) << "Failed to get fd from a semaphore."; - } + handle = vk_implementation()->GetSemaphoreHandle(vk_device(), + end_access_semaphore_); + if (!handle.is_valid()) + LOG(FATAL) << "Failed to get handle from a semaphore."; } - backing_impl()->EndAccess(readonly, std::move(fd)); + backing_impl()->EndAccess(readonly, std::move(handle)); } void ExternalVkImageSkiaRepresentation::DestroySemaphores(
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc index 072234b..f7d5e06 100644 --- a/gpu/command_buffer/service/shared_context_state.cc +++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -145,10 +145,16 @@ DCHECK(context_->IsCurrent(nullptr)); + bool use_passthrough_cmd_decoder = + gpu_preferences.use_passthrough_cmd_decoder && + gles2::PassthroughCommandDecoderSupported(); + // Virtualized contexts don't work with passthrough command decoder. + // See https://crbug.com/914976 + DCHECK(!use_passthrough_cmd_decoder || !use_virtualized_gl_contexts_); + feature_info_ = std::move(feature_info); feature_info_->Initialize(gpu::CONTEXT_TYPE_OPENGLES2, - gpu_preferences.use_passthrough_cmd_decoder && - gles2::PassthroughCommandDecoderSupported(), + use_passthrough_cmd_decoder, gles2::DisallowedFeatures()); auto* api = gl::g_current_gl_context;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc index e5272cd..0dbdf0da 100644 --- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc +++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -158,7 +158,9 @@ (*surface) = nullptr; // Export a sync fd from the semaphore. - vk_implementation->GetSemaphoreFdKHR(vk_device, vk_semaphore, sync_fd); + SemaphoreHandle semaphore_handle = + vk_implementation->GetSemaphoreHandle(vk_device, vk_semaphore); + *sync_fd = semaphore_handle.TakeHandle(); // TODO(vikassoni): We need to wait for the queue submission to complete // before we can destroy the semaphore. This will decrease the performance. @@ -488,8 +490,11 @@ for (size_t i = 0; i < sync_fds.size(); ++i) { DCHECK(sync_fds[i].is_valid()); - if (!vk_implementation()->ImportSemaphoreFdKHR( - vk_device(), std::move(sync_fds[i]), &semaphores[i])) { + semaphores[i] = vk_implementation()->ImportSemaphoreHandle( + vk_device(), + SemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + std::move(sync_fds[i]))); + if (semaphores[i] == VK_NULL_HANDLE) { failed_to_insert_semaphores = true; break; } @@ -561,10 +566,12 @@ // We need to wait only if there is a valid fd. if (sync_fd.is_valid()) { // Import the above sync fd into a semaphore. - if (!vk_implementation()->ImportSemaphoreFdKHR( - vk_device(), std::move(sync_fd), &semaphore)) { + semaphore = vk_implementation()->ImportSemaphoreHandle( + vk_device(), + SemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + std::move(sync_fd))); + if (semaphore == VK_NULL_HANDLE) return nullptr; - } // Submit wait semaphore to the queue. Note that Skia uses the same queue // exposed by vk_queue(), so this will work due to Vulkan queue ordering.
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc index 925ba5e..91cd94a 100644 --- a/gpu/command_buffer/tests/fuzzer_main.cc +++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -362,8 +362,20 @@ config_.workarounds, gpu_feature_info); command_buffer_.reset(new CommandBufferDirect()); + if (gpu_preferences_.use_passthrough_cmd_decoder) { + // Virtualized contexts don't work with passthrough command decoder. + // See https://crbug.com/914976 + config_.workarounds.use_virtualized_gl_contexts = false; + } + scoped_refptr<gl::GLContext> shared_context; + if (config_.workarounds.use_virtualized_gl_contexts) { + shared_context = context_; + } else { + shared_context = CreateContext(); + } + shared_context->MakeCurrent(surface_.get()); context_state_ = base::MakeRefCounted<SharedContextState>( - share_group_, surface_, context_, + share_group_, surface_, std::move(shared_context), config_.workarounds.use_virtualized_gl_contexts, base::DoNothing()); context_state_->InitializeGrContext(config_.workarounds, nullptr); context_state_->InitializeGL(gpu_preferences_, feature_info); @@ -391,6 +403,7 @@ gfx::ColorSpace::CreateSRGB(), usage); } + context_->MakeCurrent(surface_.get()); #if defined(GPU_FUZZER_USE_RASTER_DECODER) CHECK(feature_info->feature_flags().chromium_raster_transport); auto* context = context_state_->context(); @@ -462,7 +475,7 @@ decoder_.reset(); if (!context_lost) - context_state_->MakeCurrent(nullptr); + context_lost = !context_state_->MakeCurrent(nullptr); shared_image_factory_->DestroyAllSharedImages(!context_lost); shared_image_factory_.reset(); @@ -533,18 +546,22 @@ CreateTransferBuffer(kTinyTransferBufferSize, 5); } - void InitContext() { -#if !defined(GPU_FUZZER_USE_STUB) - context_ = new gl::GLContextEGL(share_group_.get()); - context_->Initialize(surface_.get(), config_.gl_context_attribs); + scoped_refptr<gl::GLContext> CreateContext() { +#if defined(GPU_FUZZER_USE_STUB) + auto stub = base::MakeRefCounted<gl::GLContextStub>(share_group_.get()); + stub->SetGLVersionString(config_.version); + stub->SetExtensionsString(config_.extensions.c_str()); + stub->SetUseStubApi(true); + return stub; #else - scoped_refptr<gl::GLContextStub> context_stub = - new gl::GLContextStub(share_group_.get()); - context_stub->SetGLVersionString(config_.version); - context_stub->SetExtensionsString(config_.extensions.c_str()); - context_stub->SetUseStubApi(true); - context_ = context_stub; + auto context = base::MakeRefCounted<gl::GLContextEGL>(share_group_.get()); + context->Initialize(surface_.get(), config_.gl_context_attribs); + return context; #endif + } + + void InitContext() { + context_ = CreateContext(); // When not using the passthrough decoder, ANGLE should not be generating // errors (the decoder should prevent those from happening). We register a
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc index bd26caf..8f25b70 100644 --- a/gpu/ipc/in_process_command_buffer.cc +++ b/gpu/ipc/in_process_command_buffer.cc
@@ -448,6 +448,11 @@ use_virtualized_gl_context_ |= context_group_->feature_info()->workarounds().use_virtualized_gl_contexts; + if (context_group_->use_passthrough_cmd_decoder()) { + // Virtualized contexts don't work with passthrough command decoder. + // See https://crbug.com/914976 + use_virtualized_gl_context_ = false; + } // TODO(sunnyps): Should this use ScopedCrashKey instead? crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1" : "0");
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc index 052f9fa9..3f3d6e4c 100644 --- a/gpu/ipc/service/gpu_channel_manager.cc +++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -369,6 +369,9 @@ scoped_refptr<gl::GLShareGroup> share_group; if (use_passthrough_decoder) { share_group = new gl::GLShareGroup(); + // Virtualized contexts don't work with passthrough command decoder. + // See https://crbug.com/914976 + use_virtualized_gl_contexts = false; } else { share_group = share_group_; }
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn index d3751cd..bb7d7e3 100644 --- a/gpu/vulkan/BUILD.gn +++ b/gpu/vulkan/BUILD.gn
@@ -20,6 +20,8 @@ output_name = "vulkan_wrapper" sources = [ + "semaphore_handle.cc", + "semaphore_handle.h", "vulkan_command_buffer.cc", "vulkan_command_buffer.h", "vulkan_command_pool.cc", @@ -51,11 +53,21 @@ "//base", "//ui/gfx", ] - + public_deps = [] data_deps = [] + + if (is_posix) { + sources += [ + "vulkan_posix_util.cc", + "vulkan_posix_util.h", + ] + } + if (is_fuchsia) { sources += [ "fuchsia/vulkan_fuchsia_ext.h" ] + public_deps += [ "//third_party/fuchsia-sdk/sdk:zx" ] + data_deps += [ "//third_party/fuchsia-sdk:vulkan_base" ] # VulkanInstance enables validation layer in Debug builds and when DCHECKs
diff --git a/gpu/vulkan/android/vulkan_android_unittests.cc b/gpu/vulkan/android/vulkan_android_unittests.cc index 9f00c8e..2c8ae187 100644 --- a/gpu/vulkan/android/vulkan_android_unittests.cc +++ b/gpu/vulkan/android/vulkan_android_unittests.cc
@@ -6,7 +6,6 @@ #include "base/android/android_hardware_buffer_compat.h" #include "base/android/scoped_hardware_buffer_handle.h" -#include "base/files/scoped_file.h" #include "components/viz/common/gpu/vulkan_in_process_context_provider.h" #include "gpu/vulkan/android/vulkan_implementation_android.h" #include "gpu/vulkan/vulkan_function_pointers.h" @@ -85,16 +84,15 @@ EXPECT_TRUE(vk_implementation_->SubmitSignalSemaphore( vk_context_provider_->GetDeviceQueue()->GetVulkanQueue(), semaphore1)); - // Export a sync fd from the semaphore. - base::ScopedFD sync_fd; - EXPECT_TRUE( - vk_implementation_->GetSemaphoreFdKHR(vk_device_, semaphore1, &sync_fd)); - EXPECT_GT(sync_fd.get(), -1); + // Export a handle from the semaphore. + SemaphoreHandle handle = + vk_implementation_->GetSemaphoreHandle(vk_device_, semaphore1); + EXPECT_TRUE(handle.is_valid()); - // Import the above sync fd into a new semaphore. - VkSemaphore semaphore2; - EXPECT_TRUE(vk_implementation_->ImportSemaphoreFdKHR( - vk_device_, std::move(sync_fd), &semaphore2)); + // Import the above semaphore handle into a new semaphore. + VkSemaphore semaphore2 = + vk_implementation_->ImportSemaphoreHandle(vk_device_, std::move(handle)); + EXPECT_NE(semaphore2, static_cast<VkSemaphore>(VK_NULL_HANDLE)); // Wait for the device to be idle. result = vkDeviceWaitIdle(vk_device_);
diff --git a/gpu/vulkan/android/vulkan_implementation_android.cc b/gpu/vulkan/android/vulkan_implementation_android.cc index a1e407b..f532ab8 100644 --- a/gpu/vulkan/android/vulkan_implementation_android.cc +++ b/gpu/vulkan/android/vulkan_implementation_android.cc
@@ -11,6 +11,7 @@ #include "gpu/vulkan/vulkan_device_queue.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_instance.h" +#include "gpu/vulkan/vulkan_posix_util.h" #include "gpu/vulkan/vulkan_surface.h" #include "ui/gfx/gpu_fence.h" @@ -106,6 +107,21 @@ return nullptr; } +VkSemaphore VulkanImplementationAndroid::ImportSemaphoreHandle( + VkDevice vk_device, + SemaphoreHandle sync_handle) { + return ImportVkSemaphoreHandlePosix(vk_device, std::move(sync_handle)); +} + +SemaphoreHandle VulkanImplementationAndroid::GetSemaphoreHandle( + VkDevice vk_device, + VkSemaphore vk_semaphore) { + // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT specifies a POSIX file + // descriptor handle to a Linux Sync File or Android Fence object. + return GetVkSemaphoreHandlePosix( + vk_device, vk_semaphore, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT); +} + bool VulkanImplementationAndroid::CreateVkImageAndImportAHB( const VkDevice& vk_device, const VkPhysicalDevice& vk_physical_device,
diff --git a/gpu/vulkan/android/vulkan_implementation_android.h b/gpu/vulkan/android/vulkan_implementation_android.h index 8a856209..a84adde 100644 --- a/gpu/vulkan/android/vulkan_implementation_android.h +++ b/gpu/vulkan/android/vulkan_implementation_android.h
@@ -35,6 +35,10 @@ std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( VkDevice vk_device, VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle handle) override; + SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) override; bool CreateVkImageAndImportAHB( const VkDevice& vk_device, const VkPhysicalDevice& vk_physical_device,
diff --git a/gpu/vulkan/semaphore_handle.cc b/gpu/vulkan/semaphore_handle.cc new file mode 100644 index 0000000..00ee966 --- /dev/null +++ b/gpu/vulkan/semaphore_handle.cc
@@ -0,0 +1,60 @@ +// 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 "gpu/vulkan/semaphore_handle.h" + +#if defined(OS_POSIX) +#include <unistd.h> +#include "base/posix/eintr_wrapper.h" +#endif + +#if defined(OS_FUCHSIA) +#include "base/fuchsia/fuchsia_logging.h" +#endif + +#if defined(OS_WIN) +#include <windows.h> +#endif + +namespace gpu { + +SemaphoreHandle::SemaphoreHandle() = default; +SemaphoreHandle::SemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBits type, + PlatformHandle handle) + : type_(type), handle_(std::move(handle)) {} +SemaphoreHandle::SemaphoreHandle(SemaphoreHandle&&) = default; + +SemaphoreHandle::~SemaphoreHandle() = default; + +SemaphoreHandle& SemaphoreHandle::operator=(SemaphoreHandle&&) = default; + +SemaphoreHandle SemaphoreHandle::Duplicate() const { + if (!is_valid()) + return SemaphoreHandle(); + +#if defined(OS_POSIX) + return SemaphoreHandle(type_, + base::ScopedFD(HANDLE_EINTR(dup(handle_.get())))); +#elif defined(OS_WIN) + HANDLE handle_dup; + if (!::DuplicateHandle(::GetCurrentProcess(), handle_.get(), + ::GetCurrentProcess(), &handle_dup, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + return SemaphoreHandle(); + } + return SemaphoreHandle(type_, base::win::ScopedHandle(handle_dup)); +#elif defined(OS_FUCHSIA) + zx::event event_dup; + zx_status_t status = handle_.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_dup); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) << "zx_handle_duplicate"; + return SemaphoreHandle(); + } + return SemaphoreHandle(type_, std::move(event_dup)); +#else +#error Unsupported OS +#endif +} + +} // namespace gpu
diff --git a/gpu/vulkan/semaphore_handle.h b/gpu/vulkan/semaphore_handle.h new file mode 100644 index 0000000..c59ad7a --- /dev/null +++ b/gpu/vulkan/semaphore_handle.h
@@ -0,0 +1,71 @@ +// 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 GPU_VULKAN_SEMAPHORE_HANDLE_H_ +#define GPU_VULKAN_SEMAPHORE_HANDLE_H_ + +#include <vulkan/vulkan.h> +#include <utility> + +#include "base/macros.h" +#include "build/build_config.h" +#include "gpu/vulkan/vulkan_export.h" + +#if defined(OS_POSIX) +#include "base/files/scoped_file.h" +#endif + +#if defined(OS_FUCHSIA) +#include <lib/zx/event.h> +#endif + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#endif + +namespace gpu { + +// Thin wrapper around platform-specific handles for VkSemaphores. +// Note that handle transference depends on a handle type. +// SYNC_FD handles that use copy transference, while reference transference is +// used other handles types. +class VULKAN_EXPORT SemaphoreHandle { + public: +#if defined(OS_POSIX) + using PlatformHandle = base::ScopedFD; +#elif defined(OS_WIN) + using PlatformHandle = base::win::ScopedHandle; +#elif defined(OS_FUCHSIA) + using PlatformHandle = zx::event; +#endif + + SemaphoreHandle(); + SemaphoreHandle(VkExternalSemaphoreHandleTypeFlagBits type, + PlatformHandle handle); + SemaphoreHandle(SemaphoreHandle&&); + + ~SemaphoreHandle(); + + SemaphoreHandle& operator=(SemaphoreHandle&&); + + VkExternalSemaphoreHandleTypeFlagBits vk_handle_type() { return type_; } + + bool is_valid() const { return handle_.is_valid(); } + + // Returns underlying platform-specific handle for the semaphore. is_valid() + // becomes false after this function returns. + PlatformHandle TakeHandle() { return std::move(handle_); } + + SemaphoreHandle Duplicate() const; + + private: + VkExternalSemaphoreHandleTypeFlagBits type_; + PlatformHandle handle_; + + DISALLOW_COPY_AND_ASSIGN(SemaphoreHandle); +}; + +} // namespace gpu + +#endif // GPU_VULKAN_SEMAPHORE_HANDLE_H_ \ No newline at end of file
diff --git a/gpu/vulkan/vulkan_function_pointers.cc b/gpu/vulkan/vulkan_function_pointers.cc index 7954a2d9..8ae9b81 100644 --- a/gpu/vulkan/vulkan_function_pointers.cc +++ b/gpu/vulkan/vulkan_function_pointers.cc
@@ -279,6 +279,12 @@ if (!vkGetImageMemoryRequirementsFn) return false; + vkGetImageSubresourceLayoutFn = + reinterpret_cast<PFN_vkGetImageSubresourceLayout>( + vkGetDeviceProcAddrFn(vk_device, "vkGetImageSubresourceLayout")); + if (!vkGetImageSubresourceLayoutFn) + return false; + vkResetFencesFn = reinterpret_cast<PFN_vkResetFences>( vkGetDeviceProcAddrFn(vk_device, "vkResetFences")); if (!vkResetFencesFn) @@ -328,6 +334,23 @@ #endif +#if defined(OS_FUCHSIA) + vkImportSemaphoreZirconHandleFUCHSIAFn = + reinterpret_cast<PFN_vkImportSemaphoreZirconHandleFUCHSIA>( + vkGetDeviceProcAddrFn(vk_device, + "vkImportSemaphoreZirconHandleFUCHSIA")); + if (!vkImportSemaphoreZirconHandleFUCHSIAFn) + return false; + + vkGetSemaphoreZirconHandleFUCHSIAFn = + reinterpret_cast<PFN_vkGetSemaphoreZirconHandleFUCHSIA>( + vkGetDeviceProcAddrFn(vk_device, + "vkGetSemaphoreZirconHandleFUCHSIA")); + if (!vkGetSemaphoreZirconHandleFUCHSIAFn) + return false; + +#endif + // Queue functions vkQueueSubmitFn = reinterpret_cast<PFN_vkQueueSubmit>( vkGetDeviceProcAddrFn(vk_device, "vkQueueSubmit"));
diff --git a/gpu/vulkan/vulkan_function_pointers.h b/gpu/vulkan/vulkan_function_pointers.h index f4ca267..1dfe417 100644 --- a/gpu/vulkan/vulkan_function_pointers.h +++ b/gpu/vulkan/vulkan_function_pointers.h
@@ -13,13 +13,17 @@ #include <vulkan/vulkan.h> +#include "base/native_library.h" +#include "build/build_config.h" +#include "gpu/vulkan/vulkan_export.h" + #if defined(OS_ANDROID) #include <vulkan/vulkan_android.h> #endif -#include "base/native_library.h" -#include "build/build_config.h" -#include "gpu/vulkan/vulkan_export.h" +#if defined(OS_FUCHSIA) +#include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h" +#endif namespace gpu { @@ -106,6 +110,7 @@ PFN_vkGetDeviceQueue vkGetDeviceQueueFn = nullptr; PFN_vkGetFenceStatus vkGetFenceStatusFn = nullptr; PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirementsFn = nullptr; + PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayoutFn = nullptr; PFN_vkResetFences vkResetFencesFn = nullptr; PFN_vkUpdateDescriptorSets vkUpdateDescriptorSetsFn = nullptr; PFN_vkWaitForFences vkWaitForFencesFn = nullptr; @@ -127,6 +132,13 @@ PFN_vkGetMemoryFdKHR vkGetMemoryFdKHRFn = nullptr; #endif +#if defined(OS_FUCHSIA) + PFN_vkImportSemaphoreZirconHandleFUCHSIA + vkImportSemaphoreZirconHandleFUCHSIAFn = nullptr; + PFN_vkGetSemaphoreZirconHandleFUCHSIA vkGetSemaphoreZirconHandleFUCHSIAFn = + nullptr; +#endif + // Queue functions PFN_vkQueueSubmit vkQueueSubmitFn = nullptr; PFN_vkQueueWaitIdle vkQueueWaitIdleFn = nullptr; @@ -236,6 +248,8 @@ #define vkGetFenceStatus gpu::GetVulkanFunctionPointers()->vkGetFenceStatusFn #define vkGetImageMemoryRequirements \ gpu::GetVulkanFunctionPointers()->vkGetImageMemoryRequirementsFn +#define vkGetImageSubresourceLayout \ + gpu::GetVulkanFunctionPointers()->vkGetImageSubresourceLayoutFn #define vkResetFences gpu::GetVulkanFunctionPointers()->vkResetFencesFn #define vkUpdateDescriptorSets \ gpu::GetVulkanFunctionPointers()->vkUpdateDescriptorSetsFn @@ -258,6 +272,13 @@ #define vkGetMemoryFdKHR gpu::GetVulkanFunctionPointers()->vkGetMemoryFdKHRFn #endif +#if defined(OS_FUCHSIA) +#define vkImportSemaphoreZirconHandleFUCHSIA \ + gpu::GetVulkanFunctionPointers()->vkImportSemaphoreZirconHandleFUCHSIAFn +#define vkGetSemaphoreZirconHandleFUCHSIA \ + gpu::GetVulkanFunctionPointers()->vkGetSemaphoreZirconHandleFUCHSIAFn +#endif + // Queue functions #define vkQueueSubmit gpu::GetVulkanFunctionPointers()->vkQueueSubmitFn #define vkQueueWaitIdle gpu::GetVulkanFunctionPointers()->vkQueueWaitIdleFn
diff --git a/gpu/vulkan/vulkan_implementation.cc b/gpu/vulkan/vulkan_implementation.cc index b40b4e3..d47b8d5 100644 --- a/gpu/vulkan/vulkan_implementation.cc +++ b/gpu/vulkan/vulkan_implementation.cc
@@ -66,72 +66,4 @@ return true; } -#if defined(OS_LINUX) || defined(OS_ANDROID) -bool VulkanImplementation::ImportSemaphoreFdKHR(VkDevice vk_device, - base::ScopedFD sync_fd, - VkSemaphore* vk_semaphore) { - if (!sync_fd.is_valid()) - return false; - - VkSemaphore semaphore = VK_NULL_HANDLE; - VkSemaphoreCreateInfo info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - VkResult result = vkCreateSemaphore(vk_device, &info, nullptr, &semaphore); - if (result != VK_SUCCESS) - return false; - - VkImportSemaphoreFdInfoKHR import = { - VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR}; - import.semaphore = semaphore; -#if defined(OS_ANDROID) - import.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR; - // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT specifies a POSIX file - // descriptor handle to a Linux Sync File or Android Fence object. - import.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; -#else - import.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; -#endif - import.fd = sync_fd.get(); - - result = vkImportSemaphoreFdKHR(vk_device, &import); - if (result != VK_SUCCESS) { - vkDestroySemaphore(vk_device, semaphore, nullptr); - return false; - } - - // If import is successful, the VkSemaphore object takes the ownership of fd. - ignore_result(sync_fd.release()); - *vk_semaphore = semaphore; - return true; -} - -bool VulkanImplementation::GetSemaphoreFdKHR(VkDevice vk_device, - VkSemaphore vk_semaphore, - base::ScopedFD* sync_fd) { - // Create VkSemaphoreGetFdInfoKHR structure. - VkSemaphoreGetFdInfoKHR info = {VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; - info.semaphore = vk_semaphore; -#if defined(OS_ANDROID) - // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT specifies a POSIX file - // descriptor handle to a Linux Sync File or Android Fence object. - info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; -#else - info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; -#endif - - // Create a new sync fd from the semaphore. - int fd = -1; - VkResult result = vkGetSemaphoreFdKHR(vk_device, &info, &fd); - if (result != VK_SUCCESS) { - LOG(ERROR) << "vkGetSemaphoreFdKHR failed : " << result; - sync_fd->reset(-1); - return false; - } - - // Transfer the ownership of the fd to the caller. - sync_fd->reset(fd); - return true; -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - } // namespace gpu
diff --git a/gpu/vulkan/vulkan_implementation.h b/gpu/vulkan/vulkan_implementation.h index 39992f6..0d72db4 100644 --- a/gpu/vulkan/vulkan_implementation.h +++ b/gpu/vulkan/vulkan_implementation.h
@@ -12,13 +12,10 @@ #include "base/macros.h" #include "build/build_config.h" +#include "gpu/vulkan/semaphore_handle.h" #include "gpu/vulkan/vulkan_export.h" #include "ui/gfx/native_widget_types.h" -#if defined(OS_POSIX) -#include "base/files/scoped_file.h" -#endif - #if defined(OS_ANDROID) #include "base/android/scoped_hardware_buffer_handle.h" #include "ui/gfx/geometry/size.h" @@ -93,21 +90,16 @@ return SubmitWaitSemaphores(vk_queue, {vk_semaphore}, vk_fence); } -#if defined(OS_LINUX) || defined(OS_ANDROID) - // Import a VkSemaphore from a POSIX sync file descriptor. Importing a - // semaphore payload from a file descriptor transfers ownership of the file - // descriptor from the application to the Vulkan implementation. The - // application must not perform any operations on the file descriptor after a - // successful import. - virtual bool ImportSemaphoreFdKHR(VkDevice vk_device, - base::ScopedFD sync_fd, - VkSemaphore* vk_semaphore); + // Import a VkSemaphore from a platform-specific handle. + // Handle types that don't allow permanent import are imported with temporary + // permanence (VK_SEMAPHORE_IMPORT_TEMPORARY_BIT). + virtual VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle handle) = 0; - // Export a sync fd representing the payload of a semaphore. - virtual bool GetSemaphoreFdKHR(VkDevice vk_device, - VkSemaphore vk_semaphore, - base::ScopedFD* sync_fd); -#endif + // Export a platform-specific handle for a Vulkan semaphore. Returns a null + // handle in case of a failure. + virtual SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) = 0; #if defined(OS_ANDROID) // Create a VkImage, import Android AHardwareBuffer object created outside of
diff --git a/gpu/vulkan/vulkan_posix_util.cc b/gpu/vulkan/vulkan_posix_util.cc new file mode 100644 index 0000000..d55eb51 --- /dev/null +++ b/gpu/vulkan/vulkan_posix_util.cc
@@ -0,0 +1,65 @@ +// 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 "gpu/vulkan/vulkan_posix_util.h" + +#include "gpu/vulkan/vulkan_function_pointers.h" + +namespace gpu { + +VkSemaphore ImportVkSemaphoreHandlePosix(VkDevice vk_device, + SemaphoreHandle handle) { + auto handle_type = handle.vk_handle_type(); + if (!handle.is_valid() || + (handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT && + handle_type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT)) { + return VK_NULL_HANDLE; + } + + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + VkResult result = vkCreateSemaphore(vk_device, &info, nullptr, &semaphore); + if (result != VK_SUCCESS) + return VK_NULL_HANDLE; + + base::ScopedFD fd = handle.TakeHandle(); + VkImportSemaphoreFdInfoKHR import = { + VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR}; + import.semaphore = semaphore; + if (handle_type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) + import.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR; + import.handleType = handle_type; + import.fd = fd.get(); + + result = vkImportSemaphoreFdKHR(vk_device, &import); + if (result != VK_SUCCESS) { + vkDestroySemaphore(vk_device, semaphore, nullptr); + return VK_NULL_HANDLE; + } + + // If import is successful, the VkSemaphore takes the ownership of the fd. + ignore_result(fd.release()); + + return semaphore; +} + +SemaphoreHandle GetVkSemaphoreHandlePosix( + VkDevice vk_device, + VkSemaphore vk_semaphore, + VkExternalSemaphoreHandleTypeFlagBits handle_type) { + VkSemaphoreGetFdInfoKHR info = {VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR}; + info.semaphore = vk_semaphore; + info.handleType = handle_type; + + int fd = -1; + VkResult result = vkGetSemaphoreFdKHR(vk_device, &info, &fd); + if (result != VK_SUCCESS) { + LOG(ERROR) << "vkGetSemaphoreFdKHR failed : " << result; + return SemaphoreHandle(); + } + + return SemaphoreHandle(handle_type, base::ScopedFD(fd)); +} + +} // namespace gpu
diff --git a/gpu/vulkan/vulkan_posix_util.h b/gpu/vulkan/vulkan_posix_util.h new file mode 100644 index 0000000..3b5351a --- /dev/null +++ b/gpu/vulkan/vulkan_posix_util.h
@@ -0,0 +1,28 @@ +// 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. +// +// This file contains helpers used by VulkanImplementation's on POSIX platforms +// to import/export Vulkan objects to POSIX file descriptors. They should not +// be used directly except in VulkanImplementation children. + +#ifndef GPU_VULKAN_VULKAN_POSIX_UTIL_H_ +#define GPU_VULKAN_VULKAN_POSIX_UTIL_H_ + +#include <vulkan/vulkan.h> + +#include "gpu/vulkan/semaphore_handle.h" +#include "gpu/vulkan/vulkan_export.h" + +namespace gpu { + +VULKAN_EXPORT VkSemaphore ImportVkSemaphoreHandlePosix(VkDevice vk_device, + SemaphoreHandle handle); +VULKAN_EXPORT SemaphoreHandle +GetVkSemaphoreHandlePosix(VkDevice vk_device, + VkSemaphore vk_semaphore, + VkExternalSemaphoreHandleTypeFlagBits handle_type); + +} // namespace gpu + +#endif // GPU_VULKAN_VULKAN_POSIX_UTIL_H_ \ No newline at end of file
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.cc b/gpu/vulkan/win32/vulkan_implementation_win32.cc index c36d7bc9..85201d6 100644 --- a/gpu/vulkan/win32/vulkan_implementation_win32.cc +++ b/gpu/vulkan/win32/vulkan_implementation_win32.cc
@@ -105,4 +105,17 @@ return nullptr; } +VkSemaphore VulkanImplementationWin32::ImportSemaphoreHandle( + VkDevice vk_device, + SemaphoreHandle handle) { + NOTIMPLEMENTED(); + return VK_NULL_HANDLE; +} + +SemaphoreHandle VulkanImplementationWin32::GetSemaphoreHandle( + VkDevice vk_device, + VkSemaphore vk_semaphore) { + return SemaphoreHandle(); +} + } // namespace gpu
diff --git a/gpu/vulkan/win32/vulkan_implementation_win32.h b/gpu/vulkan/win32/vulkan_implementation_win32.h index fa2b935..9768fdbe 100644 --- a/gpu/vulkan/win32/vulkan_implementation_win32.h +++ b/gpu/vulkan/win32/vulkan_implementation_win32.h
@@ -33,6 +33,10 @@ std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( VkDevice vk_device, VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle handle) override; + SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) override; private: VulkanInstance vulkan_instance_;
diff --git a/gpu/vulkan/x/vulkan_implementation_x11.cc b/gpu/vulkan/x/vulkan_implementation_x11.cc index f7e7ff8..be5a4523 100644 --- a/gpu/vulkan/x/vulkan_implementation_x11.cc +++ b/gpu/vulkan/x/vulkan_implementation_x11.cc
@@ -10,6 +10,7 @@ #include "base/optional.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_instance.h" +#include "gpu/vulkan/vulkan_posix_util.h" #include "gpu/vulkan/vulkan_surface.h" #include "ui/gfx/gpu_fence.h" #include "ui/gfx/x/x11_types.h" @@ -146,4 +147,17 @@ return nullptr; } +VkSemaphore VulkanImplementationX11::ImportSemaphoreHandle( + VkDevice vk_device, + SemaphoreHandle sync_handle) { + return ImportVkSemaphoreHandlePosix(vk_device, std::move(sync_handle)); +} + +SemaphoreHandle VulkanImplementationX11::GetSemaphoreHandle( + VkDevice vk_device, + VkSemaphore vk_semaphore) { + return GetVkSemaphoreHandlePosix( + vk_device, vk_semaphore, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT); +} + } // namespace gpu
diff --git a/gpu/vulkan/x/vulkan_implementation_x11.h b/gpu/vulkan/x/vulkan_implementation_x11.h index 869f63a..a252497 100644 --- a/gpu/vulkan/x/vulkan_implementation_x11.h +++ b/gpu/vulkan/x/vulkan_implementation_x11.h
@@ -35,6 +35,10 @@ std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( VkDevice vk_device, VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle handle) override; + SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) override; private: XDisplay* const x_display_;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index d7db0651..aaeee5c 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -3130,7 +3130,7 @@ # Max. pending time for builds. CQ considers builds pending >2h as timed # out: http://shortn/_8PaHsdYmlq. Keep this in sync. expiration_secs: 7200 # 2h - execution_timeout_secs: 10800 # 3h + execution_timeout_secs: 14400 # 4h swarming_tags: "vpython:native-python-wrapper" build_numbers: YES # Adds dimension: "builder:<builder name>" to ensure builder affinity.
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg index fd88dca..6d507cec 100644 --- a/infra/config/luci-milo.cfg +++ b/infra/config/luci-milo.cfg
@@ -1,3 +1,6 @@ +# See http://luci-config.appspot.com/schemas/projects:luci-milo.cfg for schema +# of this file and documentation. + logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/chromium.svg" headers { @@ -185,6 +188,11 @@ alt: "Blink" } links { + text: "chrome" + url: "/p/chrome/g/tryserver.chromium.chrome/builders" + alt: "Chrome" + } + links { text: "chromiumos" url: "/p/chromium/g/tryserver.chromium.chromiumos/builders" alt: "ChromiumOS"
diff --git a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.h b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.h index 12fee1f..3c1cf0d 100644 --- a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.h +++ b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.h
@@ -11,6 +11,15 @@ @class ChromeIdentity; +typedef NS_ENUM(NSInteger, TableViewAccountMode) { + // The cell can be tappable, and the colors are not dimmed. + TableViewAccountModeEnabled, + // The cell is not tappable, and the colors are not dimmed. + TableViewAccountModeNonTappable, + // The cell is not tappable, and the colors are dimmed. + TableViewAccountModeDisabled, +}; + // Item for account avatar, used everywhere an account cell is shown. @interface TableViewAccountItem : TableViewItem @@ -19,7 +28,8 @@ @property(nonatomic, copy) NSString* detailText; @property(nonatomic, assign) BOOL shouldDisplayError; @property(nonatomic, strong) ChromeIdentity* chromeIdentity; -@property(nonatomic, assign, getter=isEnabled) BOOL enabled; +// The default value is TableViewAccountModeEnabled. +@property(nonatomic, assign) TableViewAccountMode mode; @end
diff --git a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm index 66c763d..8009dbb 100644 --- a/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm +++ b/ios/chrome/browser/ui/authentication/cells/table_view_account_item.mm
@@ -34,7 +34,7 @@ if (self) { self.cellClass = [TableViewAccountCell class]; self.accessibilityTraits |= UIAccessibilityTraitButton; - _enabled = YES; + _mode = TableViewAccountModeEnabled; } return self; } @@ -57,8 +57,8 @@ UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor); } - if (self.isEnabled) { - cell.userInteractionEnabled = YES; + cell.userInteractionEnabled = self.mode == TableViewAccountModeEnabled; + if (self.mode != TableViewAccountModeDisabled) { cell.contentView.alpha = 1; UIImageView* accessoryImage = base::mac::ObjCCastStrict<UIImageView>(cell.accessoryView);
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm index 8bd70eb..0baa384 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
@@ -84,7 +84,13 @@ // Verifies that the content offset of the web view is set up at the correct // initial value when initially displaying a PDF. -- (void)testLongPDFInitialState { +// TODO(crbug.com/947536): Fails on iOS 12 devices. +#if !TARGET_IPHONE_SIMULATOR +#define MAYBE_testLongPDFInitialState DISABLED_testLongPDFInitialState +#else +#define MAYBE_testLongPDFInitialState testLongPDFInitialState +#endif +- (void)MAYBE_testLongPDFInitialState { web::test::SetUpFileBasedHttpServer(); GURL URL = web::test::HttpServer::MakeUrl( "http://ios/testing/data/http_server_files/two_pages.pdf");
diff --git a/ios/chrome/browser/ui/settings/sync/sync_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync/sync_settings_table_view_controller.mm index da1e4d9..83b8aa4 100644 --- a/ios/chrome/browser/ui/settings/sync/sync_settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/sync/sync_settings_table_view_controller.mm
@@ -374,7 +374,9 @@ [[TableViewAccountItem alloc] initWithType:ItemTypeAccount]; [self updateAccountItem:identityAccountItem withIdentity:identity]; - identityAccountItem.enabled = _syncSetupService->IsSyncEnabled(); + identityAccountItem.mode = _syncSetupService->IsSyncEnabled() + ? TableViewAccountModeEnabled + : TableViewAccountModeDisabled; ChromeIdentity* authenticatedIdentity = AuthenticationServiceFactory::GetForBrowserState(_browserState) ->GetAuthenticatedIdentity(); @@ -782,7 +784,9 @@ TableViewAccountItem* accountItem = base::mac::ObjCCastStrict<TableViewAccountItem>( [self.tableViewModel itemAtIndexPath:indexPath]); - accountItem.enabled = _syncSetupService->IsSyncEnabled(); + accountItem.mode = _syncSetupService->IsSyncEnabled() + ? TableViewAccountModeEnabled + : TableViewAccountModeDisabled; [accountsToReconfigure addObject:accountItem]; } [self reconfigureCellsForItems:accountsToReconfigure];
diff --git a/ios/web/shell/test/BUILD.gn b/ios/web/shell/test/BUILD.gn index c9664fac..eb07f38 100644 --- a/ios/web/shell/test/BUILD.gn +++ b/ios/web/shell/test/BUILD.gn
@@ -155,4 +155,6 @@ # Test support libraries. ":eg_tests+eg2", ] + + bundle_deps = [ "//ios/testing:http_server_bundle_data" ] }
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc index 1fa296a3..527cfaf 100644 --- a/media/renderers/paint_canvas_video_renderer.cc +++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -324,6 +324,8 @@ } ~VideoImageGenerator() override = default; + bool IsEligibleForAcceleratedDecoding() const override { return false; } + sk_sp<SkData> GetEncodedData() const override { return nullptr; } bool GetPixels(const SkImageInfo& info,
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc index a3112de..f021464 100644 --- a/net/base/network_change_notifier.cc +++ b/net/base/network_change_notifier.cc
@@ -455,13 +455,11 @@ continue; #endif #if defined(OS_MACOSX) - // Ignore tunnel and airdrop interfaces. - if (base::StartsWith(interfaces[i].friendly_name, "utun", - base::CompareCase::SENSITIVE) || - base::StartsWith(interfaces[i].friendly_name, "awdl", - base::CompareCase::SENSITIVE)) { + // Ignore link-local addresses as they aren't globally routable. + // Mac assigns these to disconnected interfaces like tunnel interfaces + // ("utun"), airdrop interfaces ("awdl"), and ethernet ports ("en"). + if (interfaces[i].address.IsLinkLocal()) continue; - } #endif // Remove VMware network interfaces as they're internal and should not be
diff --git a/net/base/network_change_notifier_unittest.cc b/net/base/network_change_notifier_unittest.cc index ef15630..d0bd5bff 100644 --- a/net/base/network_change_notifier_unittest.cc +++ b/net/base/network_change_notifier_unittest.cc
@@ -4,6 +4,7 @@ #include "net/base/network_change_notifier.h" +#include "build/build_config.h" #include "net/base/network_interfaces.h" #include "testing/gtest/include/gtest/gtest.h" @@ -111,6 +112,9 @@ interface_airdrop.type = NetworkChangeNotifier::CONNECTION_ETHERNET; interface_airdrop.name = "awdl0"; interface_airdrop.friendly_name = "awdl0"; + interface_airdrop.address = + // Link-local IPv6 address + IPAddress({0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4}); list.push_back(interface_airdrop); #if defined(OS_MACOSX) @@ -128,6 +132,9 @@ interface_tunnel.type = NetworkChangeNotifier::CONNECTION_ETHERNET; interface_tunnel.name = "utun0"; interface_tunnel.friendly_name = "utun0"; + interface_tunnel.address = + // Link-local IPv6 address + IPAddress({0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 2, 1}); list.push_back(interface_tunnel); #if defined(OS_MACOSX) @@ -139,6 +146,26 @@ #endif } +TEST(NetworkChangeNotifierTest, IgnoreDisconnectedEthernetOnMac) { + NetworkInterfaceList list; + NetworkInterface interface_ethernet; + interface_ethernet.type = NetworkChangeNotifier::CONNECTION_ETHERNET; + interface_ethernet.name = "en5"; + interface_ethernet.friendly_name = "en5"; + interface_ethernet.address = + // Link-local IPv6 address + IPAddress({0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 3}); + list.push_back(interface_ethernet); + +#if defined(OS_MACOSX) + EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, + NetworkChangeNotifier::ConnectionTypeFromInterfaceList(list)); +#else + EXPECT_EQ(NetworkChangeNotifier::CONNECTION_ETHERNET, + NetworkChangeNotifier::ConnectionTypeFromInterfaceList(list)); +#endif +} + TEST(NetworkChangeNotifierTest, IgnoreVMInterfaces) { NetworkInterfaceList list; NetworkInterface interface_vmnet_linux;
diff --git a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc index 395843d..158d782 100644 --- a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc +++ b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
@@ -149,21 +149,17 @@ manager_ = manager.get(); provider_->SetSensorDeviceManagerForTesting(std::move(manager)); - ASSERT_TRUE(sensors_dir_.CreateUniqueTempDir()); - - disallow_blocking_.reset(new base::ScopedDisallowBlocking); + { + base::ScopedAllowBlockingForTesting allow_blocking; + ASSERT_TRUE(sensors_dir_.CreateUniqueTempDir()); + } } void TearDown() override { - // TODO(rakuco): It should be possible to make |disallow_blocking_| a - // regular, non-std::unique_ptr member once we port - // PlatformSensorProviderLinux and accompanying APIs to base::PostTask(). - // At the moment we need to turn |disallow_blocking_| off here because - // stopping PlatformSensorProviderLinux's polling thread is a blocking - // operation. - disallow_blocking_.reset(nullptr); - - ASSERT_TRUE(sensors_dir_.Delete()); + { + base::ScopedAllowBlockingForTesting allow_blocking; + ASSERT_TRUE(sensors_dir_.Delete()); + } base::RunLoop().RunUntilIdle(); } @@ -322,7 +318,7 @@ // Used to simulate the non-test scenario where we're running in an IO thread // that forbids blocking operations. - std::unique_ptr<base::ScopedDisallowBlocking> disallow_blocking_; + base::ScopedDisallowBlocking disallow_blocking_; }; // Tests sensor is not returned if not implemented.
diff --git a/services/network/proxy_config_service_mojo.cc b/services/network/proxy_config_service_mojo.cc index 00c7dcf..11191b80 100644 --- a/services/network/proxy_config_service_mojo.cc +++ b/services/network/proxy_config_service_mojo.cc
@@ -4,6 +4,8 @@ #include "services/network/proxy_config_service_mojo.h" +#include <utility> + namespace network { ProxyConfigServiceMojo::ProxyConfigServiceMojo( @@ -39,6 +41,11 @@ observer.OnProxyConfigChanged(config_, CONFIG_VALID); } +void ProxyConfigServiceMojo::FlushProxyConfig( + FlushProxyConfigCallback callback) { + std::move(callback).Run(); +} + void ProxyConfigServiceMojo::AddObserver(Observer* observer) { observers_.AddObserver(observer); }
diff --git a/services/network/proxy_config_service_mojo.h b/services/network/proxy_config_service_mojo.h index fc90f42e..0a63cc88 100644 --- a/services/network/proxy_config_service_mojo.h +++ b/services/network/proxy_config_service_mojo.h
@@ -51,6 +51,7 @@ // mojom::ProxyConfigClient implementation: void OnProxyConfigUpdated( const net::ProxyConfigWithAnnotation& proxy_config) override; + void FlushProxyConfig(FlushProxyConfigCallback callback) override; mojom::ProxyConfigPollerClientPtr proxy_poller_client_;
diff --git a/services/network/public/cpp/network_param.typemap b/services/network/public/cpp/network_param.typemap index e8a71b2..69e0c62 100644 --- a/services/network/public/cpp/network_param.typemap +++ b/services/network/public/cpp/network_param.typemap
@@ -26,11 +26,11 @@ deps = [ "//ipc", - "//services/network/public/cpp:cpp_base", ] public_deps = [ "//net", + "//services/network/public/cpp:cpp_base", ] type_mappings = [ "network.mojom.AuthChallengeInfo=scoped_refptr<net::AuthChallengeInfo>[nullable_is_same_type]",
diff --git a/services/network/public/mojom/proxy_config_with_annotation.mojom b/services/network/public/mojom/proxy_config_with_annotation.mojom index 9528833..0b1054b8 100644 --- a/services/network/public/mojom/proxy_config_with_annotation.mojom +++ b/services/network/public/mojom/proxy_config_with_annotation.mojom
@@ -16,6 +16,9 @@ // Interface for pushing proxy configuration updates to a NetworkContext. interface ProxyConfigClient { OnProxyConfigUpdated(ProxyConfigWithAnnotation proxy_config); + + // Flush the ProxyConfig + FlushProxyConfig() => (); }; // Called periodically when the current ProxyConfig is in use, as a hint that
diff --git a/services/service_manager/sandbox/mac/gpu_v2.sb b/services/service_manager/sandbox/mac/gpu_v2.sb index e0aa0a8..40c11e4 100644 --- a/services/service_manager/sandbox/mac/gpu_v2.sb +++ b/services/service_manager/sandbox/mac/gpu_v2.sb
@@ -74,4 +74,6 @@ (allow file-read* (subpath "/Library/GPUBundles") + (subpath "/Library/Video/Plug-Ins") + (subpath "/System/Library/Video/Plug-Ins") )
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc index f058e6b..8175112 100644 --- a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc +++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.cc
@@ -18,8 +18,6 @@ viz::CompositorFrameMetadata>:: Read(viz::mojom::CompositorFrameMetadataDataView data, viz::CompositorFrameMetadata* out) { - if (data.device_scale_factor() <= 0) - return false; out->device_scale_factor = data.device_scale_factor(); if (!data.ReadRootScrollOffset(&out->root_scroll_offset)) return false;
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h index b627c6f..4c1f33e 100644 --- a/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h +++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_struct_traits.h
@@ -21,7 +21,6 @@ viz::CompositorFrameMetadata> { static float device_scale_factor( const viz::CompositorFrameMetadata& metadata) { - DCHECK_GT(metadata.device_scale_factor, 0); return metadata.device_scale_factor; }
diff --git a/storage/browser/quota/client_usage_tracker.cc b/storage/browser/quota/client_usage_tracker.cc index b26d5e4d..a682cf7 100644 --- a/storage/browser/quota/client_usage_tracker.cc +++ b/storage/browser/quota/client_usage_tracker.cc
@@ -257,6 +257,7 @@ void ClientUsageTracker::AccumulateLimitedOriginUsage(AccumulateInfo* info, UsageCallback callback, int64_t usage) { + DCHECK_GT(info->pending_jobs, 0U); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); info->limited_usage += usage; if (--info->pending_jobs) @@ -301,6 +302,7 @@ GlobalUsageCallback callback, int64_t limited_usage, int64_t unlimited_usage) { + DCHECK_GT(info->pending_jobs, 0U); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); info->limited_usage += limited_usage; info->unlimited_usage += unlimited_usage; @@ -358,6 +360,7 @@ const std::string& host, const base::Optional<url::Origin>& origin, int64_t usage) { + DCHECK_GT(info->pending_jobs, 0U); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (origin.has_value()) { DCHECK(!origin->GetURL().is_empty());
diff --git a/storage/browser/quota/client_usage_tracker.h b/storage/browser/quota/client_usage_tracker.h index c3f31fbd..3b4877d 100644 --- a/storage/browser/quota/client_usage_tracker.h +++ b/storage/browser/quota/client_usage_tracker.h
@@ -59,7 +59,7 @@ using UsageMap = std::map<url::Origin, int64_t>; struct AccumulateInfo { - int pending_jobs = 0; + size_t pending_jobs = 0; int64_t limited_usage = 0; int64_t unlimited_usage = 0; };
diff --git a/storage/browser/quota/quota_manager.cc b/storage/browser/quota/quota_manager.cc index 168168ae..cb34f7c 100644 --- a/storage/browser/quota/quota_manager.cc +++ b/storage/browser/quota/quota_manager.cc
@@ -544,7 +544,7 @@ type_(type), quota_client_mask_(quota_client_mask), error_count_(0), - remaining_clients_(-1), + remaining_clients_(0), skipped_clients_(0), is_eviction_(is_eviction), callback_(std::move(callback)), @@ -599,7 +599,7 @@ private: void DidDeleteOriginData(int tracing_id, blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_clients_, 0); + DCHECK_GT(remaining_clients_, 0U); TRACE_EVENT_ASYNC_END0("browsing_data", "QuotaManager::OriginDataDeleter", tracing_id); @@ -618,7 +618,7 @@ StorageType type_; int quota_client_mask_; int error_count_; - int remaining_clients_; + size_t remaining_clients_; int skipped_clients_; bool is_eviction_; StatusCallback callback_; @@ -639,8 +639,8 @@ type_(type), quota_client_mask_(quota_client_mask), error_count_(0), - remaining_clients_(-1), - remaining_deleters_(-1), + remaining_clients_(0), + remaining_deleters_(0), callback_(std::move(callback)), weak_factory_(this) {} @@ -679,7 +679,7 @@ private: void DidGetOriginsForHost(const std::set<url::Origin>& origins) { - DCHECK_GT(remaining_clients_, 0); + DCHECK_GT(remaining_clients_, 0U); for (const auto& origin : origins) origins_.insert(origin); @@ -704,7 +704,7 @@ } void DidDeleteOriginData(blink::mojom::QuotaStatusCode status) { - DCHECK_GT(remaining_deleters_, 0); + DCHECK_GT(remaining_deleters_, 0U); if (status != blink::mojom::QuotaStatusCode::kOk) ++error_count_; @@ -722,8 +722,8 @@ int quota_client_mask_; std::set<url::Origin> origins_; int error_count_; - int remaining_clients_; - int remaining_deleters_; + size_t remaining_clients_; + size_t remaining_deleters_; StatusCallback callback_; base::WeakPtrFactory<HostDataDeleter> weak_factory_;
diff --git a/storage/browser/quota/quota_temporary_storage_evictor.cc b/storage/browser/quota/quota_temporary_storage_evictor.cc index bf20f7e..f97d7fb 100644 --- a/storage/browser/quota/quota_temporary_storage_evictor.cc +++ b/storage/browser/quota/quota_temporary_storage_evictor.cc
@@ -141,7 +141,8 @@ this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram); } -void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) { +void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay( + int64_t delay_ms) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (eviction_timer_.IsRunning() || timer_disabled_for_testing_) return;
diff --git a/storage/browser/quota/quota_temporary_storage_evictor.h b/storage/browser/quota/quota_temporary_storage_evictor.h index 954c580cd..eb987dd1 100644 --- a/storage/browser/quota/quota_temporary_storage_evictor.h +++ b/storage/browser/quota/quota_temporary_storage_evictor.h
@@ -80,7 +80,7 @@ private: friend class content::QuotaTemporaryStorageEvictorTest; - void StartEvictionTimerWithDelay(int delay_ms); + void StartEvictionTimerWithDelay(int64_t delay_ms); void ConsiderEviction(); void OnGotEvictionRoundInfo(blink::mojom::QuotaStatusCode status, const QuotaSettings& settings,
diff --git a/storage/browser/quota/storage_monitor.cc b/storage/browser/quota/storage_monitor.cc index 5aaaa61..406845f 100644 --- a/storage/browser/quota/storage_monitor.cc +++ b/storage/browser/quota/storage_monitor.cc
@@ -37,7 +37,7 @@ observer_state_map_.erase(observer); } -int StorageObserverList::ObserverCount() const { +size_t StorageObserverList::ObserverCount() const { return observer_state_map_.size(); }
diff --git a/storage/browser/quota/storage_monitor.h b/storage/browser/quota/storage_monitor.h index 6797d3a..17ed854 100644 --- a/storage/browser/quota/storage_monitor.h +++ b/storage/browser/quota/storage_monitor.h
@@ -40,7 +40,7 @@ void RemoveObserver(StorageObserver* observer); // Returns the number of observers. - int ObserverCount() const; + size_t ObserverCount() const; // Forwards a storage change to observers. The event may be dispatched // immediately to an observer or after a delay, depending on the desired event
diff --git a/storage/browser/quota/storage_monitor_unittest.cc b/storage/browser/quota/storage_monitor_unittest.cc index ce306e5..92c821f 100644 --- a/storage/browser/quota/storage_monitor_unittest.cc +++ b/storage/browser/quota/storage_monitor_unittest.cc
@@ -50,9 +50,7 @@ return events_.back(); } - int EventCount() const { - return events_.size(); - } + size_t EventCount() const { return events_.size(); } // StorageObserver implementation: void OnStorageEvent(const StorageObserver::Event& event) override { @@ -157,7 +155,7 @@ SetLastNotificationTime(host_observers.observers_, observer); } - int GetObserverCount(const HostStorageObservers& host_observers) { + size_t GetObserverCount(const HostStorageObservers& host_observers) { return host_observers.observers_.ObserverCount(); } @@ -202,7 +200,7 @@ event.quota = 1; event.usage = 1; observer_list.OnStorageChange(event); - EXPECT_EQ(1, mock_observer.EventCount()); + EXPECT_EQ(1u, mock_observer.EventCount()); EXPECT_EQ(event, mock_observer.LastEvent()); EXPECT_EQ(nullptr, GetPendingEvent(observer_list)); EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); @@ -211,7 +209,7 @@ event.quota = 2; event.usage = 2; observer_list.OnStorageChange(event); - EXPECT_EQ(1, mock_observer.EventCount()); + EXPECT_EQ(1u, mock_observer.EventCount()); ASSERT_TRUE(GetPendingEvent(observer_list)); EXPECT_EQ(event, *GetPendingEvent(observer_list)); EXPECT_EQ(1, GetRequiredUpdatesCount(observer_list)); @@ -221,7 +219,7 @@ event.quota = 3; event.usage = 3; observer_list.OnStorageChange(event); - EXPECT_EQ(2, mock_observer.EventCount()); + EXPECT_EQ(2u, mock_observer.EventCount()); EXPECT_EQ(event, mock_observer.LastEvent()); EXPECT_EQ(nullptr, GetPendingEvent(observer_list)); EXPECT_EQ(0, GetRequiredUpdatesCount(observer_list)); @@ -231,7 +229,7 @@ event.usage = 4; observer_list.RemoveObserver(&mock_observer); observer_list.OnStorageChange(event); - EXPECT_EQ(2, mock_observer.EventCount()); + EXPECT_EQ(2u, mock_observer.EventCount()); EXPECT_EQ(nullptr, GetPendingEvent(observer_list)); } @@ -257,8 +255,8 @@ event.quota = 1; event.usage = 1; observer_list.OnStorageChange(event); - EXPECT_EQ(1, mock_observer1.EventCount()); - EXPECT_EQ(1, mock_observer2.EventCount()); + EXPECT_EQ(1u, mock_observer1.EventCount()); + EXPECT_EQ(1u, mock_observer2.EventCount()); EXPECT_EQ(event, mock_observer1.LastEvent()); EXPECT_EQ(event, mock_observer2.LastEvent()); EXPECT_EQ(nullptr, GetPendingEvent(observer_list)); @@ -270,8 +268,8 @@ event.quota = 2; event.usage = 2; observer_list.OnStorageChange(event); - EXPECT_EQ(2, mock_observer1.EventCount()); - EXPECT_EQ(1, mock_observer2.EventCount()); + EXPECT_EQ(2u, mock_observer1.EventCount()); + EXPECT_EQ(1u, mock_observer2.EventCount()); EXPECT_EQ(event, mock_observer1.LastEvent()); ASSERT_TRUE(GetPendingEvent(observer_list)); EXPECT_EQ(event, *GetPendingEvent(observer_list)); @@ -280,8 +278,8 @@ // Now dispatch the pending event to observer2. SetLastNotificationTime(observer_list, &mock_observer2); DispatchPendingEvents(observer_list); - EXPECT_EQ(2, mock_observer1.EventCount()); - EXPECT_EQ(2, mock_observer2.EventCount()); + EXPECT_EQ(2u, mock_observer1.EventCount()); + EXPECT_EQ(2u, mock_observer2.EventCount()); EXPECT_EQ(event, mock_observer1.LastEvent()); EXPECT_EQ(event, mock_observer2.LastEvent()); EXPECT_EQ(nullptr, GetPendingEvent(observer_list)); @@ -327,7 +325,7 @@ // Verify that HostStorageObservers dispatches the first event correctly. StorageObserver::Event expected_event(params.filter, kUsage, kQuota); host_observers.NotifyUsageChange(params.filter, 87324); - EXPECT_EQ(1, mock_observer.EventCount()); + EXPECT_EQ(1u, mock_observer.EventCount()); EXPECT_EQ(expected_event, mock_observer.LastEvent()); EXPECT_TRUE(host_observers.is_initialized()); @@ -337,7 +335,7 @@ expected_event.usage += kDelta; SetLastNotificationTime(host_observers, &mock_observer); host_observers.NotifyUsageChange(params.filter, kDelta); - EXPECT_EQ(2, mock_observer.EventCount()); + EXPECT_EQ(2u, mock_observer.EventCount()); EXPECT_EQ(expected_event, mock_observer.LastEvent()); } @@ -357,7 +355,7 @@ MockObserver mock_observer1; host_observers.AddObserver(&mock_observer1, params); EXPECT_FALSE(host_observers.is_initialized()); - EXPECT_EQ(0, mock_observer1.EventCount()); + EXPECT_EQ(0u, mock_observer1.EventCount()); // |host_observers| should be initialized after the second observer is // added. @@ -365,8 +363,8 @@ params.dispatch_initial_state = true; host_observers.AddObserver(&mock_observer2, params); StorageObserver::Event expected_event(params.filter, kUsage, kQuota); - EXPECT_EQ(0, mock_observer1.EventCount()); - EXPECT_EQ(1, mock_observer2.EventCount()); + EXPECT_EQ(0u, mock_observer1.EventCount()); + EXPECT_EQ(1u, mock_observer2.EventCount()); EXPECT_EQ(expected_event, mock_observer2.LastEvent()); EXPECT_TRUE(host_observers.is_initialized()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); @@ -377,8 +375,8 @@ expected_event.usage += kDelta; SetLastNotificationTime(host_observers, &mock_observer2); host_observers.NotifyUsageChange(params.filter, kDelta); - EXPECT_EQ(1, mock_observer1.EventCount()); - EXPECT_EQ(2, mock_observer2.EventCount()); + EXPECT_EQ(1u, mock_observer1.EventCount()); + EXPECT_EQ(2u, mock_observer2.EventCount()); EXPECT_EQ(expected_event, mock_observer1.LastEvent()); EXPECT_EQ(expected_event, mock_observer2.LastEvent()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); @@ -389,9 +387,9 @@ MockObserver mock_observer3; params.dispatch_initial_state = true; host_observers.AddObserver(&mock_observer3, params); - EXPECT_EQ(1, mock_observer1.EventCount()); - EXPECT_EQ(2, mock_observer2.EventCount()); - EXPECT_EQ(1, mock_observer3.EventCount()); + EXPECT_EQ(1u, mock_observer1.EventCount()); + EXPECT_EQ(2u, mock_observer2.EventCount()); + EXPECT_EQ(1u, mock_observer3.EventCount()); EXPECT_EQ(expected_event, mock_observer3.LastEvent()); } @@ -431,7 +429,7 @@ // Verify that |host_observers| is not initialized and an event has not been // dispatched. host_observers.NotifyUsageChange(params.filter, 9438); - EXPECT_EQ(0, mock_observer.EventCount()); + EXPECT_EQ(0u, mock_observer.EventCount()); EXPECT_FALSE(host_observers.is_initialized()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); @@ -440,7 +438,7 @@ quota_manager_->SetCallbackParams(kUsage, kQuota, QuotaStatusCode::kOk); host_observers.NotifyUsageChange(params.filter, 9048543); StorageObserver::Event expected_event(params.filter, kUsage, kQuota); - EXPECT_EQ(1, mock_observer.EventCount()); + EXPECT_EQ(1u, mock_observer.EventCount()); EXPECT_EQ(expected_event, mock_observer.LastEvent()); EXPECT_TRUE(host_observers.is_initialized()); } @@ -458,7 +456,7 @@ // Trigger initialization. Leave the mock quota manager uninitialized so that // the callback is not invoked. host_observers.NotifyUsageChange(params.filter, 7645); - EXPECT_EQ(0, mock_observer.EventCount()); + EXPECT_EQ(0u, mock_observer.EventCount()); EXPECT_FALSE(host_observers.is_initialized()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); @@ -469,7 +467,7 @@ const int64_t kQuota = 99585556; const int64_t kDelta = 327643; host_observers.NotifyUsageChange(params.filter, kDelta); - EXPECT_EQ(0, mock_observer.EventCount()); + EXPECT_EQ(0u, mock_observer.EventCount()); EXPECT_FALSE(host_observers.is_initialized()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); EXPECT_EQ(0, GetRequiredUpdatesCount(host_observers)); @@ -478,7 +476,7 @@ quota_manager_->SetCallbackParams(kUsage, kQuota, QuotaStatusCode::kOk); quota_manager_->InvokeCallback(); StorageObserver::Event expected_event(params.filter, kUsage + kDelta, kQuota); - EXPECT_EQ(1, mock_observer.EventCount()); + EXPECT_EQ(1u, mock_observer.EventCount()); EXPECT_EQ(expected_event, mock_observer.LastEvent()); EXPECT_TRUE(host_observers.is_initialized()); EXPECT_EQ(nullptr, GetPendingEvent(host_observers)); @@ -515,20 +513,20 @@ // Verify that the observers have been removed correctly. ASSERT_TRUE(type_observers.GetHostObservers(host1)); ASSERT_TRUE(type_observers.GetHostObservers(host2)); - EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host1))); - EXPECT_EQ(3, GetObserverCount(*type_observers.GetHostObservers(host2))); + EXPECT_EQ(2u, GetObserverCount(*type_observers.GetHostObservers(host1))); + EXPECT_EQ(3u, GetObserverCount(*type_observers.GetHostObservers(host2))); // Remove all instances of observer1. type_observers.RemoveObserver(&mock_observer1); ASSERT_TRUE(type_observers.GetHostObservers(host1)); ASSERT_TRUE(type_observers.GetHostObservers(host2)); - EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host1))); - EXPECT_EQ(2, GetObserverCount(*type_observers.GetHostObservers(host2))); + EXPECT_EQ(1u, GetObserverCount(*type_observers.GetHostObservers(host1))); + EXPECT_EQ(2u, GetObserverCount(*type_observers.GetHostObservers(host2))); // Remove all instances of observer2. type_observers.RemoveObserver(&mock_observer2); ASSERT_TRUE(type_observers.GetHostObservers(host2)); - EXPECT_EQ(1, GetObserverCount(*type_observers.GetHostObservers(host2))); + EXPECT_EQ(1u, GetObserverCount(*type_observers.GetHostObservers(host2))); // Observers of host1 has been deleted as it is empty. EXPECT_FALSE(type_observers.GetHostObservers(host1)); } @@ -563,14 +561,15 @@ storage_monitor_->AddObserver(&mock_observer3_, params2_); } - int GetObserverCount(StorageType storage_type) { + size_t GetObserverCount(StorageType storage_type) { const StorageTypeObservers* type_observers = storage_monitor_->GetStorageTypeObservers(storage_type); return StorageMonitorTestBase::GetObserverCount( *type_observers->GetHostObservers(host_)); } - void CheckObserverCount(int expected_temporary, int expected_persistent) { + void CheckObserverCount(size_t expected_temporary, + size_t expected_persistent) { ASSERT_TRUE( storage_monitor_->GetStorageTypeObservers(StorageType::kTemporary)); ASSERT_TRUE( @@ -610,9 +609,9 @@ storage_monitor_->NotifyUsageChange(params1_.filter, 9048543); StorageObserver::Event expected_event(params1_.filter, kUsage, kQuota); - EXPECT_EQ(1, mock_observer1_.EventCount()); - EXPECT_EQ(1, mock_observer2_.EventCount()); - EXPECT_EQ(0, mock_observer3_.EventCount()); + EXPECT_EQ(1u, mock_observer1_.EventCount()); + EXPECT_EQ(1u, mock_observer2_.EventCount()); + EXPECT_EQ(0u, mock_observer3_.EventCount()); EXPECT_EQ(expected_event, mock_observer1_.LastEvent()); EXPECT_EQ(expected_event, mock_observer2_.LastEvent()); } @@ -671,7 +670,7 @@ scoped_task_environment_.RunUntilIdle(); // Verify that the observer receives it. - ASSERT_EQ(1, mock_observer.EventCount()); + ASSERT_EQ(1u, mock_observer.EventCount()); const StorageObserver::Event& event = mock_observer.LastEvent(); EXPECT_EQ(params.filter, event.filter); EXPECT_EQ(kTestUsage, event.usage);
diff --git a/storage/browser/quota/usage_tracker.cc b/storage/browser/quota/usage_tracker.cc index 64fb591..8f3516d 100644 --- a/storage/browser/quota/usage_tracker.cc +++ b/storage/browser/quota/usage_tracker.cc
@@ -208,6 +208,7 @@ void UsageTracker::AccumulateClientGlobalLimitedUsage(AccumulateInfo* info, int64_t limited_usage) { + DCHECK_GT(info->pending_clients, 0U); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); info->usage += limited_usage; if (--info->pending_clients) @@ -224,6 +225,7 @@ void UsageTracker::AccumulateClientGlobalUsage(AccumulateInfo* info, int64_t usage, int64_t unlimited_usage) { + DCHECK_GT(info->pending_clients, 0U); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); info->usage += usage; info->unlimited_usage += unlimited_usage;
diff --git a/storage/browser/quota/usage_tracker.h b/storage/browser/quota/usage_tracker.h index 1d19e8d..74bcb1f 100644 --- a/storage/browser/quota/usage_tracker.h +++ b/storage/browser/quota/usage_tracker.h
@@ -73,7 +73,7 @@ struct AccumulateInfo { AccumulateInfo(); ~AccumulateInfo(); - int pending_clients = 0; + size_t pending_clients = 0; int64_t usage = 0; int64_t unlimited_usage = 0; blink::mojom::UsageBreakdownPtr usage_breakdown =
diff --git a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter index 2a315e0..65f56a78 100644 --- a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter +++ b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
@@ -16,20 +16,12 @@ -org.chromium.android_webview.test.CookieManagerTest.testCookieForWebSocketHandshake_thirdParty_disabled # https://crbug.com/941337 --org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookie_redirectFromThirdToFirst +-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookie_redirectFromThirdPartyToFirst -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookie_redirectFromFirstPartyToThird # https://crbug.com/893580 -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testLoadDataWithBaseUrlAccessingFile -# https://crbug.com/902658 --org.chromium.android_webview.test.AwProxyControllerTest.testProxyOverride --org.chromium.android_webview.test.AwProxyControllerTest.testProxyOverrideLocalhost --org.chromium.android_webview.test.AwProxyControllerTest.testCallbacks --org.chromium.android_webview.test.AwProxyControllerTest.testValidInput --org.chromium.android_webview.test.AwProxyControllerTest.testInvalidProxyUrls --org.chromium.android_webview.test.AwProxyControllerTest.testInvalidBypassRules - # Flaky tests on android_mojo and android_mojo_rel bots # https://crbug.com/936757, https://crbug.com/939355 -org.chromium.android_webview.test.AwContentsClientFullScreenTest.testOnShowCustomViewAndPlayWithHtmlControl_videoInsideDiv
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 4a0d8a0..ec85cb8 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -389,10 +389,6 @@ 'chromedriver_replay_unittests': {}, }, - # Identical to chromeos_device_friendly_gtests below minus - # - cros_vm_sanity_test - # - chrome_all_tast_tests - # Tests that run in Chrome OS VMs. # NOTE: We only want a small subset of test suites here, because most # suites assume that they stub out the underlying device hardware.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 51d91fb1..ec6a0cc0 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -596,28 +596,7 @@ ], "experiments": [ { - "name": "Enabled_LeadingIcon", - "params": { - "variant": "leading-icon" - }, - "enable_features": [ - "AutofillDropdownLayout" - ] - }, - { - "name": "Enabled_TrailingIcon", - "params": { - "variant": "trailing-icon" - }, - "enable_features": [ - "AutofillDropdownLayout" - ] - }, - { - "name": "Enabled_TwoLinesLeadingIcon", - "params": { - "variant": "two-lines-leading-icon" - }, + "name": "Enabled", "enable_features": [ "AutofillDropdownLayout" ] @@ -1434,6 +1413,21 @@ ] } ], + "ContextualSearchDefinitions": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "ContextualSearchDefinitions", + "enable_features": [ + "ContextualSearchDefinitions" + ] + } + ] + } + ], "CopylessPaste": [ { "platforms": [
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc index a22cc83..916cd14 100644 --- a/third_party/blink/common/feature_policy/feature_policy.cc +++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -438,6 +438,9 @@ {mojom::FeaturePolicyFeature::kTopNavigation, FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, mojom::PolicyValueType::kBool)}, + {mojom::FeaturePolicyFeature::kUnoptimizedLosslessImages, + FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, + mojom::PolicyValueType::kDecDouble)}, {mojom::FeaturePolicyFeature::kUnoptimizedLossyImages, FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, mojom::PolicyValueType::kDecDouble)},
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom index b5aa3fb..41a1a86 100644 --- a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom +++ b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
@@ -123,6 +123,7 @@ // When disallowed, these policies require images to have a reasonable byte-to-pixel ratio. kUnoptimizedLossyImages = 45, + kUnoptimizedLosslessImages = 46, // Don't change assigned numbers of any item, and don't reuse removed slots. // Add new features at the end of the enum.
diff --git a/third_party/blink/public/mojom/web_client_hints/OWNERS b/third_party/blink/public/mojom/web_client_hints/OWNERS index 08850f4..c02b6b0 100644 --- a/third_party/blink/public/mojom/web_client_hints/OWNERS +++ b/third_party/blink/public/mojom/web_client_hints/OWNERS
@@ -1,2 +1,6 @@ +yoavweiss@chromium.org +tbansal@chromium.org +ryansturm@chromium.org + per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h index 690e439f..68f6a59 100644 --- a/third_party/blink/public/web/web_ax_object.h +++ b/third_party/blink/public/web/web_ax_object.h
@@ -211,6 +211,19 @@ // The following selection functions get or set the global document // selection and can be called on any object in the tree. + // + // Since we are gradually moving to a new selection codebase, we have two sets + // of functions. The deprecated ones will still be used by Chromium, except in + // Web Tests, which will be using the new ones. + // TODO(nektar): Remove deprecated functions. crbug.com/639340 + + BLINK_EXPORT void SelectionDeprecated( + WebAXObject& anchor_object, + int& anchor_offset, + ax::mojom::TextAffinity& anchor_affinity, + WebAXObject& focus_object, + int& focus_offset, + ax::mojom::TextAffinity& focus_affinity) const; BLINK_EXPORT void Selection(WebAXObject& anchor_object, int& anchor_offset, ax::mojom::TextAffinity& anchor_affinity, @@ -219,10 +232,18 @@ ax::mojom::TextAffinity& focus_affinity) const; // The following selection functions return text offsets calculated starting - // the current object. They only report on a selection that is placed on + // from the current object. They only report on a selection that is placed on // the current object or on any of its descendants. + // + // Since we are gradually moving to a new selection codebase, we have two sets + // of functions. The deprecated ones will still be used by Chromium, except in + // Web Tests, which will be using the new ones. + // TODO(nektar): Remove deprecated functions. crbug.com/639340 + + BLINK_EXPORT unsigned SelectionEndDeprecated() const; BLINK_EXPORT unsigned SelectionEnd() const; BLINK_EXPORT unsigned SelectionEndLineNumber() const; + BLINK_EXPORT unsigned SelectionStartDeprecated() const; BLINK_EXPORT unsigned SelectionStart() const; BLINK_EXPORT unsigned SelectionStartLineNumber() const; @@ -267,6 +288,10 @@ BLINK_EXPORT bool Focus() const; BLINK_EXPORT bool SetAccessibilityFocus() const; BLINK_EXPORT bool SetSelected(bool) const; + BLINK_EXPORT bool SetSelectionDeprecated(const WebAXObject& anchor_object, + int anchor_offset, + const WebAXObject& focus_object, + int focus_offset) const; BLINK_EXPORT bool SetSelection(const WebAXObject& anchor_object, int anchor_offset, const WebAXObject& focus_object,
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni index b36a029..fc4c7df 100644 --- a/third_party/blink/renderer/bindings/bindings.gni +++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -199,7 +199,6 @@ "core/v8/script_promise_test.cc", "core/v8/script_streamer_test.cc", "core/v8/script_wrappable_v8_gc_integration_test.cc", - "core/v8/script_wrappable_visitor_test.cc", "core/v8/to_v8_test.cc", "core/v8/trace_wrapper_member_test.cc", "core/v8/v8_binding_for_testing.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/script_wrappable_visitor_test.cc b/third_party/blink/renderer/bindings/core/v8/script_wrappable_visitor_test.cc deleted file mode 100644 index d736917a..0000000 --- a/third_party/blink/renderer/bindings/core/v8/script_wrappable_visitor_test.cc +++ /dev/null
@@ -1,118 +0,0 @@ -// 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. - -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/core/testing/death_aware_script_wrappable.h" -#include "third_party/blink/renderer/platform/heap/trace_traits.h" - -namespace blink { - -namespace { - -class VerifyingScriptWrappableVisitor : public ScriptWrappableVisitor { - public: - VerifyingScriptWrappableVisitor() - : ScriptWrappableVisitor(ThreadState::Current()) {} - - // Visitor interface. - void Visit(const TraceWrapperV8Reference<v8::Value>&) override {} - - void VisitWithWrappers(void*, TraceDescriptor desc) override { - visited_objects_.push_back(desc.base_object_payload); - } - - void VisitBackingStoreStrongly(void* object, - void** object_slot, - TraceDescriptor desc) override { - if (!object) - return; - desc.callback(this, desc.base_object_payload); - } - - bool DidVisitObject(const ScriptWrappable* script_wrappable) const { - return std::find(visited_objects_.begin(), visited_objects_.end(), - script_wrappable) != visited_objects_.end(); - } - - protected: - using Visitor::Visit; - - private: - std::vector<void*> visited_objects_; -}; - -class ExpectObjectsVisited { - public: - ExpectObjectsVisited(VerifyingScriptWrappableVisitor* visitor, - std::initializer_list<ScriptWrappable*> objects) - : visitor_(visitor), expected_objects_(objects) {} - - ~ExpectObjectsVisited() { - for (const ScriptWrappable* expected_object : expected_objects_) { - EXPECT_TRUE(visitor_->DidVisitObject(expected_object)); - } - } - - private: - VerifyingScriptWrappableVisitor* visitor_; - std::vector<ScriptWrappable*> expected_objects_; -}; - -} // namespace - -TEST(ScriptWrappableVisitorTest, TraceWrapperMember) { - VerifyingScriptWrappableVisitor verifying_visitor; - DeathAwareScriptWrappable* parent = DeathAwareScriptWrappable::Create(); - DeathAwareScriptWrappable* child = DeathAwareScriptWrappable::Create(); - parent->SetWrappedDependency(child); - { - ExpectObjectsVisited expected(&verifying_visitor, {child}); - TraceDescriptor desc = - TraceTrait<DeathAwareScriptWrappable>::GetTraceDescriptor(parent); - desc.callback(&verifying_visitor, parent); - } -} - -TEST(ScriptWrappableVisitorTest, HeapVectorOfTraceWrapperMember) { - VerifyingScriptWrappableVisitor verifying_visitor; - DeathAwareScriptWrappable* parent = DeathAwareScriptWrappable::Create(); - DeathAwareScriptWrappable* child = DeathAwareScriptWrappable::Create(); - parent->AddWrappedVectorDependency(child); - { - ExpectObjectsVisited expected(&verifying_visitor, {child}); - TraceDescriptor desc = - TraceTrait<DeathAwareScriptWrappable>::GetTraceDescriptor(parent); - desc.callback(&verifying_visitor, parent); - } -} - -TEST(ScriptWrappableVisitorTest, HeapHashMapOfTraceWrapperMember) { - VerifyingScriptWrappableVisitor verifying_visitor; - DeathAwareScriptWrappable* parent = DeathAwareScriptWrappable::Create(); - DeathAwareScriptWrappable* key = DeathAwareScriptWrappable::Create(); - DeathAwareScriptWrappable* value = DeathAwareScriptWrappable::Create(); - parent->AddWrappedHashMapDependency(key, value); - { - ExpectObjectsVisited expected(&verifying_visitor, {key, value}); - TraceDescriptor desc = - TraceTrait<DeathAwareScriptWrappable>::GetTraceDescriptor(parent); - desc.callback(&verifying_visitor, parent); - } -} - -TEST(ScriptWrappableVisitorTest, InObjectUsingTraceWrapperMember) { - VerifyingScriptWrappableVisitor verifying_visitor; - DeathAwareScriptWrappable* parent = DeathAwareScriptWrappable::Create(); - DeathAwareScriptWrappable* child = DeathAwareScriptWrappable::Create(); - parent->AddInObjectDependency(child); - { - ExpectObjectsVisited expected(&verifying_visitor, {child}); - TraceDescriptor desc = - TraceTrait<DeathAwareScriptWrappable>::GetTraceDescriptor(parent); - desc.callback(&verifying_visitor, parent); - } -} - -} // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc index d55dd9c..17303185 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
@@ -9,7 +9,6 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_node.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_gc_controller.cc b/third_party/blink/renderer/bindings/core/v8/v8_gc_controller.cc index c3d04f0..7fe921b 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_gc_controller.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_gc_controller.cc
@@ -46,7 +46,6 @@ #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h" #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" #include "third_party/blink/renderer/platform/heap/heap_stats_collector.h" #include "third_party/blink/renderer/platform/histogram.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc index 893ed77..e3e13a2 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -695,8 +695,8 @@ // over to Blink. DCHECK(ThreadState::MainThreadState()); - ThreadState::MainThreadState()->RegisterTraceDOMWrappers( - isolate, V8GCController::TraceDOMWrappers, nullptr, nullptr); + ThreadState::MainThreadState()->RegisterTraceDOMWrappers( + isolate, V8GCController::TraceDOMWrappers); InitializeV8Common(isolate);
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn index 49a8d975..02bfeb46 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn
@@ -2079,6 +2079,7 @@ "layout/ng/ng_constraint_space_builder_test.cc", "layout/ng/ng_fieldset_layout_algorithm_test.cc", "layout/ng/ng_inline_layout_test.cc", + "layout/ng/ng_layout_result_caching_test.cc", "layout/ng/ng_layout_test.h", "layout/ng/ng_length_utils_test.cc", "layout/ng/ng_out_of_flow_layout_part_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_paint_image_generator.h b/third_party/blink/renderer/core/css/css_paint_image_generator.h index 262424e..dd99919 100644 --- a/third_party/blink/renderer/core/css/css_paint_image_generator.h +++ b/third_party/blink/renderer/core/css/css_paint_image_generator.h
@@ -58,6 +58,7 @@ virtual bool HasAlpha() const = 0; virtual const Vector<CSSSyntaxDescriptor>& InputArgumentTypes() const = 0; virtual bool IsImageGeneratorReady() const = 0; + virtual int WorkletId() const = 0; virtual void Trace(blink::Visitor* visitor) {} };
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc index c59ebff..9972737 100644 --- a/third_party/blink/renderer/core/css/css_paint_value.cc +++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -55,6 +55,11 @@ if (style.InsideLink() != EInsideLink::kNotInsideLink) return nullptr; + if (!generator_) { + generator_ = CSSPaintImageGenerator::Create( + GetName(), document, paint_image_generator_observer_); + } + // For Off-Thread PaintWorklet, we just collect the necessary inputs together // and defer the actual JavaScript call until much later (during cc Raster). if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) { @@ -72,15 +77,11 @@ custom_properties); scoped_refptr<PaintWorkletInput> input = base::MakeRefCounted<PaintWorkletInput>(GetName(), target_size, zoom, + generator_->WorkletId(), std::move(style_data)); return PaintWorkletDeferredImage::Create(std::move(input), target_size); } - if (!generator_) { - generator_ = CSSPaintImageGenerator::Create( - GetName(), document, paint_image_generator_observer_); - } - if (!ParseInputArguments(document)) return nullptr;
diff --git a/third_party/blink/renderer/core/css/css_rule.cc b/third_party/blink/renderer/core/css/css_rule.cc index f0faba5..31c53afc 100644 --- a/third_party/blink/renderer/core/css/css_rule.cc +++ b/third_party/blink/renderer/core/css/css_rule.cc
@@ -24,7 +24,6 @@ #include "third_party/blink/renderer/core/css/css_style_sheet.h" #include "third_party/blink/renderer/core/css/style_rule.h" #include "third_party/blink/renderer/core/css/style_sheet_contents.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" namespace blink { @@ -48,14 +47,12 @@ void CSSRule::SetParentStyleSheet(CSSStyleSheet* style_sheet) { parent_is_rule_ = false; parent_style_sheet_ = style_sheet; - ScriptWrappableMarkingVisitor::WriteBarrier(parent_style_sheet_); MarkingVisitor::WriteBarrier(parent_style_sheet_); } void CSSRule::SetParentRule(CSSRule* rule) { parent_is_rule_ = true; parent_rule_ = rule; - ScriptWrappableMarkingVisitor::WriteBarrier(parent_rule_); MarkingVisitor::WriteBarrier(parent_rule_); }
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.cc index c2a4bf90..15cf35c 100644 --- a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.cc +++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.cc
@@ -12,10 +12,12 @@ const String& name, const FloatSize& container_size, float effective_zoom, + int worklet_id, PaintWorkletStylePropertyMap::CrossThreadData data) : name_(name.IsolatedCopy()), container_size_(container_size), effective_zoom_(effective_zoom), + worklet_id_(worklet_id), style_map_data_(std::move(data)) {} } // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h index df95eae..a3d3f04 100644 --- a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h +++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h
@@ -39,6 +39,7 @@ PaintWorkletInput(const String& name, const FloatSize& container_size, float effective_zoom, + int worklet_id, PaintWorkletStylePropertyMap::CrossThreadData values); ~PaintWorkletInput() override = default; @@ -51,6 +52,7 @@ // These accessors are safe on any thread. const FloatSize& ContainerSize() const { return container_size_; } float EffectiveZoom() const { return effective_zoom_; } + int WorkletId() const { return worklet_id_; } // These should only be accessed on the PaintWorklet thread. String NameCopy() const { return name_.IsolatedCopy(); } @@ -62,6 +64,7 @@ const String name_; const FloatSize container_size_; const float effective_zoom_; + const int worklet_id_; PaintWorkletStylePropertyMap::CrossThreadData style_map_data_; };
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc index 0db31920..55d743b2 100644 --- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc +++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
@@ -188,7 +188,7 @@ custom_properties); scoped_refptr<PaintWorkletInput> input = base::MakeRefCounted<PaintWorkletInput>("test", FloatSize(100, 100), 1.0f, - std::move(data)); + 1, std::move(data)); DCHECK(input); thread_ = std::make_unique<WebThreadSupportingGC>(
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 38afee9..85f1033 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4850,20 +4850,8 @@ css_target_->PseudoStateChanged(CSSSelector::kPseudoTarget); } -static void LiveNodeListBaseWriteBarrier(void* parent, - const LiveNodeListBase* list) { - if (IsHTMLCollectionType(list->GetType())) { - ScriptWrappableMarkingVisitor::WriteBarrier( - static_cast<const HTMLCollection*>(list)); - } else { - ScriptWrappableMarkingVisitor::WriteBarrier( - static_cast<const LiveNodeList*>(list)); - } -} - void Document::RegisterNodeList(const LiveNodeListBase* list) { node_lists_.Add(list, list->InvalidationType()); - LiveNodeListBaseWriteBarrier(this, list); if (list->IsRootedAtTreeScope()) lists_invalidated_at_document_.insert(list); } @@ -4878,7 +4866,6 @@ void Document::RegisterNodeListWithIdNameCache(const LiveNodeListBase* list) { node_lists_.Add(list, kInvalidateOnIdNameAttrChange); - LiveNodeListBaseWriteBarrier(this, list); } void Document::UnregisterNodeListWithIdNameCache(const LiveNodeListBase* list) {
diff --git a/third_party/blink/renderer/core/dom/element_rare_data.h b/third_party/blink/renderer/core/dom/element_rare_data.h index af76a107..88dcd7c 100644 --- a/third_party/blink/renderer/core/dom/element_rare_data.h +++ b/third_party/blink/renderer/core/dom/element_rare_data.h
@@ -41,7 +41,6 @@ #include "third_party/blink/renderer/core/html/custom/custom_element_definition.h" #include "third_party/blink/renderer/core/html/custom/v0_custom_element_definition.h" #include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h"
diff --git a/third_party/blink/renderer/core/dom/events/event_listener_map.h b/third_party/blink/renderer/core/dom/events/event_listener_map.h index 0c16cfe..681e698 100644 --- a/third_party/blink/renderer/core/dom/events/event_listener_map.h +++ b/third_party/blink/renderer/core/dom/events/event_listener_map.h
@@ -38,7 +38,6 @@ #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h" #include "third_party/blink/renderer/core/dom/events/event_listener_options.h" #include "third_party/blink/renderer/core/dom/events/registered_event_listener.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" namespace blink {
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc index ccc863c..05d8449 100644 --- a/third_party/blink/renderer/core/dom/node.cc +++ b/third_party/blink/renderer/core/dom/node.cc
@@ -114,7 +114,6 @@ #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/microtask.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h" #include "third_party/blink/renderer/platform/instance_counters.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" @@ -363,7 +362,6 @@ DCHECK(data_.rare_data_); SetFlag(kHasRareDataFlag); - ScriptWrappableMarkingVisitor::WriteBarrier(RareData()); MarkingVisitor::WriteBarrier(RareData()); return *RareData(); }
diff --git a/third_party/blink/renderer/core/dom/node_rare_data.cc b/third_party/blink/renderer/core/dom/node_rare_data.cc index acbdc03..2e0536e 100644 --- a/third_party/blink/renderer/core/dom/node_rare_data.cc +++ b/third_party/blink/renderer/core/dom/node_rare_data.cc
@@ -37,7 +37,6 @@ #include "third_party/blink/renderer/core/dom/mutation_observer_registration.h" #include "third_party/blink/renderer/core/dom/node_lists_node_data.h" #include "third_party/blink/renderer/core/page/page.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/heap/handle.h" namespace blink {
diff --git a/third_party/blink/renderer/core/dom/shadow_root.h b/third_party/blink/renderer/core/dom/shadow_root.h index 74d5a94e..c34d91b 100644 --- a/third_party/blink/renderer/core/dom/shadow_root.h +++ b/third_party/blink/renderer/core/dom/shadow_root.h
@@ -34,7 +34,6 @@ #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/tree_scope.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" #include "third_party/blink/renderer/platform/wtf/casting.h"
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/third_party/blink/renderer/core/feature_policy/feature_policy.cc index 8e14c48b..225f7dc 100644 --- a/third_party/blink/renderer/core/feature_policy/feature_policy.cc +++ b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -32,8 +32,13 @@ return PolicyValue(2.0); } if (feature == mojom::FeaturePolicyFeature::kUnoptimizedLossyImages) { + // Lossy images default to at most 0.5 bytes per pixel. return PolicyValue(0.5); } + if (feature == mojom::FeaturePolicyFeature::kUnoptimizedLosslessImages) { + // Lossless images default to at most 1 byte per pixel. + return PolicyValue(1.0); + } return PolicyValue(false); }
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 index 3b26fcb..12ea924 100644 --- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 +++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -188,6 +188,11 @@ depends_on: ["FeaturePolicyForSandbox"], }, { + name: "UnoptimizedLosslessImages", + feature_policy_name: "unoptimized-lossless-images", + depends_on: ["ExperimentalProductivityFeatures"], + }, + { name: "UnoptimizedLossyImages", feature_policy_name: "unoptimized-lossy-images", depends_on: ["UnoptimizedImagePolicies"],
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index b8d9d274..e5cd857 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -94,6 +94,7 @@ #include "third_party/blink/renderer/core/loader/document_loader.h" #include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/create_window.h" +#include "third_party/blink/renderer/core/page/focus_controller.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h" @@ -1458,45 +1459,84 @@ if (!features.IsEmpty()) UseCounter::Count(*active_document, WebFeature::kDOMWindowOpenFeatures); + KURL completed_url = + url_string.IsEmpty() + ? KURL(g_empty_string) + : entered_window_frame->GetDocument()->CompleteURL(url_string); + if (!completed_url.IsEmpty() && !completed_url.IsValid()) { + UseCounter::Count(active_document, WebFeature::kWindowOpenWithInvalidURL); + exception_state.ThrowDOMException( + DOMExceptionCode::kSyntaxError, + "Unable to open a window with invalid URL '" + + completed_url.GetString() + "'.\n"); + return nullptr; + } + + WebWindowFeatures window_features = GetWindowFeaturesFromString(features); + // Get the target frame for the special cases of _top and _parent. // In those cases, we schedule a location change right now and return early. Frame* target_frame = nullptr; if (EqualIgnoringASCIICase(target, "_top")) { target_frame = &GetFrame()->Tree().Top(); + } else if (EqualIgnoringASCIICase(target, "_self")) { + target_frame = GetFrame(); } else if (EqualIgnoringASCIICase(target, "_parent")) { if (Frame* parent = GetFrame()->Tree().Parent()) target_frame = parent; else target_frame = GetFrame(); + } else if (!target.IsEmpty() && !window_features.noopener) { + target_frame = GetFrame()->FindFrameForNavigation( + target, *active_document->GetFrame(), completed_url); + if (target_frame) { + Page* target_page = target_frame->GetPage(); + if (target_page == GetFrame()->GetPage()) + target_page->GetFocusController().SetFocusedFrame(target_frame); + else + target_page->GetChromeClient().Focus(GetFrame()); + // Focusing can fire onblur, so check for detach. + if (!target_frame->GetPage()) + return nullptr; + } } - if (target_frame) { - if (!active_document->GetFrame() || - !active_document->GetFrame()->CanNavigate(*target_frame)) { - return nullptr; - } + if (!target_frame) { + return CreateWindow(completed_url, target, window_features, + *incumbent_window, *GetFrame()); + } - KURL completed_url = - entered_window_frame->GetDocument()->CompleteURL(url_string); + if (!active_document->GetFrame() || + !active_document->GetFrame()->CanNavigate(*target_frame)) { + return nullptr; + } - if (target_frame->DomWindow()->IsInsecureScriptAccess(*incumbent_window, - completed_url)) - return target_frame->DomWindow(); - - if (url_string.IsEmpty()) - return target_frame->DomWindow(); - + if (!url_string.IsEmpty() && + !target_frame->DomWindow()->IsInsecureScriptAccess(*incumbent_window, + completed_url)) { FrameLoadRequest request(active_document, ResourceRequest(completed_url)); request.GetResourceRequest().SetHasUserGesture( LocalFrame::HasTransientUserActivation(GetFrame())); if (const WebInputEvent* input_event = CurrentInputEvent::Get()) request.SetInputStartTime(input_event->TimeStamp()); target_frame->Navigate(request, WebFrameLoadType::kStandard); + } + + // TODO(japhet): window-open-noopener.html?_top and several tests in + // html/browsers/windows/browsing-context-names/ appear to require that + // the special case target names (_top, _parent, _self) ignore opener + // policy (by always returning a non-null window, and by never overriding + // the opener). The spec doesn't mention this. + if (EqualIgnoringASCIICase(target, "_top") || + EqualIgnoringASCIICase(target, "_parent") || + EqualIgnoringASCIICase(target, "_self")) { return target_frame->DomWindow(); } - return CreateWindow(url_string, target, features, *incumbent_window, - *entered_window_frame, *GetFrame(), exception_state); + if (window_features.noopener) + return nullptr; + target_frame->Client()->SetOpener(GetFrame()); + return target_frame->DomWindow(); } void LocalDOMWindow::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h index e976ae2..f328d06 100644 --- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h +++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -43,7 +43,6 @@ #include "third_party/blink/renderer/core/imagebitmap/image_bitmap_source.h" #include "third_party/blink/renderer/core/page/page_visibility_observer.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/geometry/int_size.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
diff --git a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc index d8967d6..0b38c1b0 100644 --- a/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc +++ b/third_party/blink/renderer/core/html/forms/checkbox_input_type.cc
@@ -34,6 +34,7 @@ #include "third_party/blink/renderer/core/events/keyboard_event.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/input_type_names.h" +#include "third_party/blink/renderer/core/page/spatial_navigation.h" #include "third_party/blink/renderer/platform/text/platform_locale.h" namespace blink { @@ -56,9 +57,13 @@ } void CheckboxInputType::HandleKeyupEvent(KeyboardEvent& event) { - if (event.key() != " ") - return; - DispatchSimulatedClickIfActive(event); + // Use Space key simulated click by default. + // Use Enter key simulated click when Spatial Navigation enabled. + if (event.key() == " " || + (IsSpatialNavigationEnabled(GetElement().GetDocument().GetFrame()) && + event.key() == "Enter")) { + DispatchSimulatedClickIfActive(event); + } } ClickHandlingState* CheckboxInputType::WillDispatchClick() {
diff --git a/third_party/blink/renderer/core/html/forms/radio_input_type.cc b/third_party/blink/renderer/core/html/forms/radio_input_type.cc index 9ff420db..bc9ef07 100644 --- a/third_party/blink/renderer/core/html/forms/radio_input_type.cc +++ b/third_party/blink/renderer/core/html/forms/radio_input_type.cc
@@ -137,13 +137,20 @@ } void RadioInputType::HandleKeyupEvent(KeyboardEvent& event) { - if (event.key() != " ") - return; // If an unselected radio is tabbed into (because the entire group has nothing // checked, or because of some explicit .focus() call), then allow space to // check it. if (GetElement().checked()) return; + + // Use Space key simulated click by default. + // Use Enter key simulated click when Spatial Navigation enabled. + if (event.key() == " " || + (IsSpatialNavigationEnabled(GetElement().GetDocument().GetFrame()) && + event.key() == "Enter")) { + DispatchSimulatedClickIfActive(event); + } + DispatchSimulatedClickIfActive(event); }
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl index 21180bc..8d86d0f 100644 --- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl +++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -3030,7 +3030,7 @@ array of DataEntry objectStoreDataEntries # If true, there are more entries to fetch in the given range. boolean hasMore - + # Gets metadata of an object store command getMetadata parameters @@ -5786,7 +5786,7 @@ optional array of string recommendations # Information about insecure content on the page. - type InsecureContentStatus extends object + deprecated type InsecureContentStatus extends object properties # True if the page was loaded over HTTPS and ran mixed (HTTP) content such as scripts. boolean ranMixedContent @@ -5863,7 +5863,7 @@ # `warning`, at least one corresponding explanation should be included. array of SecurityStateExplanation explanations # Information about insecure content on the page. - InsecureContentStatus insecureContentStatus + deprecated InsecureContentStatus insecureContentStatus # Overrides user-visible description of the state. optional string summary
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index bd49760c..99e86f50 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -2313,7 +2313,7 @@ if (!RuntimeEnabledFeatures::LayoutNGFragmentCachingEnabled()) return nullptr; - // TODO*cbiesinger): Support caching fragmented boxes + // TODO(cbiesinger): Support caching fragmented boxes. if (break_token) return nullptr; @@ -2339,8 +2339,6 @@ const NGConstraintSpace& old_space = cached_layout_result->GetConstraintSpaceForCaching(); - // Check the BFC offset. Even if they don't match, there're some cases we can - // still reuse the fragment. base::Optional<LayoutUnit> bfc_block_offset = cached_layout_result->BfcBlockOffset(); LayoutUnit bfc_line_offset = new_space.BfcOffset().line_offset; @@ -2348,22 +2346,58 @@ DCHECK_EQ(old_space.BfcOffset().line_offset, cached_layout_result->BfcLineOffset()); + // Check the BFC offset. Even if they don't match, there're some cases we can + // still reuse the fragment. bool is_bfc_offset_equal = new_space.BfcOffset() == old_space.BfcOffset(); - if (!is_bfc_offset_equal) { - // Earlier floats may affect this box if block offset changes. - if (new_space.HasFloats() || old_space.HasFloats()) + + // Even for the first fragment, when block fragmentation is enabled, block + // offset changes should cause re-layout, since we will fragment at other + // locations than before. + if (UNLIKELY(!is_bfc_offset_equal && new_space.HasBlockFragmentation())) { + DCHECK(old_space.HasBlockFragmentation()); + return nullptr; + } + + bool is_exclusion_space_equal = + new_space.ExclusionSpace() == old_space.ExclusionSpace(); + + // If anything changes within the layout regarding floats, we need to perform + // a series of additional checks to see if the result can still be reused. + if (!is_bfc_offset_equal || !is_exclusion_space_equal || + new_space.ClearanceOffset() != old_space.ClearanceOffset()) { + // We can't reuse a result if it was previously affected by clearance. + // + // TODO(layout-dev): There may be cases where we can re-use results here. + // However as there are complexities regarding margin-collapsing and + // clearance we currently don't attempt to cache anything. + if (cached_layout_result->IsPushedByFloats()) { + DCHECK(old_space.HasFloats()); + return nullptr; + } + + // Check we have a descendant that *may* be positioned above the block-start + // edge. We abort if either the old or new space has floats, as we don't + // keep track of how far above the child could be. This case is relatively + // rare, and only occurs with negative margins. + if (cached_layout_result->MayHaveDescendantAboveBlockStart() && + (old_space.HasFloats() || new_space.HasFloats())) return nullptr; - // Even for the first fragment, when block fragmentation is enabled, block - // offset changes should cause re-layout, since we will fragment at other - // locations than before. - if (new_space.HasBlockFragmentation() || old_space.HasBlockFragmentation()) - return nullptr; + // We can now try to adjust the BFC block-offset. + if (bfc_block_offset) { + // Check if the previous position may intersect with any floats. + if (*bfc_block_offset < + old_space.ExclusionSpace().ClearanceOffset(EClear::kBoth)) + return nullptr; - if (bfc_block_offset.has_value()) { bfc_block_offset = *bfc_block_offset - old_space.BfcOffset().block_offset + new_space.BfcOffset().block_offset; + + // Check if the new position may intersect with any floats. + if (*bfc_block_offset < + new_space.ExclusionSpace().ClearanceOffset(EClear::kBoth)) + return nullptr; } } @@ -2375,10 +2409,19 @@ // The checks above should be enough to bail if layout is incomplete, but // let's verify: DCHECK(IsBlockLayoutComplete(old_space, *cached_layout_result)); - if (is_bfc_offset_equal) + + // We can safely reuse this result if our BFC and "input" exclusion spaces + // were equal. + if (is_bfc_offset_equal && is_exclusion_space_equal) { + // In order not to rebuild the internal derived-geometry "cache" of float + // data, we need to move this to the new "output" exclusion space. + cached_layout_result->ExclusionSpace().MoveAndUpdateDerivedGeometry( + new_space.ExclusionSpace()); return cached_layout_result; + } return base::AdoptRef(new NGLayoutResult(*cached_layout_result, + new_space.ExclusionSpace(), bfc_line_offset, bfc_block_offset)); }
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.h b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.h index 1a67e0d..41a152e 100644 --- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.h +++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.h
@@ -28,13 +28,30 @@ // Struct that represents an exclusion. This currently is just a float but // we've named it an exclusion to potentially support other types in the future. struct CORE_EXPORT NGExclusion : public RefCounted<NGExclusion> { - static scoped_refptr<NGExclusion> Create( + static scoped_refptr<const NGExclusion> Create( const NGBfcRect& rect, const EFloat type, std::unique_ptr<NGExclusionShapeData> shape_data = nullptr) { return base::AdoptRef(new NGExclusion(rect, type, std::move(shape_data))); } + scoped_refptr<const NGExclusion> CopyWithOffset( + const NGBfcDelta& offset_delta) const { + if (!offset_delta.line_offset_delta && !offset_delta.block_offset_delta) + return this; + + NGBfcRect new_rect = rect; + new_rect.start_offset += offset_delta; + new_rect.end_offset += offset_delta; + + return base::AdoptRef(new NGExclusion( + new_rect, type, + shape_data ? std::make_unique<NGExclusionShapeData>( + shape_data->layout_box, shape_data->margins, + shape_data->shape_insets) + : nullptr)); + } + const NGBfcRect rect; const EFloat type; const std::unique_ptr<NGExclusionShapeData> shape_data;
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc index ec634cd..592344b 100644 --- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc +++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
@@ -174,7 +174,6 @@ NGExclusionSpaceInternal::NGExclusionSpaceInternal() : exclusions_(RefVector<scoped_refptr<const NGExclusion>>::Create()), num_exclusions_(0), - both_clear_offset_(LayoutUnit::Min()), track_shape_exclusions_(false), derived_geometry_(nullptr) {} @@ -182,7 +181,9 @@ const NGExclusionSpaceInternal& other) : exclusions_(other.exclusions_), num_exclusions_(other.num_exclusions_), - both_clear_offset_(other.both_clear_offset_), + left_clear_offset_(other.left_clear_offset_), + right_clear_offset_(other.right_clear_offset_), + last_float_block_start_(other.last_float_block_start_), track_shape_exclusions_(other.track_shape_exclusions_), derived_geometry_(std::move(other.derived_geometry_)) { // This copy-constructor does fun things. It moves the derived_geometry_ to @@ -197,7 +198,9 @@ const NGExclusionSpaceInternal& other) { exclusions_ = other.exclusions_; num_exclusions_ = other.num_exclusions_; - both_clear_offset_ = other.both_clear_offset_; + left_clear_offset_ = other.left_clear_offset_; + right_clear_offset_ = other.right_clear_offset_; + last_float_block_start_ = other.last_float_block_start_; track_shape_exclusions_ = other.track_shape_exclusions_; derived_geometry_ = std::move(other.derived_geometry_); other.derived_geometry_ = nullptr; @@ -209,10 +212,7 @@ NGExclusionSpaceInternal::DerivedGeometry::DerivedGeometry( bool track_shape_exclusions) - : track_shape_exclusions_(track_shape_exclusions), - last_float_block_start_(LayoutUnit::Min()), - left_float_clear_offset_(LayoutUnit::Min()), - right_float_clear_offset_(LayoutUnit::Min()) { + : track_shape_exclusions_(track_shape_exclusions) { // The exclusion space must always have at least one shelf, at -Infinity. shelves_.emplace_back(/* block_offset */ LayoutUnit::Min(), track_shape_exclusions_); @@ -221,17 +221,23 @@ void NGExclusionSpaceInternal::Add(scoped_refptr<const NGExclusion> exclusion) { DCHECK_LE(num_exclusions_, exclusions_->size()); - // Perform a copy-on-write if the number of exclusions has gone out of sync. - if (num_exclusions_ != exclusions_->size()) { - scoped_refptr<RefVector<scoped_refptr<const NGExclusion>>> exclusions = - RefVector<scoped_refptr<const NGExclusion>>::Create(); - exclusions->GetMutableVector()->AppendRange( - exclusions_->GetVector().begin(), - exclusions_->GetVector().begin() + num_exclusions_); - std::swap(exclusions_, exclusions); + bool already_exists = false; - // The derived_geometry_ member is now invalid. - derived_geometry_ = nullptr; + if (num_exclusions_ < exclusions_->size()) { + if (*exclusion == *exclusions_->at(num_exclusions_) && + !exclusion->shape_data) { + // We might be adding an exclusion seen in a previous layout pass. + already_exists = true; + } else { + // Perform a copy-on-write if the number of exclusions has gone out of + // sync. + scoped_refptr<RefVector<scoped_refptr<const NGExclusion>>> exclusions = + RefVector<scoped_refptr<const NGExclusion>>::Create(); + exclusions->GetMutableVector()->AppendRange( + exclusions_->GetVector().begin(), + exclusions_->GetVector().begin() + num_exclusions_); + std::swap(exclusions_, exclusions); + } } // If this is the first exclusion with shape_data, the derived_geometry_ @@ -244,35 +250,31 @@ if (derived_geometry_) derived_geometry_->Add(*exclusion); - both_clear_offset_ = - std::max(both_clear_offset_, exclusion->rect.BlockEndOffset()); + // Update the members used for clearance calculations. + LayoutUnit clear_offset = exclusion->rect.BlockEndOffset(); + if (exclusion->type == EFloat::kLeft) + left_clear_offset_ = std::max(left_clear_offset_, clear_offset); + else if (exclusion->type == EFloat::kRight) + right_clear_offset_ = std::max(right_clear_offset_, clear_offset); - exclusions_->emplace_back(std::move(exclusion)); + last_float_block_start_ = + std::max(last_float_block_start_, exclusion->rect.BlockStartOffset()); + + if (!already_exists) + exclusions_->emplace_back(std::move(exclusion)); num_exclusions_++; } void NGExclusionSpaceInternal::DerivedGeometry::Add( const NGExclusion& exclusion) { - last_float_block_start_ = - std::max(last_float_block_start_, exclusion.rect.BlockStartOffset()); - - const LayoutUnit exclusion_end_offset = exclusion.rect.BlockEndOffset(); - - // Update the members used for clearance calculations. - if (exclusion.type == EFloat::kLeft) { - left_float_clear_offset_ = - std::max(left_float_clear_offset_, exclusion.rect.BlockEndOffset()); - } else if (exclusion.type == EFloat::kRight) { - right_float_clear_offset_ = - std::max(right_float_clear_offset_, exclusion.rect.BlockEndOffset()); - } - // If the exclusion takes up no inline space, we shouldn't pay any further // attention to it. The only thing it can affect is block-axis positioning of // subsequent floats (dealt with above). if (exclusion.rect.LineEndOffset() <= exclusion.rect.LineStartOffset()) return; + const LayoutUnit exclusion_end_offset = exclusion.rect.BlockEndOffset(); + #if DCHECK_IS_ON() bool inserted = false; #endif @@ -628,24 +630,6 @@ } } -LayoutUnit NGExclusionSpaceInternal::DerivedGeometry::ClearanceOffset( - EClear clear_type) const { - switch (clear_type) { - case EClear::kNone: - return LayoutUnit::Min(); // Nothing to do here. - case EClear::kLeft: - return left_float_clear_offset_; - case EClear::kRight: - return right_float_clear_offset_; - case EClear::kBoth: - return std::max(left_float_clear_offset_, right_float_clear_offset_); - default: - NOTREACHED(); - } - - return LayoutUnit::Min(); -} - const NGExclusionSpaceInternal::DerivedGeometry& NGExclusionSpaceInternal::GetDerivedGeometry() const { // Re-build the geometry if it isn't present.
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h index 87b3e4ca..01a7022 100644 --- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h +++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
@@ -42,7 +42,8 @@ const NGLogicalSize& minimum_size) const { // If the area clears all floats, we can just return the layout opportunity // which matches the available space. - if (offset.block_offset >= both_clear_offset_) { + if (offset.block_offset >= + std::max(left_clear_offset_, right_clear_offset_)) { NGBfcOffset end_offset( offset.line_offset + available_inline_size.ClampNegativeToZero(), LayoutUnit::Max()); @@ -58,7 +59,8 @@ const LayoutUnit available_inline_size) const { // If the area clears all floats, we can just return a single layout // opportunity which matches the available space. - if (offset.block_offset >= both_clear_offset_) { + if (offset.block_offset >= + std::max(left_clear_offset_, right_clear_offset_)) { NGBfcOffset end_offset( offset.line_offset + available_inline_size.ClampNegativeToZero(), LayoutUnit::Max()); @@ -71,18 +73,71 @@ } LayoutUnit ClearanceOffset(EClear clear_type) const { - if (clear_type == EClear::kNone) - return LayoutUnit::Min(); - - return GetDerivedGeometry().ClearanceOffset(clear_type); + switch (clear_type) { + case EClear::kNone: + return LayoutUnit::Min(); + case EClear::kLeft: + return left_clear_offset_; + case EClear::kRight: + return right_clear_offset_; + case EClear::kBoth: + return std::max(left_clear_offset_, right_clear_offset_); + default: + NOTREACHED(); + return LayoutUnit::Min(); + } } - LayoutUnit LastFloatBlockStart() const { - return GetDerivedGeometry().LastFloatBlockStart(); - } + LayoutUnit LastFloatBlockStart() const { return last_float_block_start_; } bool IsEmpty() const { return !num_exclusions_; } + // Pre-initializes the exclusions vector to something used in a previous + // layout pass, however keeps the number of exclusions as zero. + void PreInitialize(const NGExclusionSpaceInternal& other) { + DCHECK_EQ(exclusions_->size(), 0u); + DCHECK_GT(other.exclusions_->size(), 0u); + + exclusions_ = other.exclusions_; + } + + // See |NGExclusionSpace::MoveAndUpdateDerivedGeometry|. + void MoveAndUpdateDerivedGeometry(const NGExclusionSpaceInternal& other) { + if (!other.derived_geometry_) + return; + + derived_geometry_ = std::move(other.derived_geometry_); + other.derived_geometry_ = nullptr; + + // Iterate through all the exclusions which were added by the layout, and + // update the DerivedGeometry. + for (wtf_size_t i = other.num_exclusions_; i < num_exclusions_; ++i) { + const NGExclusion& exclusion = *exclusions_->at(i); + + // If we come across an exclusion with shape data, we opt-out of this + // optimization. + if (!track_shape_exclusions_ && exclusion.shape_data) { + track_shape_exclusions_ = true; + derived_geometry_ = nullptr; + return; + } + + derived_geometry_->Add(exclusion); + } + } + + // See |NGExclusionSpace::MergeExclusionSpaces|. + void MergeExclusionSpaces(const NGBfcDelta& offset_delta, + const NGExclusionSpaceInternal& previous_output, + const NGExclusionSpaceInternal* previous_input) { + // We need to copy all the exclusions over which were added by the cached + // layout result. + for (wtf_size_t i = previous_input ? previous_input->num_exclusions_ : 0; + i < previous_output.num_exclusions_; ++i) { + Add(previous_output.exclusions_->at(i)->CopyWithOffset(offset_delta)); + } + } + bool operator==(const NGExclusionSpaceInternal& other) const; bool operator!=(const NGExclusionSpaceInternal& other) const { return !(*this == other); @@ -181,7 +236,16 @@ // space has, which may differ to the number of exclusions in the Vector. scoped_refptr<RefVector<scoped_refptr<const NGExclusion>>> exclusions_; wtf_size_t num_exclusions_; - LayoutUnit both_clear_offset_; + + // These members are used for keeping track of the "lowest" offset for each + // type of float. This is used for implementing float clearance. + LayoutUnit left_clear_offset_ = LayoutUnit::Min(); + LayoutUnit right_clear_offset_ = LayoutUnit::Min(); + + // This member is used for implementing the "top edge alignment rule" for + // floats. Floats can be positioned at negative offsets, hence is initialized + // the minimum value. + LayoutUnit last_float_block_start_ = LayoutUnit::Min(); // In order to reduce the amount of copies related to bookkeeping shape data, // we initially ignore exclusions with shape data. When we first see an @@ -196,12 +260,12 @@ // // NGExclusionSpace space1; // space1.Add(exclusion1); - // space1.LastFloatBlockStart(); // Builds derived_geometry_ to answer query. + // space1.FindLayoutOpportunity(); // Builds derived_geometry_. // // NGExclusionSpace space2(space1); // Moves derived_geometry_ to space2. // space2.Add(exclusion2); // Modifies derived_geometry_. // - // space1.LastFloatBlockStart(); // Re-builds derived_geometry_. + // space1.FindLayoutOpportunity(); // Re-builds derived_geometry_. // // This is efficient (desirable) as the common usage pattern is only the last // exclusion space in the copy-chain is used for answering queries. Only when @@ -230,9 +294,6 @@ const LayoutUnit available_inline_size, const LambdaFunc&) const; - LayoutUnit ClearanceOffset(EClear clear_type) const; - LayoutUnit LastFloatBlockStart() const { return last_float_block_start_; } - // See NGShelf for a broad description of what shelves are. We always begin // with one, which has the internal value of: // { @@ -269,16 +330,6 @@ Vector<NGLayoutOpportunity, 4> opportunities_; bool track_shape_exclusions_; - - // This member is used for implementing the "top edge alignment rule" for - // floats. Floats can be positioned at negative offsets, hence is - // initialized the minimum value. - LayoutUnit last_float_block_start_; - - // These members are used for keeping track of the "lowest" offset for each - // type of float. This is used for implementing float clearance. - LayoutUnit left_float_clear_offset_; - LayoutUnit right_float_clear_offset_; }; // Returns the derived_geometry_ member, potentially re-built from the @@ -371,6 +422,83 @@ return !exclusion_space_ || exclusion_space_->IsEmpty(); } + // See |NGExclusionSpaceInternal::PreInitialize|. + void PreInitialize(const NGExclusionSpace& other) const { + // Don't pre-initialize if we've already got an exclusions vector. + if (exclusion_space_) + return; + + // Don't pre-initialize if the other exclusion space didn't have an + // exclusions vector. + if (!other.exclusion_space_) + return; + + exclusion_space_ = std::make_unique<NGExclusionSpaceInternal>(); + exclusion_space_->PreInitialize(*other.exclusion_space_); + } + + // Shifts the DerivedGeometry data-structure to this exclusion space, and + // adds any new exclusions. + void MoveAndUpdateDerivedGeometry(const NGExclusionSpace& other) const { + if (!exclusion_space_ || !other.exclusion_space_) + return; + + exclusion_space_->MoveAndUpdateDerivedGeometry(*other.exclusion_space_); + } + + // This produces a new exclusion space for a |NGLayoutResult| which is being + // re-used for caching purposes. + // + // It takes: + // - |old_output| The exclusion space associated with the cached layout + // result (the output of layout). + // - |old_input| The exclusion space which produced the cached layout result + // (the input into layout). + // - |new_input| The exclusion space which is being used to produce a new + // layout result (the new input into layout). + // - |offset_delta| the amount that the layout result was moved in BFC + // coordinate space. + // + // |old_output| should contain the *at least* same exclusions as |old_input| + // however may have added some more exclusions during its layout. + // + // This function takes those exclusions added by the cached layout-result + // (the difference between |old_output| and |old_input|), and adds them to + // |new_input|. It will additionally shift them by |offset_delta|. + // + // This produces the correct exclusion space "new_output" for the new reused + // layout result. + static NGExclusionSpace MergeExclusionSpaces( + const NGExclusionSpace& old_output, + const NGExclusionSpace& old_input, + const NGExclusionSpace& new_input, + const NGBfcDelta& offset_delta) { + // We start building the new exclusion space from the new input, this + // (should) have the derived geometry which will move to |new_output|. + NGExclusionSpace new_output = new_input; + + // If we didn't have any floats previously, we don't need to add any new + // ones, just return the new output. + if (!old_output.exclusion_space_) + return new_output; + + // If the layout didn't add any new exclusions, we can just return the new + // output. + if (old_input == old_output) + return new_output; + + if (!new_output.exclusion_space_) { + new_output.exclusion_space_ = + std::make_unique<NGExclusionSpaceInternal>(); + } + + new_output.exclusion_space_->MergeExclusionSpaces( + offset_delta, *old_output.exclusion_space_, + old_input.exclusion_space_.get()); + + return new_output; + } + bool operator==(const NGExclusionSpace& other) const { if (exclusion_space_ == other.exclusion_space_) return true; @@ -383,7 +511,7 @@ } private: - std::unique_ptr<NGExclusionSpaceInternal> exclusion_space_; + mutable std::unique_ptr<NGExclusionSpaceInternal> exclusion_space_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc index 66247b0..377c48f 100644 --- a/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc +++ b/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space_test.cc
@@ -341,5 +341,147 @@ NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); } +TEST(NGExclusionSpaceTest, PreInitialization) { + NGExclusionSpace original_exclusion_space; + + original_exclusion_space.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(20), LayoutUnit(15))), + EFloat::kLeft)); + original_exclusion_space.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()), + NGBfcOffset(LayoutUnit(85), LayoutUnit(15))), + EFloat::kRight)); + + NGExclusionSpace exclusion_space1; + exclusion_space1.PreInitialize(original_exclusion_space); + EXPECT_NE(original_exclusion_space, exclusion_space1); + + exclusion_space1.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(20), LayoutUnit(15))), + EFloat::kLeft)); + EXPECT_NE(original_exclusion_space, exclusion_space1); + + // Adding the same exclusions will make the spaces equal. + exclusion_space1.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(65), LayoutUnit()), + NGBfcOffset(LayoutUnit(85), LayoutUnit(15))), + EFloat::kRight)); + EXPECT_EQ(original_exclusion_space, exclusion_space1); + + // Adding a third exclusion will make the spaces non-equal. + exclusion_space1.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)), + NGBfcOffset(LayoutUnit(30), LayoutUnit(40))), + EFloat::kLeft)); + EXPECT_NE(original_exclusion_space, exclusion_space1); + + NGExclusionSpace exclusion_space2; + exclusion_space2.PreInitialize(original_exclusion_space); + EXPECT_NE(original_exclusion_space, exclusion_space2); + + exclusion_space2.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(), LayoutUnit()), + NGBfcOffset(LayoutUnit(20), LayoutUnit(15))), + EFloat::kLeft)); + EXPECT_NE(original_exclusion_space, exclusion_space2); + + // Adding a different second exclusion will make the spaces non-equal. + exclusion_space2.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)), + NGBfcOffset(LayoutUnit(30), LayoutUnit(40))), + EFloat::kLeft)); + EXPECT_NE(original_exclusion_space, exclusion_space2); +} + +TEST(NGExclusionSpaceTest, MergeExclusionSpacesNoPreviousExclusions) { + NGExclusionSpace old_input; + NGExclusionSpace old_output = old_input; + + old_output.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(10), LayoutUnit(25)), + NGBfcOffset(LayoutUnit(30), LayoutUnit(40))), + EFloat::kLeft)); + + NGExclusionSpace new_input; + + NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces( + old_output, old_input, new_input, + /* offset_delta */ {LayoutUnit(10), LayoutUnit(20)}); + + // To check the equality pre-initialize a new exclusion space with the + // |new_output|, and add the expected exclusions. + NGExclusionSpace expected; + expected.PreInitialize(new_output); + expected.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(40), LayoutUnit(60))), + EFloat::kLeft)); + + EXPECT_EQ(expected, new_output); +} + +TEST(NGExclusionSpaceTest, MergeExclusionSpacesPreviousExclusions) { + NGExclusionSpace old_input; + old_input.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(40), LayoutUnit(60))), + EFloat::kLeft)); + + NGExclusionSpace old_output = old_input; + old_output.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(100), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(140), LayoutUnit(60))), + EFloat::kRight)); + + NGExclusionSpace new_input; + new_input.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(40), LayoutUnit(50))), + EFloat::kLeft)); + + NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces( + old_output, old_input, new_input, + /* offset_delta */ {LayoutUnit(10), LayoutUnit(20)}); + + // To check the equality pre-initialize a new exclusion space with the + // |new_output|, and add the expected exclusions. + NGExclusionSpace expected; + expected.PreInitialize(new_output); + expected.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(40), LayoutUnit(50))), + EFloat::kLeft)); + expected.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(110), LayoutUnit(65)), + NGBfcOffset(LayoutUnit(150), LayoutUnit(80))), + EFloat::kRight)); + + EXPECT_EQ(expected, new_output); +} + +TEST(NGExclusionSpaceTest, MergeExclusionSpacesNoOutputExclusions) { + NGExclusionSpace old_input; + old_input.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(20), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(40), LayoutUnit(60))), + EFloat::kLeft)); + old_input.Add(NGExclusion::Create( + NGBfcRect(NGBfcOffset(LayoutUnit(100), LayoutUnit(45)), + NGBfcOffset(LayoutUnit(140), LayoutUnit(60))), + EFloat::kRight)); + + NGExclusionSpace old_output = old_input; + + NGExclusionSpace new_input; + NGExclusionSpace new_output = NGExclusionSpace::MergeExclusionSpaces( + old_output, old_input, new_input, + /* offset_delta */ {LayoutUnit(10), LayoutUnit(20)}); + + NGExclusionSpace expected; + EXPECT_EQ(expected, new_output); +} + } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h b/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h index f20e112..ded1830 100644 --- a/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h +++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h
@@ -10,6 +10,16 @@ namespace blink { +struct CORE_EXPORT NGBfcDelta { + NGBfcDelta() = default; + NGBfcDelta(LayoutUnit line_offset_delta, LayoutUnit block_offset_delta) + : line_offset_delta(line_offset_delta), + block_offset_delta(block_offset_delta) {} + + LayoutUnit line_offset_delta; + LayoutUnit block_offset_delta; +}; + // NGBfcOffset is the position of a rect (typically a fragment) relative to // a block formatting context (BFC). BFCs are agnostic to text direction, and // uses line_offset instead of inline_offset. @@ -24,6 +34,16 @@ LayoutUnit line_offset; LayoutUnit block_offset; + NGBfcOffset& operator+=(const NGBfcDelta& delta) { + *this = *this + delta; + return *this; + } + + NGBfcOffset operator+(const NGBfcDelta& delta) { + return {line_offset + delta.line_offset_delta, + block_offset + delta.block_offset_delta}; + } + bool operator==(const NGBfcOffset& other) const; bool operator!=(const NGBfcOffset& other) const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index feabadc3..75f72d4 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -220,6 +220,16 @@ if (block_flow && !first_child) block_flow->ClearNGInlineNodeData(); + // The exclusion space internally is a pointer to a shared vector, and + // equality of exclusion spaces is performed using pointer comparison on this + // internal shared vector. + // In order for the caching logic to work correctly we need to set the + // pointer to the value previous shared vector. + if (const NGLayoutResult* previous_result = box_->GetCachedLayoutResult()) { + constraint_space.ExclusionSpace().PreInitialize( + previous_result->GetConstraintSpaceForCaching().ExclusionSpace()); + } + scoped_refptr<const NGLayoutResult> layout_result = box_->CachedLayoutResult(constraint_space, break_token); if (layout_result) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index 4d4d616..154213ec 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -421,16 +421,20 @@ // to verify that any constraint space size (available size, percentage size, // and so on) and BFC offset changes won't require re-layout, before skipping. bool MaySkipLayout(const NGConstraintSpace& other) const { - if (HasRareData() && other.HasRareData()) { - if (!rare_data_->MaySkipLayout(*other.rare_data_)) - return false; - } else if (HasRareData() != other.HasRareData()) { - // We have a bfc_offset_, and a rare_data_ (or vice-versa). + if (!bitfields_.MaySkipLayout(other.bitfields_)) return false; - } - return exclusion_space_ == other.exclusion_space_ && - bitfields_.MaySkipLayout(other.bitfields_); + if (!HasRareData() && !other.HasRareData()) + return true; + + if (HasRareData() && other.HasRareData()) + return rare_data_->MaySkipLayout(*other.rare_data_); + + if (HasRareData()) + return rare_data_->IsInitialForMaySkipLayout(); + + DCHECK(other.HasRareData()); + return other.rare_data_->IsInitialForMaySkipLayout(); } bool AreSizesEqual(const NGConstraintSpace& other) const { @@ -526,13 +530,21 @@ bool MaySkipLayout(const RareData& other) const { return margin_strut == other.margin_strut && floats_bfc_block_offset == other.floats_bfc_block_offset && - clearance_offset == other.clearance_offset && fragmentainer_block_size == other.fragmentainer_block_size && fragmentainer_space_at_bfc_start == other.fragmentainer_space_at_bfc_start && block_direction_fragmentation_type == other.block_direction_fragmentation_type; } + + // Must be kept in sync with members checked within |MaySkipLayout|. + bool IsInitialForMaySkipLayout() const { + return margin_strut == NGMarginStrut() && + floats_bfc_block_offset == base::nullopt && + fragmentainer_block_size == NGSizeIndefinite && + fragmentainer_space_at_bfc_start == NGSizeIndefinite && + block_direction_fragmentation_type == kFragmentNone; + } }; // This struct simply allows us easily copy, compare, and initialize all the
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc index 77f9b791..f6d3b47 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -87,6 +87,10 @@ !child.PhysicalFragment()->IsOutOfFlowPositioned()) has_child_that_depends_on_percentage_block_size_ = true; + if (child.MayHaveDescendantAboveBlockStart() && + !child.PhysicalFragment()->IsBlockFormattingContextRoot()) + may_have_descendant_above_block_start_ = true; + return AddChild(child.PhysicalFragment(), child_offset); } @@ -127,6 +131,9 @@ } } + if (child_offset.block_offset < LayoutUnit()) + may_have_descendant_above_block_start_ = true; + if (!IsParallelWritingMode(child->Style().GetWritingMode(), Style().GetWritingMode())) has_orthogonal_flow_roots_ = true;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h index ebff9391..6e9b284 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -248,6 +248,7 @@ bool has_orthogonal_flow_roots_ = false; bool has_child_that_depends_on_percentage_block_size_ = false; bool has_block_fragmentation_ = false; + bool may_have_descendant_above_block_start_ = false; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc index 83b2b7e0..e8c6ba8 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc
@@ -143,7 +143,7 @@ // Creates an exclusion from the fragment that will be placed in the provided // layout opportunity. -scoped_refptr<NGExclusion> CreateExclusion( +scoped_refptr<const NGExclusion> CreateExclusion( const NGLogicalSize& float_available_size, const NGLogicalSize& float_percentage_size, const NGLogicalSize& float_replaced_percentage_size, @@ -291,7 +291,7 @@ } // Add the float as an exclusion. - scoped_refptr<NGExclusion> exclusion = CreateExclusion( + scoped_refptr<const NGExclusion> exclusion = CreateExclusion( float_available_size, float_percentage_size, float_replaced_percentage_size, float_fragment, float_margin_bfc_offset, fragment_margins, *unpositioned_float, parent_space, parent_style,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index 87342a0b..47ec7e2 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -46,14 +46,19 @@ << "Use the other constructor for successful layout"; } -NGLayoutResult::NGLayoutResult(const NGLayoutResult& other, - LayoutUnit bfc_line_offset, - base::Optional<LayoutUnit> bfc_block_offset) +NGLayoutResult::NGLayoutResult( + const NGLayoutResult& other, + const NGExclusionSpace& new_input_exclusion_space, + LayoutUnit bfc_line_offset, + base::Optional<LayoutUnit> bfc_block_offset) : space_(other.space_), physical_fragment_(other.physical_fragment_), oof_positioned_descendants_(other.oof_positioned_descendants_), unpositioned_list_marker_(other.unpositioned_list_marker_), - exclusion_space_(other.exclusion_space_), + exclusion_space_(MergeExclusionSpaces(other, + new_input_exclusion_space, + bfc_line_offset, + bfc_block_offset)), bfc_line_offset_(bfc_line_offset), bfc_block_offset_(bfc_block_offset), end_margin_strut_(other.end_margin_strut_), @@ -66,6 +71,8 @@ is_pushed_by_floats_(other.is_pushed_by_floats_), adjoining_floats_(other.adjoining_floats_), has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_), + may_have_descendant_above_block_start_( + other.may_have_descendant_above_block_start_), depends_on_percentage_block_size_( other.depends_on_percentage_block_size_), status_(other.status_) {} @@ -85,6 +92,8 @@ is_pushed_by_floats_(builder->is_pushed_by_floats_), adjoining_floats_(builder->adjoining_floats_), has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_), + may_have_descendant_above_block_start_( + builder->may_have_descendant_above_block_start_), depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)), status_(kSuccess) {} @@ -130,4 +139,27 @@ return false; } +NGExclusionSpace NGLayoutResult::MergeExclusionSpaces( + const NGLayoutResult& other, + const NGExclusionSpace& new_input_exclusion_space, + LayoutUnit bfc_line_offset, + base::Optional<LayoutUnit> bfc_block_offset) { + // If we are merging exclusion spaces we should be copying a previous layout + // result. It is impossible to reach a state where bfc_block_offset has a + // value, and the result which we are copying doesn't (or visa versa). + // This would imply the result has switched its "empty" state for margin + // collapsing, which would mean it isn't possible to reuse the result. + DCHECK_EQ(bfc_block_offset.has_value(), other.bfc_block_offset_.has_value()); + + NGBfcDelta offset_delta = {bfc_line_offset - other.bfc_line_offset_, + bfc_block_offset && other.bfc_block_offset_ + ? *bfc_block_offset - *other.bfc_block_offset_ + : LayoutUnit()}; + + return NGExclusionSpace::MergeExclusionSpaces( + /* old_output */ other.exclusion_space_, + /* old_input */ other.space_.ExclusionSpace(), + /* new_input */ new_input_exclusion_space, offset_delta); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index c7865c0..cb33a38 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -43,7 +43,8 @@ // Create a copy of NGLayoutResult with |BfcBlockOffset| replaced by the given // parameter. Note, when |bfc_block_offset| is |nullopt|, |BfcBlockOffset| is // still replaced with |nullopt|. - NGLayoutResult(const NGLayoutResult&, + NGLayoutResult(const NGLayoutResult& other, + const NGExclusionSpace& new_input_exclusion_space, LayoutUnit bfc_line_offset, base::Optional<LayoutUnit> bfc_block_offset); ~NGLayoutResult(); @@ -116,6 +117,12 @@ return depends_on_percentage_block_size_; } + // Returns true if we have a descendant within this formatting context, which + // is potentially above our block-start edge. + bool MayHaveDescendantAboveBlockStart() const { + return may_have_descendant_above_block_start_; + } + // Returns true if the space stored with this layout result, is valid. bool HasValidConstraintSpaceForCaching() const { return has_valid_space_; } @@ -147,6 +154,12 @@ static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&); + static NGExclusionSpace MergeExclusionSpaces( + const NGLayoutResult& other, + const NGExclusionSpace& new_input_exclusion_space, + LayoutUnit bfc_line_offset, + base::Optional<LayoutUnit> bfc_block_offset); + // The constraint space which generated this layout result, may not be valid // as indicated by |has_valid_space_|. const NGConstraintSpace space_; @@ -173,6 +186,7 @@ unsigned adjoining_floats_ : 2; // NGFloatTypes unsigned has_orthogonal_flow_roots_ : 1; + unsigned may_have_descendant_above_block_start_ : 1; unsigned depends_on_percentage_block_size_ : 1; unsigned status_ : 1;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc new file mode 100644 index 0000000..c28116a --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
@@ -0,0 +1,398 @@ +// 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 "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" + +namespace blink { +namespace { + +// These tests exercise the caching logic of |NGLayoutResult|s. They are +// rendering tests which contain two children: "test" and "src". +// +// Both have layout initially performed on them, however the "src" will have a +// different |NGConstraintSpace| which is then used to test either a cache hit +// or miss. +class NGLayoutResultCachingTest : public NGLayoutTest {}; + +TEST_F(NGLayoutResultCachingTest, HitDifferentExclusionSpace) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Same BFC offset, different exclusion space. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="test" style="height: 20px;"></div> + </div> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 30px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_NE(result.get(), nullptr); + EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(50)); + EXPECT_EQ(result->BfcLineOffset(), LayoutUnit()); +} + +TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffset) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Different BFC offset, same exclusion space. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="test" style="height: 20px; padding-top: 5px;"> + <div class="float" style="height: 20px;"></div> + </div> + </div> + <div class="bfc"> + <div style="height: 40px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="src" style="height: 20px; padding-top: 5px;"> + <div class="float" style="height: 20px;"></div> + </div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_NE(result.get(), nullptr); + EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(40)); + EXPECT_EQ(result->BfcLineOffset(), LayoutUnit()); + + // Also check that the exclusion(s) got moved correctly. + LayoutOpportunityVector opportunities = + result->ExclusionSpace().AllLayoutOpportunities( + /* offset */ {LayoutUnit(), LayoutUnit()}, + /* available_inline_size */ LayoutUnit(100)); + + EXPECT_EQ(opportunities.size(), 3u); + EXPECT_EQ(opportunities[0].rect.start_offset, + NGBfcOffset(LayoutUnit(50), LayoutUnit())); + EXPECT_EQ(opportunities[0].rect.end_offset, + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); + + EXPECT_EQ(opportunities[1].rect.start_offset, + NGBfcOffset(LayoutUnit(), LayoutUnit(20))); + EXPECT_EQ(opportunities[1].rect.end_offset, + NGBfcOffset(LayoutUnit(100), LayoutUnit(45))); + + EXPECT_EQ(opportunities[2].rect.start_offset, + NGBfcOffset(LayoutUnit(), LayoutUnit(65))); + EXPECT_EQ(opportunities[2].rect.end_offset, + NGBfcOffset(LayoutUnit(100), LayoutUnit::Max())); +} + +TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart1) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Same BFC offset, different exclusion space, descendant above + // block start. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="test" style="height: 20px; padding-top: 5px;"> + <div style="height: 10px; margin-top: -10px;"></div> + </div> + </div> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 30px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart2) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Different BFC offset, same exclusion space, descendant above + // block start. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="test" style="height: 20px; padding-top: 5px;"> + <div style="height: 10px; margin-top: -10px;"></div> + </div> + </div> + <div class="bfc"> + <div style="height: 40px;"> + <div class="float" style="height: 20px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding1) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Same BFC offset, different exclusion space, float initially + // intruding. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="test" style="height: 20px;"></div> + </div> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 30px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding2) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Different BFC offset, same exclusion space, float initially + // intruding. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="test" style="height: 60px;"></div> + </div> + <div class="bfc"> + <div style="height: 70px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude1) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Same BFC offset, different exclusion space, float will intrude. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 40px;"></div> + </div> + <div id="test" style="height: 20px;"></div> + </div> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude2) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Different BFC offset, same exclusion space, float will intrude. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 40px;"></div> + </div> + <div id="test" style="height: 60px;"></div> + </div> + <div class="bfc"> + <div style="height: 30px;"> + <div class="float" style="height: 40px;"></div> + </div> + <div id="src" style="height: 20px;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Same BFC offset, different exclusion space, pushed by floats. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="test" style="height: 20px; clear: left;"></div> + </div> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 40px;"></div> + </div> + <div id="src" style="height: 20px; clear: left;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) { + ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); + + // Different BFC offset, same exclusion space, pushed by floats. + SetBodyInnerHTML(R"HTML( + <style> + .bfc { display: flow-root; width: 300px; height: 300px; } + .float { float: left; width: 50px; } + </style> + <div class="bfc"> + <div style="height: 50px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="test" style="height: 20px; clear: left;"></div> + </div> + <div class="bfc"> + <div style="height: 30px;"> + <div class="float" style="height: 60px;"></div> + </div> + <div id="src" style="height: 20px; clear: left;"></div> + </div> + )HTML"); + + LayoutBlockFlow* test = ToLayoutBlockFlow(GetLayoutObjectByElementId("test")); + LayoutBlockFlow* src = ToLayoutBlockFlow(GetLayoutObjectByElementId("src")); + + const NGConstraintSpace& space = + src->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = + test->CachedLayoutResult(space, nullptr); + + EXPECT_EQ(result.get(), nullptr); +} + +} // namespace +} // namespace blink
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h index 0fe23a5..361a47fa 100644 --- a/third_party/blink/renderer/core/loader/empty_clients.h +++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -138,7 +138,6 @@ Page* CreateWindowDelegate(LocalFrame*, const FrameLoadRequest&, const WebWindowFeatures&, - NavigationPolicy, SandboxFlags, const FeaturePolicy::FeatureState&, const SessionStorageNamespaceId&) override {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index d7fa95b0..9188670 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -840,6 +840,7 @@ if (!target_frame && !request.FrameName().IsEmpty() && should_navigate_target_frame) { request.SetFrameType(network::mojom::RequestContextFrameType::kAuxiliary); + request.SetNavigationPolicy(kNavigationPolicyNewForegroundTab); CreateWindowForRequest(request, *frame_); return; // Navigation will be handled by the new frame/window. }
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc index 43ceb0c..24b9e00 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc +++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -246,7 +246,7 @@ } IntSize ImageResourceContent::IntrinsicSize( - RespectImageOrientationEnum should_respect_image_orientation) { + RespectImageOrientationEnum should_respect_image_orientation) const { if (!image_) return IntSize(); if (should_respect_image_orientation == kRespectImageOrientation && @@ -507,10 +507,11 @@ resource_length = static_cast<double>(GetImage()->Data()->size()); } - // Calculate the image's adjusted compression ratio (in bytes per pixel). A - // constant allowance (1024 bytes) is provided to allow room for headers and - // to account for small images (which are harder to compress). - double compression_ratio = (resource_length - 1024) / pixels; + // Calculate the image's compression ratio (in bytes per pixel) with both 1k + // and 10k overhead. The constant overhead allowance is provided to allow room + // for headers and to account for small images (which are harder to compress). + double compression_ratio_1k = (resource_length - 1024) / pixels; + double compression_ratio_10k = (resource_length - 10240) / pixels; // Note that this approach may not always correctly identify the image (for // example, due to a misconfigured web server). This approach SHOULD work in @@ -522,7 +523,13 @@ // Enforce the lossy image policy. return context.IsFeatureEnabled( mojom::FeaturePolicyFeature::kUnoptimizedLossyImages, - PolicyValue(compression_ratio), ReportOptions::kReportOnFailure); + PolicyValue(compression_ratio_1k), ReportOptions::kReportOnFailure); + } + if (MIMETypeRegistry::IsLosslessImageMIMEType(mime_type)) { + // Enforce the lossless image policy. + return context.IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUnoptimizedLosslessImages, + PolicyValue(compression_ratio_10k), ReportOptions::kReportOnFailure); } return true;
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h index b32c43ae..9e1aa362 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h +++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -74,7 +74,7 @@ // object size resolved using a default object size of 300x150. // TODO(fs): Make SVGImages return proper intrinsic width/height. IntSize IntrinsicSize( - RespectImageOrientationEnum should_respect_image_orientation); + RespectImageOrientationEnum should_respect_image_orientation) const; void UpdateImageAnimationPolicy();
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.cc b/third_party/blink/renderer/core/loader/resource/script_resource.cc index e684b18..dc76a049 100644 --- a/third_party/blink/renderer/core/loader/resource/script_resource.cc +++ b/third_party/blink/renderer/core/loader/resource/script_resource.cc
@@ -249,7 +249,8 @@ } void ScriptResource::ResponseBodyReceived( - ResponseBodyLoaderDrainableInterface& body_loader) { + ResponseBodyLoaderDrainableInterface& body_loader, + scoped_refptr<base::SingleThreadTaskRunner> loader_task_runner) { ResponseBodyLoaderClient* response_body_loader_client; CHECK(!data_pipe_); data_pipe_ = body_loader.DrainAsDataPipe(&response_body_loader_client); @@ -258,8 +259,7 @@ response_body_loader_client_ = response_body_loader_client; watcher_ = std::make_unique<mojo::SimpleWatcher>( - FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, - Loader()->GetLoadingTaskRunner()); + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, loader_task_runner); watcher_->Watch(data_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.h b/third_party/blink/renderer/core/loader/resource/script_resource.h index 9ed6d69..1aa8e72 100644 --- a/third_party/blink/renderer/core/loader/resource/script_resource.h +++ b/third_party/blink/renderer/core/loader/resource/script_resource.h
@@ -94,7 +94,8 @@ ~ScriptResource() override; void ResponseBodyReceived( - ResponseBodyLoaderDrainableInterface& body_loader) override; + ResponseBodyLoaderDrainableInterface& body_loader, + scoped_refptr<base::SingleThreadTaskRunner> loader_task_runner) override; void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/core/page/BUILD.gn b/third_party/blink/renderer/core/page/BUILD.gn index 0dbf16e1..ed595c3 100644 --- a/third_party/blink/renderer/core/page/BUILD.gn +++ b/third_party/blink/renderer/core/page/BUILD.gn
@@ -91,6 +91,8 @@ "scrolling/text_fragment_anchor.h", "scrolling/text_fragment_finder.cc", "scrolling/text_fragment_finder.h", + "scrolling/text_fragment_selector.cc", + "scrolling/text_fragment_selector.h", "scrolling/top_document_root_scroller_controller.cc", "scrolling/top_document_root_scroller_controller.h", "scrolling/viewport_scroll_callback.cc",
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc index b5dc2e2..8bf9ac4 100644 --- a/third_party/blink/renderer/core/page/chrome_client.cc +++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -103,7 +103,6 @@ LocalFrame* frame, const FrameLoadRequest& r, const WebWindowFeatures& features, - NavigationPolicy navigation_policy, SandboxFlags sandbox_flags, const FeaturePolicy::FeatureState& opener_feature_state, const SessionStorageNamespaceId& session_storage_namespace_id) { @@ -112,8 +111,8 @@ return nullptr; } - return CreateWindowDelegate(frame, r, features, navigation_policy, - sandbox_flags, opener_feature_state, + return CreateWindowDelegate(frame, r, features, sandbox_flags, + opener_feature_state, session_storage_namespace_id); }
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h index afec0ec..2478359 100644 --- a/third_party/blink/renderer/core/page/chrome_client.h +++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -159,7 +159,6 @@ Page* CreateWindow(LocalFrame*, const FrameLoadRequest&, const WebWindowFeatures&, - NavigationPolicy, SandboxFlags, const FeaturePolicy::FeatureState&, const SessionStorageNamespaceId&); @@ -413,7 +412,6 @@ virtual Page* CreateWindowDelegate(LocalFrame*, const FrameLoadRequest&, const WebWindowFeatures&, - NavigationPolicy, SandboxFlags, const FeaturePolicy::FeatureState&, const SessionStorageNamespaceId&) = 0;
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc index 58f973b..65122bd 100644 --- a/third_party/blink/renderer/core/page/chrome_client_impl.cc +++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -252,7 +252,6 @@ LocalFrame* frame, const FrameLoadRequest& r, const WebWindowFeatures& features, - NavigationPolicy navigation_policy, SandboxFlags sandbox_flags, const FeaturePolicy::FeatureState& opener_feature_state, const SessionStorageNamespaceId& session_storage_namespace_id) { @@ -270,7 +269,7 @@ static_cast<WebViewImpl*>(web_view_->Client()->CreateView( WebLocalFrameImpl::FromFrame(frame), WrappedResourceRequest(r.GetResourceRequest()), features, frame_name, - static_cast<WebNavigationPolicy>(navigation_policy), + static_cast<WebNavigationPolicy>(r.GetNavigationPolicy()), r.GetShouldSetOpener() == kNeverSetOpener, static_cast<WebSandboxFlags>(sandbox_flags), opener_feature_state, session_storage_namespace_id));
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h index c118059..1e6bee5 100644 --- a/third_party/blink/renderer/core/page/chrome_client_impl.h +++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -80,7 +80,6 @@ Page* CreateWindowDelegate(LocalFrame*, const FrameLoadRequest&, const WebWindowFeatures&, - NavigationPolicy, SandboxFlags, const FeaturePolicy::FeatureState&, const SessionStorageNamespaceId&) override;
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc index c72505f..1814c9f 100644 --- a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc +++ b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
@@ -92,11 +92,11 @@ ScopedPagePauser pauser; LocalFrame* frame = To<WebLocalFrameImpl>(main_frame_)->GetFrame(); FrameLoadRequest request(frame->GetDocument()); + request.SetNavigationPolicy(kNavigationPolicyNewForegroundTab); WebWindowFeatures features; - EXPECT_EQ(nullptr, - chrome_client_impl_->CreateWindow( - frame, request, features, kNavigationPolicyNewForegroundTab, - kSandboxNone, FeaturePolicy::FeatureState(), "")); + EXPECT_EQ(nullptr, chrome_client_impl_->CreateWindow( + frame, request, features, kSandboxNone, + FeaturePolicy::FeatureState(), "")); } class FakeColorChooserClient
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc index 0cea61a..ac65f93 100644 --- a/third_party/blink/renderer/core/page/create_window.cc +++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -49,10 +49,8 @@ #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/loader/frame_load_request.h" #include "third_party/blink/renderer/core/page/chrome_client.h" -#include "third_party/blink/renderer/core/page/focus_controller.h" #include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/probe/core_probes.h" -#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" @@ -185,27 +183,6 @@ return window_features; } -static Frame* ReuseExistingWindow(LocalFrame& active_frame, - LocalFrame& lookup_frame, - const AtomicString& frame_name, - const KURL& destination_url) { - if (!frame_name.IsEmpty() && !EqualIgnoringASCIICase(frame_name, "_blank")) { - if (Frame* frame = lookup_frame.FindFrameForNavigation( - frame_name, active_frame, destination_url)) { - if (!EqualIgnoringASCIICase(frame_name, "_self")) { - if (Page* page = frame->GetPage()) { - if (page == active_frame.GetPage()) - page->GetFocusController().SetFocusedFrame(frame); - else - page->GetChromeClient().Focus(&active_frame); - } - } - return frame; - } - } - return nullptr; -} - static void MaybeLogWindowOpen(LocalFrame& opener_frame) { AdTracker* ad_tracker = opener_frame.GetAdTracker(); if (!ad_tracker) @@ -233,15 +210,30 @@ static Frame* CreateNewWindow(LocalFrame& opener_frame, const FrameLoadRequest& request, const WebWindowFeatures& features, - bool force_new_foreground_tab, bool& created) { - Page* old_page = opener_frame.GetPage(); - if (!old_page) - return nullptr; + DCHECK(request.GetResourceRequest().RequestorOrigin() || + opener_frame.GetDocument()->Url().IsEmpty()); + DCHECK_EQ(request.GetFrameType(), + network::mojom::RequestContextFrameType::kAuxiliary); - NavigationPolicy policy = force_new_foreground_tab - ? kNavigationPolicyNewForegroundTab - : NavigationPolicyForCreateWindow(features); + const KURL& url = request.GetResourceRequest().Url(); + probe::WindowOpen(opener_frame.GetDocument(), url, request.FrameName(), + features, + LocalFrame::HasTransientUserActivation(&opener_frame)); + created = false; + + // Sandboxed frames cannot open new auxiliary browsing contexts. + if (opener_frame.GetDocument()->IsSandboxed(kSandboxPopups)) { + // FIXME: This message should be moved off the console once a solution to + // https://bugs.webkit.org/show_bug.cgi?id=103274 exists. + opener_frame.GetDocument()->AddConsoleMessage(ConsoleMessage::Create( + mojom::ConsoleMessageSource::kSecurity, + mojom::ConsoleMessageLevel::kError, + "Blocked opening '" + url.ElidedString() + + "' in a new window because the request was made in a sandboxed " + "frame whose 'allow-popups' permission is not set.")); + return nullptr; + } bool propagate_sandbox = opener_frame.GetDocument()->IsSandboxed( kSandboxPropagatesToAuxiliaryBrowsingContexts); @@ -258,6 +250,7 @@ SessionStorageNamespaceId new_namespace_id = AllocateSessionStorageNamespaceId(); + Page* old_page = opener_frame.GetPage(); if (base::FeatureList::IsEnabled(features::kOnionSoupDOMStorage)) { // TODO(dmurph): Don't copy session storage when features.noopener is true: // https://html.spec.whatwg.org/C/#copy-session-storage @@ -267,8 +260,8 @@ } Page* page = old_page->GetChromeClient().CreateWindow( - &opener_frame, request, features, policy, sandbox_flags, - opener_feature_state, new_namespace_id); + &opener_frame, request, features, sandbox_flags, opener_feature_state, + new_namespace_id); if (!page) return nullptr; @@ -299,90 +292,21 @@ window_rect.SetHeight(features.height); page->GetChromeClient().SetWindowRectWithAdjustment(window_rect, frame); - page->GetChromeClient().Show(policy); + page->GetChromeClient().Show(request.GetNavigationPolicy()); MaybeLogWindowOpen(opener_frame); created = true; return &frame; } -static Frame* CreateWindowHelper(LocalFrame& opener_frame, - LocalFrame& active_frame, - LocalFrame& lookup_frame, - const FrameLoadRequest& request, - const WebWindowFeatures& features, - bool force_new_foreground_tab, - bool& created) { - DCHECK(request.GetResourceRequest().RequestorOrigin() || - opener_frame.GetDocument()->Url().IsEmpty()); - DCHECK_EQ(request.GetFrameType(), - network::mojom::RequestContextFrameType::kAuxiliary); - probe::WindowOpen(opener_frame.GetDocument(), - request.GetResourceRequest().Url(), request.FrameName(), - features, - LocalFrame::HasTransientUserActivation(&opener_frame)); - created = false; - - Frame* window = - features.noopener || force_new_foreground_tab - ? nullptr - : ReuseExistingWindow(active_frame, lookup_frame, request.FrameName(), - request.GetResourceRequest().Url()); - - if (!window) { - // Sandboxed frames cannot open new auxiliary browsing contexts. - if (opener_frame.GetDocument()->IsSandboxed(kSandboxPopups)) { - // FIXME: This message should be moved off the console once a solution to - // https://bugs.webkit.org/show_bug.cgi?id=103274 exists. - opener_frame.GetDocument()->AddConsoleMessage(ConsoleMessage::Create( - mojom::ConsoleMessageSource::kSecurity, - mojom::ConsoleMessageLevel::kError, - "Blocked opening '" + - request.GetResourceRequest().Url().ElidedString() + - "' in a new window because the request was made in a sandboxed " - "frame whose 'allow-popups' permission is not set.")); - return nullptr; - } - } - - if (window) { - // JS can run inside ReuseExistingWindow (via onblur), which can detach - // the target window. - if (!window->Client()) - return nullptr; - if (request.GetShouldSetOpener() == kMaybeSetOpener) - window->Client()->SetOpener(&opener_frame); - return window; - } - - return CreateNewWindow(opener_frame, request, features, - force_new_foreground_tab, created); -} - -DOMWindow* CreateWindow(const String& url_string, +DOMWindow* CreateWindow(const KURL& completed_url, const AtomicString& frame_name, - const String& window_features_string, + const WebWindowFeatures& window_features, LocalDOMWindow& incumbent_window, - LocalFrame& entered_window_frame, - LocalFrame& opener_frame, - ExceptionState& exception_state) { + LocalFrame& opener_frame) { LocalFrame* active_frame = incumbent_window.GetFrame(); DCHECK(active_frame); - KURL completed_url = - url_string.IsEmpty() - ? KURL(g_empty_string) - : entered_window_frame.GetDocument()->CompleteURL(url_string); - if (!completed_url.IsEmpty() && !completed_url.IsValid()) { - UseCounter::Count(incumbent_window.document(), - WebFeature::kWindowOpenWithInvalidURL); - exception_state.ThrowDOMException( - DOMExceptionCode::kSyntaxError, - "Unable to open a window with invalid URL '" + - completed_url.GetString() + "'.\n"); - return nullptr; - } - if (completed_url.ProtocolIsJavaScript() && opener_frame.GetDocument()->GetContentSecurityPolicy() && !ContentSecurityPolicy::ShouldBypassMainWorld( @@ -398,11 +322,10 @@ } } - WebWindowFeatures window_features = - GetWindowFeaturesFromString(window_features_string); - FrameLoadRequest frame_request(incumbent_window.document(), ResourceRequest(completed_url), frame_name); + frame_request.SetNavigationPolicy( + NavigationPolicyForCreateWindow(window_features)); frame_request.SetShouldSetOpener(window_features.noopener ? kNeverSetOpener : kMaybeSetOpener); frame_request.SetFrameType( @@ -431,16 +354,15 @@ // different from the opener frame, and the name references a frame relative // to the opener frame. bool created; - Frame* new_frame = CreateWindowHelper( - opener_frame, *active_frame, opener_frame, frame_request, window_features, - false /* force_new_foreground_tab */, created); + Frame* new_frame = + CreateNewWindow(opener_frame, frame_request, window_features, created); if (!new_frame) return nullptr; if (new_frame->DomWindow()->IsInsecureScriptAccess(incumbent_window, completed_url)) return window_features.noopener ? nullptr : new_frame->DomWindow(); - if (created || !url_string.IsEmpty()) { + if (created || !completed_url.IsEmpty()) { FrameLoadRequest request(incumbent_window.document(), ResourceRequest(completed_url)); request.GetResourceRequest().SetHasUserGesture(has_user_gesture); @@ -469,9 +391,7 @@ WebWindowFeatures features; features.noopener = request.GetShouldSetOpener() == kNeverSetOpener; bool created; - Frame* new_frame = CreateWindowHelper( - opener_frame, opener_frame, opener_frame, request, features, - true /* force_new_foreground_tab */, created); + Frame* new_frame = CreateNewWindow(opener_frame, request, features, created); if (!new_frame) return; auto* new_local_frame = DynamicTo<LocalFrame>(new_frame);
diff --git a/third_party/blink/renderer/core/page/create_window.h b/third_party/blink/renderer/core/page/create_window.h index 5178d84..11e635ae4 100644 --- a/third_party/blink/renderer/core/page/create_window.h +++ b/third_party/blink/renderer/core/page/create_window.h
@@ -27,31 +27,25 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_CREATE_WINDOW_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_CREATE_WINDOW_H_ +#include "third_party/blink/public/web/web_window_features.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" -#include "third_party/blink/renderer/core/loader/frame_loader_types.h" -#include "third_party/blink/renderer/core/loader/navigation_policy.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" // To avoid conflicts with the CreateWindow macro from the Windows SDK... #undef CreateWindow namespace blink { -class ExceptionState; class LocalFrame; struct FrameLoadRequest; -struct WebWindowFeatures; -DOMWindow* CreateWindow(const String& url_string, +DOMWindow* CreateWindow(const KURL& completed_url, const AtomicString& frame_name, - const String& window_features_string, + const WebWindowFeatures&, LocalDOMWindow& incumbent_window, - LocalFrame& entered_window_frame, - LocalFrame& opener_frame, - ExceptionState&); + LocalFrame& opener_frame); void CreateWindowForRequest(const FrameLoadRequest&, LocalFrame& opener_frame); -// Exposed for testing CORE_EXPORT WebWindowFeatures GetWindowFeaturesFromString(const String&); } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc index 2e5aa2b..9d417eb 100644 --- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc +++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -36,15 +36,26 @@ if (!element.GetLayoutObject()) return false; - LayoutObject* layout_object = element.GetLayoutObject(); + DCHECK(element.GetLayoutObject()->IsBox()); + + LayoutBox* layout_box = ToLayoutBox(element.GetLayoutObject()); // TODO(bokan): Broken for OOPIF. crbug.com/642378. Document& top_document = element.GetDocument().TopDocument(); if (!top_document.GetLayoutView()) return false; - FloatQuad quad = layout_object->LocalToAbsoluteQuad( - FloatRect(ToLayoutBox(layout_object)->PhysicalPaddingBoxRect())); + // We need to be more strict for iframes and use the content box since the + // iframe will use the parent's layout size. Using the padding box would mean + // the content would relayout on promotion/demotion. The layout size matching + // the parent is done to ensure consistent semantics with respect to how the + // mobile URL bar affects layout, which isn't a concern for non-iframe + // elements because those semantics will already be applied to the element. + LayoutRect rect = layout_box->IsLayoutIFrame() + ? layout_box->PhysicalContentBoxRect() + : layout_box->PhysicalPaddingBoxRect(); + + FloatQuad quad = layout_box->LocalToAbsoluteQuad(FloatRect(rect)); if (!quad.IsRectilinear()) return false;
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc index af53b7f..1004a99d 100644 --- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -2555,6 +2555,91 @@ << "Once loaded, the iframe should be promoted."; } +// Ensure that we're using the content box for an iframe. Promotion will cause +// the content to use the layout size of the parent frame so having padding or +// a border would cause us to relayout. +TEST_F(ImplicitRootScrollerSimTest, IframeUsesContentBox) { + WebView().ResizeWithBrowserControls(IntSize(800, 600), 0, 0, false); + SimRequest main_request("https://example.com/test.html", "text/html"); + SimRequest child_request("https://example.com/child.html", "text/html"); + LoadURL("https://example.com/test.html"); + main_request.Complete(R"HTML( + <!DOCTYPE> + <style> + iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; + box-sizing: border-box; + + } + body, html { + margin: 0; + width: 100%; + height: 100%; + overflow:hidden; + } + + </style> + <iframe id="container" src="child.html"> + )HTML"); + child_request.Complete(R"HTML( + <!DOCTYPE html> + <style> + div { + border: 5px solid black; + background-color: red; + width: 99%; + height: 100px; + } + html { + height: 200%; + } + </style> + <div></div> + )HTML"); + + Compositor().BeginFrame(); + + Element* iframe = GetDocument().getElementById("container"); + + ASSERT_EQ(iframe, + GetDocument().GetRootScrollerController().EffectiveRootScroller()) + << "The iframe should start off promoted."; + + // Adding padding should cause the iframe to be demoted. + { + iframe->setAttribute(html_names::kStyleAttr, "padding-left: 20%"); + Compositor().BeginFrame(); + + EXPECT_NE(iframe, + GetDocument().GetRootScrollerController().EffectiveRootScroller()) + << "The iframe should be demoted once it has padding."; + } + + // Replacing padding with a border should also ensure the iframe remains + // demoted. + { + iframe->setAttribute(html_names::kStyleAttr, "border: 5px solid black"); + Compositor().BeginFrame(); + + EXPECT_NE(iframe, + GetDocument().GetRootScrollerController().EffectiveRootScroller()) + << "The iframe should be demoted once it has border."; + } + + // Removing the border should now cause the iframe to be promoted once again. + iframe->setAttribute(html_names::kStyleAttr, ""); + Compositor().BeginFrame(); + + ASSERT_EQ(iframe, + GetDocument().GetRootScrollerController().EffectiveRootScroller()) + << "The iframe should once again be promoted when border is removed"; +} + // Test that we don't promote any elements implicitly if the main document has // vertical scrolling. TEST_F(ImplicitRootScrollerSimTest, OverflowInMainDocumentRestrictsImplicit) {
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc index b2c0ffa..aa93dc0 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/renderer/core/editing/visible_units.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h" #include "third_party/blink/renderer/core/scroll/scroll_alignment.h" namespace blink { @@ -20,31 +21,32 @@ namespace { constexpr char kTextFragmentIdentifierPrefix[] = "targetText="; -constexpr size_t kTextFragmentIdentifierPrefixLength = +constexpr size_t kTextFragmentIdentifierPrefixArrayLength = base::size(kTextFragmentIdentifierPrefix); -bool ParseTargetTextIdentifier(const String& fragment, - String* start, - String* end) { - if (fragment.Find(kTextFragmentIdentifierPrefix) != 0) - return false; +bool ParseTargetTextIdentifier( + const String& fragment, + std::vector<TextFragmentSelector>* out_selectors) { + DCHECK(out_selectors); - size_t comma_pos = fragment.find(','); - size_t start_pos = kTextFragmentIdentifierPrefixLength - 1; + size_t start_pos = 0; + size_t end_pos = 0; + while (end_pos != kNotFound) { + if (fragment.Find(kTextFragmentIdentifierPrefix, start_pos) != start_pos) + return false; - if (comma_pos == kNotFound) { - *start = fragment.Substring(start_pos); - *end = ""; - } else { - *start = fragment.Substring(start_pos, comma_pos - start_pos); + // The prefix array length includes the \0 string terminator. + start_pos += kTextFragmentIdentifierPrefixArrayLength - 1; + end_pos = fragment.find('&', start_pos); - size_t second_start_pos = comma_pos + 1; - size_t end_pos = fragment.find('&', comma_pos); - - if (end_pos == kNotFound) - *end = fragment.Substring(second_start_pos); - else - *end = fragment.Substring(second_start_pos, end_pos - second_start_pos); + String target_text; + if (end_pos == kNotFound) { + target_text = fragment.Substring(start_pos); + } else { + target_text = fragment.Substring(start_pos, end_pos - start_pos); + start_pos = end_pos + 1; + } + out_selectors->push_back(TextFragmentSelector::Create(target_text)); } return true; @@ -54,33 +56,38 @@ TextFragmentAnchor* TextFragmentAnchor::TryCreate(const KURL& url, LocalFrame& frame) { - String start; - String end; + std::vector<TextFragmentSelector> selectors; - if (!ParseTargetTextIdentifier(url.FragmentIdentifier(), &start, &end)) + if (!ParseTargetTextIdentifier(url.FragmentIdentifier(), &selectors)) return nullptr; - String decoded_start = DecodeURLEscapeSequences(start, DecodeURLMode::kUTF8); - String decoded_end = DecodeURLEscapeSequences(end, DecodeURLMode::kUTF8); - - return MakeGarbageCollected<TextFragmentAnchor>(decoded_start, decoded_end, - frame); + return MakeGarbageCollected<TextFragmentAnchor>(selectors, frame); } -TextFragmentAnchor::TextFragmentAnchor(const String& start, - const String& end, - LocalFrame& frame) - : start_(start), end_(end), finder_(*this), frame_(&frame) { +TextFragmentAnchor::TextFragmentAnchor( + const std::vector<TextFragmentSelector>& text_fragment_selectors, + LocalFrame& frame) + : frame_(&frame) { + DCHECK(!text_fragment_selectors.empty()); DCHECK(frame_->View()); + + text_fragment_finders_.reserve(text_fragment_selectors.size()); + for (TextFragmentSelector selector : text_fragment_selectors) + text_fragment_finders_.emplace_back(*this, selector); } bool TextFragmentAnchor::Invoke() { if (search_finished_) return false; - // TODO(bokan): We need to add the capability to match a snippet based on - // it's start and end. https://crbug.com/924964. - finder_.FindMatch(start_, *frame_->GetDocument()); + frame_->GetDocument()->Markers().RemoveMarkersOfTypes( + DocumentMarker::MarkerTypes::TextMatch()); + + if (!user_scrolled_) + first_match_needs_scroll_ = true; + + for (auto& finder : text_fragment_finders_) + finder.FindMatch(*frame_->GetDocument()); // Scrolling into view from the call above might cause us to set // search_finished_ so recompute here. @@ -95,7 +102,7 @@ if (!IsExplicitScrollType(type)) return; - search_finished_ = true; + user_scrolled_ = true; } void TextFragmentAnchor::PerformPreRafActions() {} @@ -115,30 +122,28 @@ if (search_finished_) return; - Document& document = *frame_->GetDocument(); + if (first_match_needs_scroll_) { + first_match_needs_scroll_ = false; - document.Markers().RemoveMarkersOfTypes( - DocumentMarker::MarkerTypes::TextMatch()); + LayoutRect bounding_box = LayoutRect(ComputeTextRect(range)); - LayoutRect bounding_box = LayoutRect(ComputeTextRect(range)); + DCHECK(range.Nodes().begin() != range.Nodes().end()); - DCHECK(range.Nodes().begin() != range.Nodes().end()); + Node& node = *range.Nodes().begin(); - Node& node = *range.Nodes().begin(); + DCHECK(node.GetLayoutObject()); - DCHECK(node.GetLayoutObject()); - - node.GetLayoutObject()->ScrollRectToVisible( - bounding_box, - WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded, - ScrollAlignment::kAlignCenterIfNeeded, - kProgrammaticScroll)); - + node.GetLayoutObject()->ScrollRectToVisible( + bounding_box, + WebScrollIntoViewParams(ScrollAlignment::kAlignCenterIfNeeded, + ScrollAlignment::kAlignCenterIfNeeded, + kProgrammaticScroll)); + } EphemeralRange dom_range = EphemeralRange(ToPositionInDOMTree(range.StartPosition()), ToPositionInDOMTree(range.EndPosition())); - document.Markers().AddTextMatchMarker(dom_range, - TextMatchMarker::MatchStatus::kActive); + frame_->GetDocument()->Markers().AddTextMatchMarker( + dom_range, TextMatchMarker::MatchStatus::kActive); frame_->GetEditor().SetMarkedTextMatchesAreHighlighted(true); }
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h index e08bc4b..aab1d90 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
@@ -19,11 +19,13 @@ class KURL; class CORE_EXPORT TextFragmentAnchor final : public FragmentAnchor, - TextFragmentFinder::Client { + public TextFragmentFinder::Client { public: static TextFragmentAnchor* TryCreate(const KURL& url, LocalFrame& frame); - TextFragmentAnchor(const String& start, const String& end, LocalFrame& frame); + TextFragmentAnchor( + const std::vector<TextFragmentSelector>& text_fragment_selectors, + LocalFrame& frame); ~TextFragmentAnchor() override = default; bool Invoke() override; @@ -42,14 +44,13 @@ void DidFindMatch(const EphemeralRangeInFlatTree& range) override; private: - const String start_; - const String end_; - - TextFragmentFinder finder_; + std::vector<TextFragmentFinder> text_fragment_finders_; Member<LocalFrame> frame_; bool search_finished_ = false; + bool user_scrolled_ = false; + bool first_match_needs_scroll_ = true; DISALLOW_COPY_AND_ASSIGN(TextFragmentAnchor); };
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc index 83d4609..3ca3d08 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
@@ -138,6 +138,7 @@ Compositor().BeginFrame(); EXPECT_FALSE(GetDocument().View()->GetFragmentAnchor()); + EXPECT_TRUE(GetDocument().Markers().Markers().IsEmpty()); } // If the targetText=... string matches an id, we should scroll using id @@ -244,6 +245,150 @@ << LayoutViewport()->GetScrollOffset().ToString(); } +// Ensure multiple targetTexts are highlighted and the first is scrolled into +// view. +TEST_F(TextFragmentAnchorTest, MultipleTextFragments) { + SimRequest request( + "https://example.com/test.html#targetText=test&targetText=more", + "text/html"); + LoadURL("https://example.com/test.html#targetText=test&targetText=more"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + #first { + position: absolute; + top: 1000px; + } + #second { + position: absolute; + top: 2000px; + } + </style> + <p id="first">This is a test page</p> + <p id="second">This is some more text</p> + )HTML"); + Compositor().BeginFrame(); + + RunAsyncMatchingTasks(); + + Element& first = *GetDocument().getElementById("first"); + + EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(first))) + << "First <p> wasn't scrolled into view, viewport's scroll offset: " + << LayoutViewport()->GetScrollOffset().ToString(); + + EXPECT_EQ(2u, GetDocument().Markers().Markers().size()); +} + +// Ensure we scroll the second targetText into view if the first isn't found. +TEST_F(TextFragmentAnchorTest, FirstTextFragmentNotFound) { + SimRequest request( + "https://example.com/test.html#targetText=test&targetText=more", + "text/html"); + LoadURL("https://example.com/test.html#targetText=test&targetText=more"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + #first { + position: absolute; + top: 1000px; + } + #second { + position: absolute; + top: 2000px; + } + </style> + <p id="first">This is a page</p> + <p id="second">This is some more text</p> + )HTML"); + Compositor().BeginFrame(); + + RunAsyncMatchingTasks(); + + Element& second = *GetDocument().getElementById("second"); + + EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(second))) + << "Second <p> wasn't scrolled into view, viewport's scroll offset: " + << LayoutViewport()->GetScrollOffset().ToString(); + + EXPECT_EQ(1u, GetDocument().Markers().Markers().size()); +} + +// Ensure we still scroll the first targetText into view if the second isn't +// found. +TEST_F(TextFragmentAnchorTest, OnlyFirstTextFragmentFound) { + SimRequest request( + "https://example.com/test.html#targetText=test&targetText=more", + "text/html"); + LoadURL("https://example.com/test.html#targetText=test&targetText=more"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + p { + position: absolute; + top: 1000px; + } + </style> + <p id="text">This is a test page</p> + )HTML"); + Compositor().BeginFrame(); + + RunAsyncMatchingTasks(); + + Element& p = *GetDocument().getElementById("text"); + + EXPECT_TRUE(ViewportRect().Contains(BoundingRectInFrame(p))) + << "<p> Element wasn't scrolled into view, viewport's scroll offset: " + << LayoutViewport()->GetScrollOffset().ToString(); + + EXPECT_EQ(1u, GetDocument().Markers().Markers().size()); +} + +// Make sure multiple non-matching strings doesn't cause scroll and the fragment +// is removed when completed. +TEST_F(TextFragmentAnchorTest, MultipleNonMatchingStrings) { + SimRequest request( + "https://example.com/" + "test.html#targetText=unicorn&targetText=cookie&targetText=cat", + "text/html"); + LoadURL( + "https://example.com/" + "test.html#targetText=unicorn&targetText=cookie&targetText=cat"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + body { + height: 1200px; + } + p { + position: absolute; + top: 1000px; + } + </style> + <p id="text">This is a test page</p> + )HTML"); + Compositor().BeginFrame(); + RunAsyncMatchingTasks(); + + EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset()); + + // Force a layout + GetDocument().body()->setAttribute(html_names::kStyleAttr, "height: 1300px"); + Compositor().BeginFrame(); + + EXPECT_FALSE(GetDocument().View()->GetFragmentAnchor()); + EXPECT_TRUE(GetDocument().Markers().Markers().IsEmpty()); +} + } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc index 48b6136..a6bf09d 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
@@ -12,14 +12,15 @@ #include "third_party/blink/renderer/core/editing/ephemeral_range.h" #include "third_party/blink/renderer/core/editing/finder/find_buffer.h" #include "third_party/blink/renderer/core/editing/position.h" +#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h" namespace blink { -TextFragmentFinder::TextFragmentFinder(Client& client) : client_(client) {} +TextFragmentFinder::TextFragmentFinder(Client& client, + const TextFragmentSelector& selector) + : client_(client), selector_(selector) {} -void TextFragmentFinder::FindMatch(const String& search_text, - Document& document) { - search_text_ = search_text; +void TextFragmentFinder::FindMatch(Document& document) { PositionInFlatTree search_start = PositionInFlatTree::FirstPositionInNode(document); @@ -36,8 +37,10 @@ // Find in the whole block. FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end)); const FindOptions find_options = kCaseInsensitive; + // TODO(bokan): We need to add the capability to match a snippet based on + // it's start and end. https://crbug.com/924964. std::unique_ptr<FindBuffer::Results> match_results = - buffer.FindMatches(search_text_, find_options); + buffer.FindMatches(selector_.Start(), find_options); if (!match_results->IsEmpty()) { FindBuffer::BufferMatchResult match = match_results->front();
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h index 1c180b3..34db3f7b 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/editing/forward.h" +#include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { @@ -24,18 +25,17 @@ }; // Client must outlive the finder. - TextFragmentFinder(Client& client); + TextFragmentFinder(Client& client, const TextFragmentSelector& selector); ~TextFragmentFinder() = default; - // Begins searching for the given string in the given top-level document. - void FindMatch(const String& search_text, Document& document); + // Begins searching in the given top-level document. + void FindMatch(Document& document); private: Client& client_; + const TextFragmentSelector selector_; String search_text_; - - DISALLOW_COPY_AND_ASSIGN(TextFragmentFinder); }; } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc new file mode 100644 index 0000000..b4ac4b79 --- /dev/null +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.cc
@@ -0,0 +1,37 @@ +// 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 "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" + +namespace blink { + +TextFragmentSelector TextFragmentSelector::Create(const String& target_text) { + SelectorType type; + String start; + String end; + + size_t comma_pos = target_text.find(','); + + if (comma_pos == kNotFound) { + type = kExact; + start = target_text; + end = ""; + } else { + type = kRange; + start = target_text.Substring(0, comma_pos); + end = target_text.Substring(comma_pos + 1); + } + + return TextFragmentSelector( + type, DecodeURLEscapeSequences(start, DecodeURLMode::kUTF8), + DecodeURLEscapeSequences(end, DecodeURLMode::kUTF8)); +} + +TextFragmentSelector::TextFragmentSelector(SelectorType type, + String start, + String end) + : type_(type), start_(start), end_(end) {} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h new file mode 100644 index 0000000..a10d0cf9 --- /dev/null +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h
@@ -0,0 +1,41 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +// TextFragmentSelector represents a single targetText=... selector of a +// TextFragmentAnchor, parsed into its components. +class CORE_EXPORT TextFragmentSelector final { + public: + static TextFragmentSelector Create(const String& target_text); + + enum SelectorType { + // An exact selector on the string start_. + kExact, + // A range selector on a text range start_ to end_. + kRange, + }; + + TextFragmentSelector(SelectorType type, String start, String end); + ~TextFragmentSelector() = default; + + SelectorType Type() const { return type_; } + String Start() const { return start_; } + String End() const { return end_; } + + private: + const SelectorType type_; + String start_; + String end_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_SCROLLING_TEXT_FRAGMENT_SELECTOR_H_
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h index 3de80a3..ff1f7e48 100644 --- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h +++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
@@ -75,21 +75,18 @@ void DidResizeViewport(); - // Returns the ScrollableArea associated with the globalRootScroller(). Note, - // this isn't necessarily the PLSA belonging to the root scroller Node's - // LayoutBox. If the root scroller is the documentElement then we use the - // LocalFrameView (or LayoutView if root-layer-scrolls). + // Returns the ScrollableArea associated with the globalRootScroller(). ScrollableArea* RootScrollerArea() const; - // Returns the size we should use for the root scroller, accounting for top - // controls adjustment and using the root LocalFrameView. + // Returns the size we should use for the root scroller, accounting for + // browser controls adjustment and using the root LocalFrameView. IntSize RootScrollerVisibleArea() const; private: - // Calculates the Node that should be the globalRootScroller. On a simple - // page, this will simply the root frame's effectiveRootScroller but if the - // root scroller is set to an iframe, this will then descend into the iframe - // to find its effective root scroller. + // Calculates the Node that should be the global root scroller. On a simple + // page, this will be the root frame's effective root scroller. If the + // effective root scroller is an iframe, this will then recursively descend + // into the iframe to find its effective root scroller. Node* FindGlobalRootScroller(); // Should be called to set a new node as the global root scroller and that @@ -97,19 +94,19 @@ void UpdateGlobalRootScroller(Node* new_global_root_scroller); // Updates the is_global_root_scroller bits in all the necessary places when - // the global root scsroller changes. + // the global root scroller changes. void UpdateCachedBits(Node* old_global, Node* new_global); Document* TopDocument() const; // The apply-scroll callback that moves browser controls and produces // overscroll effects. This class makes sure this callback is set on the - // appropriate root scroller element. + // global root scroller element. Member<ViewportScrollCallback> viewport_apply_scroll_; // The page level root scroller. i.e. The actual node for which scrolling // should move browser controls and produce overscroll glow. Once an - // m_viewportApplyScroll has been created, it will always be set on this + // |viewport_apply_scroll_| has been created, it will always be set on this // Node. WeakMember<Node> global_root_scroller_;
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc index 8b03dfa..f8afeac 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.cc +++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -64,6 +64,11 @@ if (!ShouldReportElement(frame, attr, intersection_rect)) return; + const Node* node = layout_object->GetNode(); + DCHECK(node); + DCHECK(node->IsElementNode()); + const AtomicString& id = ToElement(node)->GetIdAttribute(); + DCHECK(GetSupplementable()->document() == &layout_object->GetDocument()); DCHECK(layout_object->GetDocument().GetSecurityOrigin()); if (!Performance::PassesTimingAllowCheck( @@ -78,7 +83,8 @@ // Create an entry with a |startTime| of 0. performance->AddElementTiming( AtomicString(cached_image->Url().GetString()), intersection_rect, - TimeTicks(), cached_image->LoadResponseEnd(), attr); + TimeTicks(), cached_image->LoadResponseEnd(), attr, + cached_image->IntrinsicSize(kDoNotRespectImageOrientation), id); } return; } @@ -88,9 +94,10 @@ if (!layerTreeView) return; - element_timings_.emplace_back(AtomicString(cached_image->Url().GetString()), - intersection_rect, - cached_image->LoadResponseEnd(), attr); + element_timings_.emplace_back( + AtomicString(cached_image->Url().GetString()), intersection_rect, + cached_image->LoadResponseEnd(), attr, + cached_image->IntrinsicSize(kDoNotRespectImageOrientation), id); // Only queue a swap promise when |element_timings_| was empty. All of the // records in |element_timings_| will be processed when the promise succeeds // or fails, and at that time the vector is cleared. @@ -140,9 +147,10 @@ if (performance && (performance->HasObserverFor(PerformanceEntry::kElement) || performance->ShouldBufferEntries())) { for (const auto& element_timing : element_timings_) { - performance->AddElementTiming(element_timing.name, element_timing.rect, - timestamp, element_timing.response_end, - element_timing.identifier); + performance->AddElementTiming( + element_timing.name, element_timing.rect, timestamp, + element_timing.response_end, element_timing.identifier, + element_timing.intrinsic_size, element_timing.id); } } element_timings_.clear();
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.h b/third_party/blink/renderer/core/paint/image_element_timing.h index 3b710af..b17e5cd 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.h +++ b/third_party/blink/renderer/core/paint/image_element_timing.h
@@ -67,16 +67,22 @@ ElementTimingInfo(const AtomicString& name, const FloatRect& rect, const TimeTicks& response_end, - const AtomicString& identifier) + const AtomicString& identifier, + const IntSize& intrinsic_size, + const AtomicString& id) : name(name), rect(rect), response_end(response_end), - identifier(identifier) {} + identifier(identifier), + intrinsic_size(intrinsic_size), + id(id) {} AtomicString name; FloatRect rect; TimeTicks response_end; AtomicString identifier; + IntSize intrinsic_size; + AtomicString id; }; // Vector containing the element timing infos that will be reported during the // next swap promise callback.
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc index fcb790e9..86bfd85 100644 --- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc +++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -1070,10 +1070,9 @@ // Being the global root scroller will affect clipping size due to browser // controls behavior so we need to update compositing based on updated clip // geometry. - if (GetLayoutBox()->GetNode()->IsElementNode()) { + if (GetLayoutBox()->GetNode()->IsElementNode()) ToElement(GetLayoutBox()->GetNode())->SetNeedsCompositingUpdate(); - GetLayoutBox()->SetNeedsPaintPropertyUpdate(); - } + GetLayoutBox()->SetNeedsPaintPropertyUpdate(); // On Android, where the VisualViewport supplies scrollbars, we need to // remove the PLSA's scrollbars if we become the global root scroller.
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 214cfac..24b014cd 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
@@ -1760,6 +1760,8 @@ state.user_scrollable_vertical = scrollable_area->UserInputScrollable(kVerticalScrollbar); + state.scrolls_outer_viewport = box.IsGlobalRootScroller(); + auto ancestor_reasons = context_.current.scroll->GetMainThreadScrollingReasons(); state.main_thread_scrolling_reasons =
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc index d1fcd5c..171fb01 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -6596,4 +6596,63 @@ EXPECT_FALSE(text->FirstFragment().PaintProperties()); } +TEST_P(PaintPropertyTreeBuilderTest, SetViewportScrollingBits) { + SetBodyInnerHTML(R"HTML( + <style> + body, html { + margin: 0; + width: 100%; + height: 100%; + } + #scroller { + width: 100%; + height: 200%; + overflow: auto; + } + </style> + <div id="scroller"> + <div style="height: 3000px"></div> + </div> + )HTML"); + + const auto* scroller_node = PaintPropertiesForElement("scroller")->Scroll(); + const auto* document_node = DocScroll(); + + // Ensure the LayoutView's ScrollNode is marked as scrolling the "outer" or + // "layout" viewport. + { + EXPECT_FALSE(scroller_node->ScrollsOuterViewport()); + EXPECT_TRUE(document_node->ScrollsOuterViewport()); + } + + // Ensure the visual viewport is the only one that sets the inner scroll bit. + { + EXPECT_TRUE(GetDocument() + .GetPage() + ->GetVisualViewport() + .GetScrollNode() + ->ScrollsInnerViewport()); + EXPECT_FALSE(scroller_node->ScrollsInnerViewport()); + EXPECT_FALSE(document_node->ScrollsInnerViewport()); + } + + // Make the scroller fill the viewport. This will make it eligible for root + // scroller promotion. Ensure the outer viewport scrolling property is + // correctly recomputed, moving it from the LayoutView to the scroller. + { + Element* scroller = GetDocument().getElementById("scroller"); + scroller->setAttribute(html_names::kStyleAttr, "height: 100%"); + LocalFrameView* frame_view = GetDocument().View(); + frame_view->UpdateAllLifecyclePhases( + DocumentLifecycle::LifecycleUpdateReason::kTest); + ASSERT_TRUE(scroller->GetLayoutObject()->IsGlobalRootScroller()); + + EXPECT_TRUE(scroller_node->ScrollsOuterViewport()); + + // Since the document is no longer scrollable and isn't the root scroller + // it shouldn't have a node. + EXPECT_FALSE(DocScroll()); + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc index dfb74b0..68e55a36 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.cc +++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -15,9 +15,15 @@ const FloatRect& intersection_rect, DOMHighResTimeStamp start_time, DOMHighResTimeStamp response_end, - const AtomicString& identifier) { + const AtomicString& identifier, + int naturalWidth, + int naturalHeight, + const AtomicString& id) { + DCHECK_GT(naturalWidth, 0); + DCHECK_GT(naturalHeight, 0); return MakeGarbageCollected<PerformanceElementTiming>( - name, intersection_rect, start_time, response_end, identifier); + name, intersection_rect, start_time, response_end, identifier, + naturalWidth, naturalHeight, id); } PerformanceElementTiming::PerformanceElementTiming( @@ -25,11 +31,17 @@ const FloatRect& intersection_rect, DOMHighResTimeStamp start_time, DOMHighResTimeStamp response_end, - const AtomicString& identifier) + const AtomicString& identifier, + int naturalWidth, + int naturalHeight, + const AtomicString& id) : PerformanceEntry(name, start_time, start_time), intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)), response_end_(response_end), - identifier_(identifier) {} + identifier_(identifier), + naturalWidth_(naturalWidth), + naturalHeight_(naturalHeight), + id_(id) {} PerformanceElementTiming::~PerformanceElementTiming() = default;
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h index 190fc03..6bb3f24 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.h +++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -24,13 +24,19 @@ const FloatRect& intersection_rect, DOMHighResTimeStamp start_time, DOMHighResTimeStamp response_end, - const AtomicString& identifier); + const AtomicString& identifier, + int naturalWidth, + int naturalHeight, + const AtomicString& id); PerformanceElementTiming(const AtomicString& name, const FloatRect& intersection_rect, DOMHighResTimeStamp start_time, DOMHighResTimeStamp response_end, - const AtomicString& identifier); + const AtomicString& identifier, + int naturalWidth, + int naturalHeight, + const AtomicString& id); ~PerformanceElementTiming() override; AtomicString entryType() const override; @@ -42,6 +48,12 @@ AtomicString identifier() const { return identifier_; } + unsigned naturalWidth() const { return naturalWidth_; } + + unsigned naturalHeight() const { return naturalHeight_; } + + AtomicString id() const { return id_; } + void Trace(blink::Visitor*) override; private: @@ -50,6 +62,9 @@ Member<DOMRectReadOnly> intersection_rect_; DOMHighResTimeStamp response_end_; AtomicString identifier_; + unsigned naturalWidth_; + unsigned naturalHeight_; + AtomicString id_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.idl b/third_party/blink/renderer/core/timing/performance_element_timing.idl index 3796be6..99eb393 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.idl +++ b/third_party/blink/renderer/core/timing/performance_element_timing.idl
@@ -8,6 +8,9 @@ readonly attribute DOMRectReadOnly intersectionRect; readonly attribute DOMHighResTimeStamp responseEnd; readonly attribute DOMString identifier; + readonly attribute unsigned long naturalWidth; + readonly attribute unsigned long naturalHeight; + readonly attribute DOMString id; // TODO(peria): toJSON is not in spec. https://crbug.com/736332 [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
diff --git a/third_party/blink/renderer/core/timing/performance_observer.idl b/third_party/blink/renderer/core/timing/performance_observer.idl index 23e069d..c4348fb 100644 --- a/third_party/blink/renderer/core/timing/performance_observer.idl +++ b/third_party/blink/renderer/core/timing/performance_observer.idl
@@ -12,7 +12,7 @@ ConstructorCallWith=ScriptState, Exposed=(Window,Worker) ] interface PerformanceObserver { - [RaisesException] void observe(PerformanceObserverInit options); + [RaisesException] void observe(optional PerformanceObserverInit options); void disconnect(); PerformanceEntryList takeRecords(); [CallWith=ScriptState] static readonly attribute FrozenArray<DOMString> supportedEntryTypes;
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc index 1b9a1ec..9e4bf25 100644 --- a/third_party/blink/renderer/core/timing/window_performance.cc +++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -400,11 +400,14 @@ const FloatRect& rect, TimeTicks start_time, TimeTicks response_end, - const AtomicString& identifier) { + const AtomicString& identifier, + const IntSize& intrinsic_size, + const AtomicString& id) { DCHECK(origin_trials::ElementTimingEnabled(GetExecutionContext())); PerformanceElementTiming* entry = PerformanceElementTiming::Create( name, rect, MonotonicTimeToDOMHighResTimeStamp(start_time), - MonotonicTimeToDOMHighResTimeStamp(response_end), identifier); + MonotonicTimeToDOMHighResTimeStamp(response_end), identifier, + intrinsic_size.Width(), intrinsic_size.Height(), id); if (HasObserverFor(PerformanceEntry::kElement)) { UseCounter::Count(GetFrame()->GetDocument(), WebFeature::kElementTimingExplicitlyRequested);
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h index 8ac6b2c6..1f0dec8 100644 --- a/third_party/blink/renderer/core/timing/window_performance.h +++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -84,7 +84,9 @@ const FloatRect& rect, TimeTicks start_time, TimeTicks response_end, - const AtomicString& identifier); + const AtomicString& identifier, + const IntSize& intrinsic_size, + const AtomicString& id); void AddLayoutJankFraction(double jank_fraction);
diff --git a/third_party/blink/renderer/core/workers/worker_backing_thread.cc b/third_party/blink/renderer/core/workers/worker_backing_thread.cc index 7952313..3ca32526 100644 --- a/third_party/blink/renderer/core/workers/worker_backing_thread.cc +++ b/third_party/blink/renderer/core/workers/worker_backing_thread.cc
@@ -76,9 +76,7 @@ V8Initializer::InitializeWorker(isolate_); ThreadState::Current()->RegisterTraceDOMWrappers( - isolate_, V8GCController::TraceDOMWrappers, - ScriptWrappableMarkingVisitor::InvalidateDeadObjectsInMarkingDeque, - ScriptWrappableMarkingVisitor::PerformCleanup); + isolate_, V8GCController::TraceDOMWrappers); if (RuntimeEnabledFeatures::V8IdleTasksEnabled()) { V8PerIsolateData::EnableIdleTasks( isolate_, std::make_unique<V8IdleTaskRunner>(scheduler));
diff --git a/third_party/blink/renderer/devtools/front_end/application_test_runner/ResourcesTestRunner.js b/third_party/blink/renderer/devtools/front_end/application_test_runner/ResourcesTestRunner.js index 28fe125..3945403 100644 --- a/third_party/blink/renderer/devtools/front_end/application_test_runner/ResourcesTestRunner.js +++ b/third_party/blink/renderer/devtools/front_end/application_test_runner/ResourcesTestRunner.js
@@ -12,10 +12,12 @@ * doesn't get reset between tests. */ ApplicationTestRunner.resetState = async function() { - const securityOrigin = new Common.ParsedURL(TestRunner.url()).securityOrigin(); - const storageTypes = - ['appcache', 'cache_storage', 'cookies', 'indexeddb', 'local_storage', 'service_workers', 'websql']; - await TestRunner.mainTarget.storageAgent().clearDataForOrigin(securityOrigin, storageTypes.join(',')); + const targets = SDK.targetManager.targets(); + for (const target of targets) { + const securityOrigin = new Common.ParsedURL(target.inspectedURL()).securityOrigin(); + await target.storageAgent().clearDataForOrigin( + securityOrigin, Resources.ClearStorageView.AllStorageTypes.join(',')); + } }; ApplicationTestRunner.createWebSQLDatabase = function(name) {
diff --git a/third_party/blink/renderer/devtools/front_end/security/SecurityModel.js b/third_party/blink/renderer/devtools/front_end/security/SecurityModel.js index 1d1648b5..d380a9c 100644 --- a/third_party/blink/renderer/devtools/front_end/security/SecurityModel.js +++ b/third_party/blink/renderer/devtools/front_end/security/SecurityModel.js
@@ -75,14 +75,12 @@ * @param {!Protocol.Security.SecurityState} securityState * @param {boolean} schemeIsCryptographic * @param {!Array<!Protocol.Security.SecurityStateExplanation>} explanations - * @param {?Protocol.Security.InsecureContentStatus} insecureContentStatus * @param {?string} summary */ - constructor(securityState, schemeIsCryptographic, explanations, insecureContentStatus, summary) { + constructor(securityState, schemeIsCryptographic, explanations, summary) { this.securityState = securityState; this.schemeIsCryptographic = schemeIsCryptographic; this.explanations = explanations; - this.insecureContentStatus = insecureContentStatus; this.summary = summary; } }; @@ -105,8 +103,8 @@ * @param {?string=} summary */ securityStateChanged(securityState, schemeIsCryptographic, explanations, insecureContentStatus, summary) { - const pageSecurityState = new Security.PageSecurityState( - securityState, schemeIsCryptographic, explanations, insecureContentStatus, summary || null); + const pageSecurityState = + new Security.PageSecurityState(securityState, schemeIsCryptographic, explanations, summary || null); this._model.dispatchEventToListeners(Security.SecurityModel.Events.SecurityStateChanged, pageSecurityState); }
diff --git a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js index 286231b..5512cc3a 100644 --- a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js +++ b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
@@ -94,13 +94,11 @@ * @param {!Protocol.Security.SecurityState} newSecurityState * @param {boolean} schemeIsCryptographic * @param {!Array<!Protocol.Security.SecurityStateExplanation>} explanations - * @param {?Protocol.Security.InsecureContentStatus} insecureContentStatus * @param {?string} summary */ - _updateSecurityState(newSecurityState, schemeIsCryptographic, explanations, insecureContentStatus, summary) { + _updateSecurityState(newSecurityState, schemeIsCryptographic, explanations, summary) { this._sidebarMainViewElement.setSecurityState(newSecurityState); - this._mainView.updateSecurityState( - newSecurityState, schemeIsCryptographic, explanations, insecureContentStatus, summary); + this._mainView.updateSecurityState(newSecurityState, schemeIsCryptographic, explanations, summary); } /** @@ -111,9 +109,8 @@ const securityState = /** @type {!Protocol.Security.SecurityState} */ (data.securityState); const schemeIsCryptographic = /** @type {boolean} */ (data.schemeIsCryptographic); const explanations = /** @type {!Array<!Protocol.Security.SecurityStateExplanation>} */ (data.explanations); - const insecureContentStatus = /** @type {?Protocol.Security.InsecureContentStatus} */ (data.insecureContentStatus); const summary = /** @type {?string} */ (data.summary); - this._updateSecurityState(securityState, schemeIsCryptographic, explanations, insecureContentStatus, summary); + this._updateSecurityState(securityState, schemeIsCryptographic, explanations, summary); } selectAndSwitchToMainView() { @@ -645,10 +642,9 @@ * @param {!Protocol.Security.SecurityState} newSecurityState * @param {boolean} schemeIsCryptographic * @param {!Array<!Protocol.Security.SecurityStateExplanation>} explanations - * @param {?Protocol.Security.InsecureContentStatus} insecureContentStatus * @param {?string} summary */ - updateSecurityState(newSecurityState, schemeIsCryptographic, explanations, insecureContentStatus, summary) { + updateSecurityState(newSecurityState, schemeIsCryptographic, explanations, summary) { // Remove old state. // It's safe to call this even when this._securityState is undefined. this._summarySection.classList.remove('security-summary-' + this._securityState); @@ -666,7 +662,7 @@ // Use override summary if present, otherwise use base explanation this._summaryText.textContent = summary || summaryExplanationStrings[this._securityState]; - this._explanations = explanations, this._insecureContentStatus = insecureContentStatus; + this._explanations = explanations; this._schemeIsCryptographic = schemeIsCryptographic; this.refreshExplanations();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc index 153d5256..5a123db 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_position.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -12,6 +12,7 @@ #include "third_party/blink/renderer/core/editing/ephemeral_range.h" #include "third_party/blink/renderer/core/editing/iterators/character_iterator.h" #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h" +#include "third_party/blink/renderer/core/editing/iterators/text_iterator_behavior.h" #include "third_party/blink/renderer/core/editing/position.h" #include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h" @@ -207,9 +208,12 @@ // character offset. // TODO(nektar): Use LayoutNG offset mapping instead of // |TextIterator|. + TextIteratorBehavior::Builder iterator_builder; + const TextIteratorBehavior text_iterator_behavior = + iterator_builder.SetDoesNotEmitSpaceBeyondRangeEnd(true).Build(); const auto first_position = Position::FirstPositionInNode(*container_node); - int offset = - TextIterator::RangeLength(first_position, parent_anchored_position); + int offset = TextIterator::RangeLength( + first_position, parent_anchored_position, text_iterator_behavior); ax_position.text_offset_or_child_index_ = offset; ax_position.affinity_ = affinity; DCHECK(ax_position.IsValid()); @@ -365,11 +369,15 @@ } // TODO(nektar): Use LayoutNG offset mapping instead of |TextIterator|. + TextIteratorBehavior::Builder iterator_builder; + const TextIteratorBehavior text_iterator_behavior = + iterator_builder.SetDoesNotEmitSpaceBeyondRangeEnd(true).Build(); const auto first_position = Position::FirstPositionInNode(*container_object_->GetNode()); const auto last_position = Position::LastPositionInNode(*container_object_->GetNode()); - return TextIterator::RangeLength(first_position, last_position); + return TextIterator::RangeLength(first_position, last_position, + text_iterator_behavior); } TextAffinity AXPosition::Affinity() const { @@ -731,9 +739,13 @@ } // TODO(nektar): Use LayoutNG offset mapping instead of |TextIterator|. + TextIteratorBehavior::Builder iterator_builder; + const TextIteratorBehavior text_iterator_behavior = + iterator_builder.SetDoesNotEmitSpaceBeyondRangeEnd(true).Build(); const auto first_position = Position::FirstPositionInNode(*container_node); const auto last_position = Position::LastPositionInNode(*container_node); - CharacterIterator character_iterator(first_position, last_position); + CharacterIterator character_iterator(first_position, last_position, + text_iterator_behavior); const EphemeralRange range = character_iterator.CalculateCharacterSubrange( 0, adjusted_position.text_offset_or_child_index_); return PositionWithAffinity(range.EndPosition(), affinity_);
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.cc b/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.cc index b088fc6..d6d5842c 100644 --- a/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.cc +++ b/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.cc
@@ -125,6 +125,10 @@ return HasDocumentDefinition(); } +int CSSPaintImageGeneratorImpl::WorkletId() const { + return paint_worklet_->WorkletId(); +} + void CSSPaintImageGeneratorImpl::Trace(blink::Visitor* visitor) { visitor->Trace(observer_); visitor->Trace(paint_worklet_);
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.h b/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.h index 6fa7f01c..a6d8658c 100644 --- a/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.h +++ b/third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.h
@@ -41,6 +41,7 @@ bool HasAlpha() const final; const Vector<CSSSyntaxDescriptor>& InputArgumentTypes() const final; bool IsImageGeneratorReady() const final; + int WorkletId() const final; // Should be called from the PaintWorkletGlobalScope when a javascript class // is registered with the same name.
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc index 2c36e3db..5f42eec8 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/modules/csspaint/paint_worklet.h" +#include "base/atomic_sequence_num.h" #include "base/rand_util.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -17,6 +18,15 @@ namespace blink { +namespace { +base::AtomicSequenceNumber g_next_worklet_id; +int NextId() { + // Start id from 1. This way it safe to use it as key in hashmap with default + // key traits. + return g_next_worklet_id.GetNext() + 1; +} +} // namespace + const wtf_size_t PaintWorklet::kNumGlobalScopes = 2u; const size_t kMaxPaintCountToSwitch = 30u; DocumentPaintDefinition* const kInvalidDocumentPaintDefinition = nullptr; @@ -41,7 +51,8 @@ : Worklet(frame->GetDocument()), Supplement<LocalDOMWindow>(*frame->DomWindow()), pending_generator_registry_( - MakeGarbageCollected<PaintWorkletPendingGeneratorRegistry>()) {} + MakeGarbageCollected<PaintWorkletPendingGeneratorRegistry>()), + worklet_id_(NextId()) {} PaintWorklet::~PaintWorklet() = default; @@ -135,8 +146,8 @@ pending_generator_registry_, GetNumberOfGlobalScopes() + 1); } - PaintWorkletProxyClient* proxy_client = - PaintWorkletProxyClient::Create(To<Document>(GetExecutionContext())); + PaintWorkletProxyClient* proxy_client = PaintWorkletProxyClient::Create( + To<Document>(GetExecutionContext()), worklet_id_); WorkerClients* worker_clients = WorkerClients::Create(); ProvidePaintWorkletProxyClientTo(worker_clients, proxy_client);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h index 6ff9115..c0d771f2 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -48,6 +48,7 @@ DocumentDefinitionMap& GetDocumentDefinitionMap() { return document_definition_map_; } + int WorkletId() const { return worklet_id_; } void Trace(blink::Visitor*) override; protected: @@ -81,6 +82,12 @@ // scope. SelectGlobalScope resets this at the beginning of each frame. int paints_before_switching_global_scope_; + // An atomic sequence number to ensure that it is unique for each paint + // worklet. This id is integrated in the PaintWorkletInput which will be used + // in PaintWorkletPaintDispatcher::Paint, to identify the right painter, to + // paint the image. + int worklet_id_; + DISALLOW_COPY_AND_ASSIGN(PaintWorklet); };
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc index cac79df..1bb542b 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
@@ -20,22 +20,6 @@ #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" namespace blink { -namespace { - -class MockPaintWorkletProxyClient : public PaintWorkletProxyClient { - public: - MockPaintWorkletProxyClient() - : PaintWorkletProxyClient(nullptr), did_set_global_scope_(false) {} - void SetGlobalScope(WorkletGlobalScope*) override { - did_set_global_scope_ = true; - } - bool did_set_global_scope() { return did_set_global_scope_; } - - private: - bool did_set_global_scope_; -}; - -} // namespace // TODO(smcgruer): Extract a common base class between this and // AnimationWorkletGlobalScope.
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc index 1614a76..0a1ac7c 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -19,19 +19,22 @@ "PaintWorkletProxyClient"; // static -PaintWorkletProxyClient* PaintWorkletProxyClient::Create(Document* document) { +PaintWorkletProxyClient* PaintWorkletProxyClient::Create(Document* document, + int worklet_id) { WebLocalFrameImpl* local_frame = WebLocalFrameImpl::FromFrame(document->GetFrame()); scoped_refptr<PaintWorkletPaintDispatcher> compositor_painter_dispatcher = local_frame->LocalRootFrameWidget()->EnsureCompositorPaintDispatcher(); return MakeGarbageCollected<PaintWorkletProxyClient>( - std::move(compositor_painter_dispatcher)); + worklet_id, std::move(compositor_painter_dispatcher)); } PaintWorkletProxyClient::PaintWorkletProxyClient( + int worklet_id, scoped_refptr<PaintWorkletPaintDispatcher> compositor_paintee) : compositor_paintee_(std::move(compositor_paintee)), + worklet_id_(worklet_id), state_(RunState::kUninitialized) { DCHECK(IsMainThread()); }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h index c8a0cccf..4ad2396 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
@@ -33,9 +33,10 @@ public: static const char kSupplementName[]; - static PaintWorkletProxyClient* Create(Document*); + static PaintWorkletProxyClient* Create(Document*, int worklet_id); PaintWorkletProxyClient( + int worklet_id, scoped_refptr<PaintWorkletPaintDispatcher> compositor_paintee); virtual ~PaintWorkletProxyClient() = default; @@ -51,6 +52,7 @@ PaintWorkletProxyClientConstruction); scoped_refptr<PaintWorkletPaintDispatcher> compositor_paintee_; + const int worklet_id_; CrossThreadPersistent<PaintWorkletGlobalScope> global_scope_; enum RunState { kUninitialized, kWorking, kDisposed } state_; };
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc index 614ed29..2aa76747 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
@@ -17,14 +17,16 @@ TEST_F(PaintWorkletProxyClientTest, PaintWorkletProxyClientConstruction) { PaintWorkletProxyClient* proxy_client = - MakeGarbageCollected<PaintWorkletProxyClient>(nullptr); + MakeGarbageCollected<PaintWorkletProxyClient>(1, nullptr); + EXPECT_EQ(proxy_client->worklet_id_, 1); EXPECT_EQ(proxy_client->compositor_paintee_, nullptr); scoped_refptr<PaintWorkletPaintDispatcher> dispatcher = base::MakeRefCounted<PaintWorkletPaintDispatcher>(); proxy_client = - MakeGarbageCollected<PaintWorkletProxyClient>(std::move(dispatcher)); + MakeGarbageCollected<PaintWorkletProxyClient>(1, std::move(dispatcher)); + EXPECT_EQ(proxy_client->worklet_id_, 1); EXPECT_NE(proxy_client->compositor_paintee_, nullptr); }
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc index 5cf8cc7..4ae119aba 100644 --- a/third_party/blink/renderer/modules/exported/web_ax_object.cc +++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -57,7 +57,9 @@ #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/modules/accessibility/ax_object.h" #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h" +#include "third_party/blink/renderer/modules/accessibility/ax_position.h" #include "third_party/blink/renderer/modules/accessibility/ax_range.h" +#include "third_party/blink/renderer/modules/accessibility/ax_selection.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" namespace blink { @@ -773,6 +775,55 @@ WebAXObject& focus_object, int& focus_offset, ax::mojom::TextAffinity& focus_affinity) const { + anchor_object = WebAXObject(); + anchor_offset = -1; + anchor_affinity = ax::mojom::TextAffinity::kDownstream; + focus_object = WebAXObject(); + focus_offset = -1; + focus_affinity = ax::mojom::TextAffinity::kDownstream; + + if (IsDetached()) + return; + + WebAXObject focus = FromWebDocumentFocused(GetDocument()); + if (focus.IsDetached()) + return; + + const auto ax_selection = + focus.private_->IsNativeTextControl() + ? AXSelection::FromCurrentSelection( + ToTextControl(*focus.private_->GetNode())) + : AXSelection::FromCurrentSelection(*focus.private_->GetDocument()); + if (!ax_selection) + return; + + const AXPosition base = ax_selection.Base(); + anchor_object = WebAXObject(const_cast<AXObject*>(base.ContainerObject())); + const AXPosition extent = ax_selection.Extent(); + focus_object = WebAXObject(const_cast<AXObject*>(extent.ContainerObject())); + + if (base.IsTextPosition()) { + anchor_offset = base.TextOffset(); + anchor_affinity = ToAXAffinity(base.Affinity()); + } else { + anchor_offset = base.ChildIndex(); + } + + if (extent.IsTextPosition()) { + focus_offset = extent.TextOffset(); + focus_affinity = ToAXAffinity(extent.Affinity()); + } else { + focus_offset = extent.ChildIndex(); + } +} + +void WebAXObject::SelectionDeprecated( + WebAXObject& anchor_object, + int& anchor_offset, + ax::mojom::TextAffinity& anchor_affinity, + WebAXObject& focus_object, + int& focus_offset, + ax::mojom::TextAffinity& focus_affinity) const { if (IsDetached()) { anchor_object = WebAXObject(); anchor_offset = -1; @@ -811,6 +862,90 @@ int anchor_offset, const WebAXObject& focus_object, int focus_offset) const { + if (IsDetached() || anchor_object.IsDetached() || focus_object.IsDetached()) + return false; + + AXPosition ax_base, ax_extent; + if (static_cast<const AXObject*>(anchor_object)->IsTextObject() || + static_cast<const AXObject*>(anchor_object)->IsNativeTextControl()) { + ax_base = + AXPosition::CreatePositionInTextObject(*anchor_object, anchor_offset); + } else if (anchor_offset <= 0) { + ax_base = AXPosition::CreateFirstPositionInObject(*anchor_object); + } else if (anchor_offset >= static_cast<int>(anchor_object.ChildCount())) { + ax_base = AXPosition::CreateLastPositionInObject(*anchor_object); + } else { + DCHECK_GE(anchor_offset, 0); + ax_base = AXPosition::CreatePositionBeforeObject( + *anchor_object.ChildAt(static_cast<unsigned int>(anchor_offset))); + } + + if (static_cast<const AXObject*>(focus_object)->IsTextObject() || + static_cast<const AXObject*>(focus_object)->IsNativeTextControl()) { + ax_extent = + AXPosition::CreatePositionInTextObject(*focus_object, focus_offset); + } else if (focus_offset <= 0) { + ax_extent = AXPosition::CreateFirstPositionInObject(*focus_object); + } else if (focus_offset >= static_cast<int>(focus_object.ChildCount())) { + ax_extent = AXPosition::CreateLastPositionInObject(*focus_object); + } else { + DCHECK_GE(focus_offset, 0); + ax_extent = AXPosition::CreatePositionBeforeObject( + *focus_object.ChildAt(static_cast<unsigned int>(focus_offset))); + } + + AXSelection::Builder builder; + AXSelection ax_selection = + builder.SetBase(ax_base).SetExtent(ax_extent).Build(); + return ax_selection.Select(); +} + +unsigned WebAXObject::SelectionEnd() const { + if (IsDetached()) + return 0; + + WebAXObject focus = FromWebDocumentFocused(GetDocument()); + if (focus.IsDetached()) + return 0; + + const auto ax_selection = + focus.private_->IsNativeTextControl() + ? AXSelection::FromCurrentSelection( + ToTextControl(*focus.private_->GetNode())) + : AXSelection::FromCurrentSelection(*focus.private_->GetDocument()); + if (!ax_selection) + return 0; + + if (ax_selection.Extent().IsTextPosition()) + return ax_selection.Extent().TextOffset(); + return ax_selection.Extent().ChildIndex(); +} + +unsigned WebAXObject::SelectionStart() const { + if (IsDetached()) + return 0; + + WebAXObject focus = FromWebDocumentFocused(GetDocument()); + if (focus.IsDetached()) + return 0; + + const auto ax_selection = + focus.private_->IsNativeTextControl() + ? AXSelection::FromCurrentSelection( + ToTextControl(*focus.private_->GetNode())) + : AXSelection::FromCurrentSelection(*focus.private_->GetDocument()); + if (!ax_selection) + return 0; + + if (ax_selection.Base().IsTextPosition()) + return ax_selection.Base().TextOffset(); + return ax_selection.Base().ChildIndex(); +} + +bool WebAXObject::SetSelectionDeprecated(const WebAXObject& anchor_object, + int anchor_offset, + const WebAXObject& focus_object, + int focus_offset) const { if (IsDetached()) return false; @@ -820,7 +955,7 @@ return private_->RequestSetSelectionAction(ax_selection); } -unsigned WebAXObject::SelectionEnd() const { +unsigned WebAXObject::SelectionEndDeprecated() const { if (IsDetached()) return 0; @@ -831,7 +966,7 @@ return ax_selection.focus_offset; } -unsigned WebAXObject::SelectionStart() const { +unsigned WebAXObject::SelectionStartDeprecated() const { if (IsDetached()) return 0;
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc index 3e0f605..4d0044a8 100644 --- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc +++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
@@ -214,11 +214,17 @@ if (!IsWanted()) return; - if (!GetDocument().FocusedElement() || - (GetDocument().FocusedElement()->parentElement() != this && - GetDocument().FocusedElement() != this)) { - SetIsWanted(false); + // Cancel hiding if the focused element is a descendent of this element + auto* focused_element = GetDocument().FocusedElement(); + while (focused_element) { + if (focused_element == this) { + return; + } + + focused_element = focused_element->parentElement(); } + + SetIsWanted(false); } // Focus the given item in the list if it is displayed. Returns whether it was
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc index 421487e..fbab43b 100644 --- a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc +++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
@@ -100,6 +100,9 @@ MediaElement().DisableAutomaticTextTrackSelection(); } + // Close the text track list, + // since we don't support selecting multiple tracks + SetIsWanted(false); event.SetDefaultHandled(); } MediaControlPopupMenuElement::DefaultEventHandler(event);
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc index 50fb3b6f2..7efcfb71 100644 --- a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc +++ b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
@@ -267,6 +267,9 @@ std::unique_ptr<InstalledScriptsManager::ScriptData> ServiceWorkerInstalledScriptsManager::GetScriptData(const KURL& script_url) { DCHECK(!IsMainThread()); + TRACE_EVENT1("ServiceWorker", + "ServiceWorkerInstalledScriptsManager::GetScriptData", "url", + script_url.GetString().Utf8().data()); if (!IsScriptInstalled(script_url)) return nullptr;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc index aeb9f00..06c08f4 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -93,7 +93,6 @@ #include "third_party/blink/renderer/modules/webgl/webgl_vertex_array_object.h" #include "third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_oes.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h" #include "third_party/blink/renderer/platform/cross_thread_functional.h" #include "third_party/blink/renderer/platform/geometry/int_size.h"
diff --git a/third_party/blink/renderer/modules/worklet/worklet_thread_test_common.cc b/third_party/blink/renderer/modules/worklet/worklet_thread_test_common.cc index edae41a6..a42679ef 100644 --- a/third_party/blink/renderer/modules/worklet/worklet_thread_test_common.cc +++ b/third_party/blink/renderer/modules/worklet/worklet_thread_test_common.cc
@@ -51,7 +51,7 @@ WorkerReportingProxy* reporting_proxy, PaintWorkletProxyClient* proxy_client) { if (!proxy_client) - proxy_client = MakeGarbageCollected<PaintWorkletProxyClient>(nullptr); + proxy_client = MakeGarbageCollected<PaintWorkletProxyClient>(1, nullptr); WorkerClients* clients = WorkerClients::Create(); ProvidePaintWorkletProxyClientTo(clients, proxy_client);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index ed38474..c7504e6 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -446,10 +446,6 @@ "bindings/script_state.h", "bindings/script_wrappable.cc", "bindings/script_wrappable.h", - "bindings/script_wrappable_marking_visitor.cc", - "bindings/script_wrappable_marking_visitor.h", - "bindings/script_wrappable_visitor.h", - "bindings/script_wrappable_visitor_verifier.h", "bindings/shared_persistent.h", "bindings/string_resource.cc", "bindings/string_resource.h",
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h index 6e1213cae..3387994c 100644 --- a/third_party/blink/renderer/platform/bindings/dom_data_store.h +++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -35,7 +35,6 @@ #include "base/optional.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h" #include "third_party/blink/renderer/platform/wtf/allocator.h"
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable.cc b/third_party/blink/renderer/platform/bindings/script_wrappable.cc index d32c3d53..5d4d7bd 100644 --- a/third_party/blink/renderer/platform/bindings/script_wrappable.cc +++ b/third_party/blink/renderer/platform/bindings/script_wrappable.cc
@@ -5,7 +5,6 @@ #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc b/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc deleted file mode 100644 index 03f615b3..0000000 --- a/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc +++ /dev/null
@@ -1,318 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" - -#include "base/auto_reset.h" -#include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h" -#include "third_party/blink/renderer/platform/bindings/custom_wrappable.h" -#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" -#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h" -#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" -#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" -#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" -#include "third_party/blink/renderer/platform/heap/heap_compact.h" -#include "third_party/blink/renderer/platform/heap/heap_page.h" -#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" -#include "third_party/blink/renderer/platform/wtf/time.h" - -namespace blink { - -ScriptWrappableMarkingVisitor::~ScriptWrappableMarkingVisitor() = default; - -void ScriptWrappableMarkingVisitor::TracePrologue() { - // This CHECK ensures that wrapper tracing is not started from scopes - // that forbid GC execution, e.g., constructors. - CHECK(ThreadState::Current()); - PerformCleanup(); - - CHECK(!tracing_in_progress_); - CHECK(!should_cleanup_); - CHECK(headers_to_unmark_.IsEmpty()); - CHECK(marking_deque_.IsEmpty()); - CHECK(verifier_deque_.IsEmpty()); - tracing_in_progress_ = true; - ThreadState::Current()->EnableWrapperTracingBarrier(); -} - -void ScriptWrappableMarkingVisitor::EnterFinalPause(EmbedderStackState) { - CHECK(ThreadState::Current()); - ThreadState::Current()->DisableWrapperTracingBarrier(); - ActiveScriptWrappableBase::TraceActiveScriptWrappables(isolate(), this); -} - -void ScriptWrappableMarkingVisitor::TraceEpilogue() { - CHECK(ThreadState::Current()); - DCHECK(marking_deque_.IsEmpty()); -#if DCHECK_IS_ON() - ScriptWrappableVisitorVerifier verifier(ThreadState::Current()); - for (auto& marking_data : verifier_deque_) { - // Check that all children of this object are marked. - marking_data.Trace(&verifier); - } -#endif - - should_cleanup_ = true; - tracing_in_progress_ = false; - ScheduleIdleLazyCleanup(); -} - -void ScriptWrappableMarkingVisitor::AbortTracingForTermination() { - CHECK(ThreadState::Current()); - should_cleanup_ = true; - tracing_in_progress_ = false; - ThreadState::Current()->DisableWrapperTracingBarrier(); - PerformCleanup(); -} - -bool ScriptWrappableMarkingVisitor::IsTracingDone() { - return marking_deque_.empty(); -} - -bool ScriptWrappableMarkingVisitor::IsRootForNonTracingGC( - const v8::TracedGlobal<v8::Value>& handle) { - return UnifiedHeapController::IsRootForNonTracingGCInternal(handle); -} - -void ScriptWrappableMarkingVisitor::PerformCleanup() { - if (!should_cleanup_) - return; - - CHECK(!tracing_in_progress_); - for (auto* header : headers_to_unmark_) { - // Dead objects residing in the marking deque may become invalid due to - // minor garbage collections and are therefore set to nullptr. We have - // to skip over such objects. - if (header) - header->UnmarkWrapperHeader(); - } - - headers_to_unmark_.clear(); - marking_deque_.clear(); - verifier_deque_.clear(); - should_cleanup_ = false; -} - -void ScriptWrappableMarkingVisitor::ScheduleIdleLazyCleanup() { - if (idle_cleanup_task_scheduled_) - return; - - ThreadScheduler::Current()->PostIdleTask( - FROM_HERE, WTF::Bind(&ScriptWrappableMarkingVisitor::PerformLazyCleanup, - WTF::Unretained(this))); - idle_cleanup_task_scheduled_ = true; -} - -void ScriptWrappableMarkingVisitor::PerformLazyCleanup(TimeTicks deadline) { - idle_cleanup_task_scheduled_ = false; - - if (!should_cleanup_) - return; - - TRACE_EVENT1("blink_gc,devtools.timeline", - "ScriptWrappableMarkingVisitor::performLazyCleanup", - "idleDeltaInSeconds", - (deadline - CurrentTimeTicks()).InSecondsF()); - - const int kDeadlineCheckInterval = 2500; - int processed_wrapper_count = 0; - for (auto it = headers_to_unmark_.rbegin(); - it != headers_to_unmark_.rend();) { - auto* header = *it; - // Dead objects residing in the marking deque may become invalid due to - // minor garbage collections and are therefore set to nullptr. We have - // to skip over such objects. - if (header) - header->UnmarkWrapperHeader(); - - ++it; - headers_to_unmark_.pop_back(); - - processed_wrapper_count++; - if (processed_wrapper_count % kDeadlineCheckInterval == 0) { - if (deadline <= CurrentTimeTicks()) { - ScheduleIdleLazyCleanup(); - return; - } - } - } - - // Unmarked all headers. - CHECK(headers_to_unmark_.IsEmpty()); - marking_deque_.clear(); - verifier_deque_.clear(); - should_cleanup_ = false; -} - -void ScriptWrappableMarkingVisitor::RegisterV8Reference( - const std::pair<void*, void*>& internal_fields) { - DCHECK(tracing_in_progress_); - - WrapperTypeInfo* wrapper_type_info = - reinterpret_cast<WrapperTypeInfo*>(internal_fields.first); - if (wrapper_type_info->gin_embedder != gin::GinEmbedder::kEmbedderBlink) { - return; - } - - wrapper_type_info->TraceWithWrappers(this, internal_fields.second); -} - -void ScriptWrappableMarkingVisitor::RegisterV8References( - const std::vector<std::pair<void*, void*>>& - internal_fields_of_potential_wrappers) { - CHECK(ThreadState::Current()); - for (auto& pair : internal_fields_of_potential_wrappers) { - RegisterV8Reference(pair); - } -} - -bool ScriptWrappableMarkingVisitor::AdvanceTracing(double deadline_in_ms) { - constexpr int kObjectsBeforeInterrupt = 100; - // Do not drain the marking deque in a state where we can generally not - // perform a GC. This makes sure that TraceTraits and friends find - // themselves in a well-defined environment, e.g., properly set up vtables. - CHECK(ThreadState::Current()); - CHECK(tracing_in_progress_); - TimeTicks deadline = - TimeTicks() + TimeDelta::FromMillisecondsD(deadline_in_ms); - while (deadline.is_max() || WTF::CurrentTimeTicks() < deadline) { - for (int objects = 0; objects++ < kObjectsBeforeInterrupt;) { - if (marking_deque_.IsEmpty()) { - return true; - } - marking_deque_.TakeFirst().Trace(this); - } - } - return false; -} - -void ScriptWrappableMarkingVisitor::MarkWrapperHeader( - HeapObjectHeader* header) { - DCHECK(!header->IsWrapperHeaderMarked()); - // Verify that no compactable & movable objects are slated for - // lazy unmarking. - DCHECK(!HeapCompact::IsCompactableArena( - PageFromObject(header)->Arena()->ArenaIndex())); - header->MarkWrapperHeader(); - headers_to_unmark_.push_back(header); -} - -void ScriptWrappableMarkingVisitor::WriteBarrier( - v8::Isolate* isolate, - const TraceWrapperV8Reference<v8::Value>& dst_object) { - if (dst_object.IsEmpty() || !ThreadState::IsAnyWrapperTracing()) - return; - ScriptWrappableMarkingVisitor* visitor = CurrentVisitor(isolate); - if (!visitor->WrapperTracingInProgress()) - return; - - // Conservatively assume that the source object containing |dst_object| is - // marked. - visitor->Trace(dst_object); -} - -void ScriptWrappableMarkingVisitor::WriteBarrier( - v8::Isolate* isolate, - const WrapperTypeInfo* wrapper_type_info, - void* object) { - if (!ThreadState::IsAnyWrapperTracing()) - return; - ScriptWrappableMarkingVisitor* visitor = CurrentVisitor(isolate); - if (!visitor->WrapperTracingInProgress()) - return; - - wrapper_type_info->TraceWithWrappers(visitor, object); -} - -void ScriptWrappableMarkingVisitor::Visit( - const TraceWrapperV8Reference<v8::Value>& traced_wrapper) { - // The write barrier may try to mark a wrapper because cleanup is still - // delayed. Bail out in this case. We also allow unconditional marking which - // requires us to bail out here when tracing is not in progress. - if (!tracing_in_progress_ || traced_wrapper.Get().IsEmpty()) - return; - - RegisterEmbedderReference(traced_wrapper.Get()); -} - -void ScriptWrappableMarkingVisitor::VisitWithWrappers( - void* object, - TraceDescriptor descriptor) { - HeapObjectHeader* header = - HeapObjectHeader::FromPayload(descriptor.base_object_payload); - if (header->IsWrapperHeaderMarked()) - return; - MarkWrapperHeader(header); - DCHECK(tracing_in_progress_); - DCHECK(header->IsWrapperHeaderMarked()); - marking_deque_.push_back(MarkingDequeItem(descriptor)); -#if DCHECK_IS_ON() - verifier_deque_.push_back(MarkingDequeItem(descriptor)); -#endif -} - -void ScriptWrappableMarkingVisitor::VisitBackingStoreStrongly( - void* object, - void** object_slot, - TraceDescriptor desc) { - if (!object) - return; - desc.callback(this, desc.base_object_payload); -} - -void ScriptWrappableMarkingVisitor::InvalidateDeadObjectsInMarkingDeque() { - for (auto it = marking_deque_.begin(); it != marking_deque_.end(); ++it) { - auto& marking_data = *it; - if (marking_data.ShouldBeInvalidated()) { - marking_data.Invalidate(); - } - } - for (auto it = verifier_deque_.begin(); it != verifier_deque_.end(); ++it) { - auto& marking_data = *it; - if (marking_data.ShouldBeInvalidated()) { - marking_data.Invalidate(); - } - } - for (auto** it = headers_to_unmark_.begin(); it != headers_to_unmark_.end(); - ++it) { - auto* header = *it; - if (header && !header->IsMarked()) { - *it = nullptr; - } - } -} - -void ScriptWrappableMarkingVisitor::InvalidateDeadObjectsInMarkingDeque( - v8::Isolate* isolate) { - ScriptWrappableMarkingVisitor* script_wrappable_visitor = - V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor(); - if (script_wrappable_visitor) - script_wrappable_visitor->InvalidateDeadObjectsInMarkingDeque(); -} - -void ScriptWrappableMarkingVisitor::PerformCleanup(v8::Isolate* isolate) { - ScriptWrappableMarkingVisitor* script_wrappable_visitor = - V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor(); - if (script_wrappable_visitor) - script_wrappable_visitor->PerformCleanup(); -} - -ScriptWrappableMarkingVisitor* ScriptWrappableMarkingVisitor::CurrentVisitor( - v8::Isolate* isolate) { - return V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor(); -} - -bool ScriptWrappableMarkingVisitor::MarkingDequeContains(void* needle) { - for (auto item : marking_deque_) { - if (item.RawObjectPointer() == needle) - return true; - } - return false; -} - -} // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h b/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h deleted file mode 100644 index 12dbd34..0000000 --- a/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h +++ /dev/null
@@ -1,250 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_ - -#include "base/gtest_prod_util.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" -#include "third_party/blink/renderer/platform/heap/heap_page.h" -#include "third_party/blink/renderer/platform/heap/threading_traits.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/wtf/deque.h" -#include "third_party/blink/renderer/platform/wtf/time.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" -#include "v8/include/v8.h" - -namespace blink { - -class HeapObjectHeader; -class ScriptWrappableVisitor; -template <typename T> -class TraceWrapperV8Reference; -struct WrapperTypeInfo; - -// ScriptWrappableVisitor is used to trace through Blink's heap to find all -// reachable wrappers. V8 calls this visitor during its garbage collection, -// see v8::EmbedderHeapTracer. -class PLATFORM_EXPORT ScriptWrappableMarkingVisitor - : public v8::EmbedderHeapTracer, - public ScriptWrappableVisitor { - DISALLOW_IMPLICIT_CONSTRUCTORS(ScriptWrappableMarkingVisitor); - - public: - static ScriptWrappableMarkingVisitor* CurrentVisitor(v8::Isolate*); - - // Replace all dead objects in the marking deque with nullptr after Oilpan - // garbage collection. - static void InvalidateDeadObjectsInMarkingDeque(v8::Isolate*); - - // Immediately clean up all wrappers. - static void PerformCleanup(v8::Isolate*); - - // Conservative Dijkstra barrier. - // - // On assignment 'x.a = y' during incremental marking the Dijkstra barrier - // suggests checking the color of 'x' and only mark 'y' if 'x' is marked. - // - // Since checking 'x' is expensive in the current setting, as it requires - // either a back pointer or expensive lookup logic due to large objects and - // multiple inheritance, just assume that 'x' is black. We assume here that - // since an object 'x' is referenced for a write, it will generally also be - // alive in the current GC cycle. - template <typename T> - inline static void WriteBarrier(const T* dst_object); - - template <typename T> - static void WriteBarrier(TraceWrapperMember<T>* array, size_t length); - - static void WriteBarrier(v8::Isolate*, const WrapperTypeInfo*, void*); - - static void WriteBarrier(v8::Isolate*, - const TraceWrapperV8Reference<v8::Value>&); - - explicit ScriptWrappableMarkingVisitor(ThreadState* thread_state) - : ScriptWrappableVisitor(thread_state) {} - ~ScriptWrappableMarkingVisitor() override; - - bool WrapperTracingInProgress() const { return tracing_in_progress_; } - - void AbortTracingForTermination(); - - // v8::EmbedderHeapTracer interface. - void TracePrologue() override; - void RegisterV8References(const std::vector<std::pair<void*, void*>>& - internal_fields_of_potential_wrappers) override; - void RegisterV8Reference(const std::pair<void*, void*>& internal_fields); - bool AdvanceTracing(double deadline_in_ms) override; - void TraceEpilogue() override; - void EnterFinalPause(EmbedderStackState) override; - bool IsTracingDone() override; - bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>&) override; - - // ScriptWrappableVisitor interface. - void Visit(const TraceWrapperV8Reference<v8::Value>&) override; - void VisitWithWrappers(void*, TraceDescriptor) override; - void VisitBackingStoreStrongly(void* object, - void** object_slot, - TraceDescriptor desc) override; - - protected: - using Visitor::Visit; - - private: - class MarkingDequeItem { - DISALLOW_NEW(); - - public: - explicit MarkingDequeItem(const TraceDescriptor& descriptor) - : raw_object_pointer_(descriptor.base_object_payload), - trace_callback_(descriptor.callback) { - DCHECK(raw_object_pointer_); - DCHECK(trace_callback_); - } - - // Traces wrappers if the underlying object has not yet been invalidated. - inline void Trace(ScriptWrappableVisitor* visitor) const { - if (raw_object_pointer_) { - trace_callback_(visitor, const_cast<void*>(raw_object_pointer_)); - } - } - - inline const void* RawObjectPointer() { return raw_object_pointer_; } - - // Returns true if the object is currently marked in Oilpan and false - // otherwise. - inline bool ShouldBeInvalidated() { - return raw_object_pointer_ && !GetHeapObjectHeader()->IsMarked(); - } - - // Invalidates the current wrapper marking data, i.e., calling Trace - // will result in a noop. - inline void Invalidate() { raw_object_pointer_ = nullptr; } - - private: - inline const HeapObjectHeader* GetHeapObjectHeader() { - return HeapObjectHeader::FromPayload(raw_object_pointer_); - } - - const void* raw_object_pointer_; - TraceCallback trace_callback_; - }; - - void MarkWrapperHeader(HeapObjectHeader*); - - // Schedule an idle task to perform a lazy (incremental) clean up of - // wrappers. - void ScheduleIdleLazyCleanup(); - void PerformLazyCleanup(TimeTicks deadline); - - void InvalidateDeadObjectsInMarkingDeque(); - - // Immediately cleans up all wrappers if necessary. - void PerformCleanup(); - - WTF::Deque<MarkingDequeItem>* MarkingDeque() { return &marking_deque_; } - - bool MarkingDequeContains(void* needle); - - // Returns true if wrapper tracing is currently in progress, i.e., - // TracePrologue has been called, and TraceEpilogue has not yet been called. - bool tracing_in_progress_ = false; - - // Indicates whether an idle task for a lazy cleanup has already been - // scheduled. The flag is used to avoid scheduling multiple idle tasks for - // cleaning up. - bool idle_cleanup_task_scheduled_ = false; - - // Indicates whether cleanup should currently happen. The flag is used to - // avoid cleaning up in the next GC cycle. - bool should_cleanup_ = false; - - // Collection of objects we need to trace from. We assume it is safe to hold - // on to the raw pointers because: - // - oilpan object cannot move - // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete all - // obsolete objects - WTF::Deque<MarkingDequeItem> marking_deque_; - - // Collection of objects we started tracing from. We assume it is safe to - // hold on to the raw pointers because: - // - oilpan object cannot move - // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete - // all obsolete objects - // - // These objects are used when TraceWrappablesVerifier feature is enabled to - // verify that all objects reachable in the atomic pause were marked - // incrementally. If not, there is one or multiple write barriers missing. - WTF::Deque<MarkingDequeItem> verifier_deque_; - - // Collection of headers we need to unmark after the tracing finished. We - // assume it is safe to hold on to the headers because: - // - oilpan objects cannot move - // - objects this headers belong to are invalidated by the oilpan GC in - // invalidateDeadObjectsInMarkingDeque. - WTF::Vector<HeapObjectHeader*> headers_to_unmark_; - - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, MixinTracing); - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, - OilpanClearsMarkingDequeWhenObjectDied); - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, - ScriptWrappableMarkingVisitorTracesWrappers); - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, - OilpanClearsHeadersWhenObjectDied); - FRIEND_TEST_ALL_PREFIXES( - ScriptWrappableMarkingVisitorTest, - MarkedObjectDoesNothingOnWriteBarrierHitWhenDependencyIsMarkedToo); - FRIEND_TEST_ALL_PREFIXES( - ScriptWrappableMarkingVisitorTest, - MarkedObjectMarksDependencyOnWriteBarrierHitWhenNotMarked); - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, - WriteBarrierOnHeapVectorSwap1); - FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, - WriteBarrierOnHeapVectorSwap2); -}; - -template <typename T> -inline void ScriptWrappableMarkingVisitor::WriteBarrier(const T* dst_object) { - if (!ThreadState::IsAnyWrapperTracing() || !dst_object) - return; - - const ThreadState* thread_state = - ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); - DCHECK(thread_state); - // Bail out if tracing is not in progress. - if (!thread_state->IsWrapperTracing()) - return; - - // If the wrapper is already marked we can bail out here. - if (TraceTrait<T>::GetHeapObjectHeader(const_cast<T*>(dst_object)) - ->IsWrapperHeaderMarked()) - return; - - CurrentVisitor(thread_state->GetIsolate()) - ->VisitWithWrappers(const_cast<T*>(dst_object), - TraceDescriptorFor(dst_object)); -} - -template <typename T> -inline void ScriptWrappableMarkingVisitor::WriteBarrier( - TraceWrapperMember<T>* array, - size_t length) { - if (!ThreadState::IsAnyWrapperTracing() || !array) - return; - - const ThreadState* thread_state = - ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); - DCHECK(thread_state); - // Bail out if tracing is not in progress. - if (!thread_state->IsWrapperTracing()) - return; - - for (size_t i = 0; i < length; ++i) { - CurrentVisitor(thread_state->GetIsolate())->Trace(array[i]); - } -} - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h b/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h deleted file mode 100644 index ab4e5277..0000000 --- a/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_ - -#include "third_party/blink/renderer/platform/heap/visitor.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace blink { - -// Abstract visitor for visiting ScriptWrappable. Inherit from this -// visitor and implement the remaining Visit*() methods to visit all -// references related to wrappers. -class PLATFORM_EXPORT ScriptWrappableVisitor : public Visitor { - public: - explicit ScriptWrappableVisitor(ThreadState* thread_state) - : Visitor(thread_state) {} - - // Unused blink::Visitor overrides. Derived visitors should still override - // the cross-component visitation methods. See Visitor documentation. - void Visit(void* object, TraceDescriptor desc) final {} - void VisitWeak(void* object, - void** object_slot, - TraceDescriptor desc, - WeakCallback callback) final {} - void VisitBackingStoreWeakly(void*, - void**, - TraceDescriptor, - WeakCallback, - void*) final {} - void VisitBackingStoreOnly(void*, void**) final {} - void RegisterBackingStoreCallback(void**, MovingObjectCallback, void*) final { - } - void RegisterWeakCallback(void*, WeakCallback) final {} - - protected: - using Visitor::Visit; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_
diff --git a/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h b/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h deleted file mode 100644 index 1dac8ec3..0000000 --- a/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h +++ /dev/null
@@ -1,54 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_VERIFIER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_VERIFIER_H_ - -#include "base/logging.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" -#include "third_party/blink/renderer/platform/heap/gc_info.h" - -namespace blink { - -// This visitor should be applied on wrapper members of each marked object -// after marking is complete. The Visit method checks that the given wrapper -// is also marked. -class ScriptWrappableVisitorVerifier final : public ScriptWrappableVisitor { - public: - ScriptWrappableVisitorVerifier(ThreadState* thread_state) - : ScriptWrappableVisitor(thread_state) {} - - void Visit(const TraceWrapperV8Reference<v8::Value>&) final {} - - void VisitWithWrappers(void* object, TraceDescriptor descriptor) final { - HeapObjectHeader* header = - HeapObjectHeader::FromPayload(descriptor.base_object_payload); - const char* name = GCInfoTable::Get() - .GCInfoFromIndex(header->GcInfoIndex()) - ->name(descriptor.base_object_payload) - .value; - // If this FATAL is hit, it means that a white (not discovered by - // Trace) object was assigned as a member to a black object (already - // processed by Trace). The black object will not be processed anymore - // so white object will remain undetected and therefore its wrapper - // and all wrappers reachable from it would be collected. - // - // This means there is a write barrier missing somewhere. Check the - // backtrace to see which types are causing this and review all the places - // where white object is set to a black object. - LOG_IF(FATAL, !header->IsWrapperHeaderMarked()) - << "Write barrier missed for " << name; - } - - void VisitBackingStoreStrongly(void* object, - void** object_slot, - TraceDescriptor desc) final {} - - protected: - using Visitor::Visit; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_VERIFIER_H_
diff --git a/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h b/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h index 79304ef44..c4a74f1 100644 --- a/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h +++ b/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h
@@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_MEMBER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_MEMBER_H_ -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/heap/heap_allocator.h" namespace blink { @@ -23,11 +22,7 @@ public: TraceWrapperMember() : Member<T>(nullptr) {} - TraceWrapperMember(T* raw) : Member<T>(raw) { - // We have to use a write barrier here because of in-place construction - // in containers, such as HeapVector::push_back. - ScriptWrappableMarkingVisitor::WriteBarrier(raw); - } + TraceWrapperMember(T* raw) : Member<T>(raw) {} TraceWrapperMember(WTF::HashTableDeletedValueType x) : Member<T>(x) {} @@ -36,21 +31,18 @@ TraceWrapperMember& operator=(const TraceWrapperMember& other) { Member<T>::operator=(other); DCHECK_EQ(other.Get(), this->Get()); - ScriptWrappableMarkingVisitor::WriteBarrier(this->Get()); return *this; } TraceWrapperMember& operator=(const Member<T>& other) { Member<T>::operator=(other); DCHECK_EQ(other.Get(), this->Get()); - ScriptWrappableMarkingVisitor::WriteBarrier(this->Get()); return *this; } TraceWrapperMember& operator=(T* other) { Member<T>::operator=(other); DCHECK_EQ(other, this->Get()); - ScriptWrappableMarkingVisitor::WriteBarrier(this->Get()); return *this; } @@ -78,14 +70,7 @@ // TraceWrapperMember and Member match in vector backings. HeapVector<Member<T>>& a_ = reinterpret_cast<HeapVector<Member<T>>&>(a); a_.swap(b); - if (ThreadState::IsAnyWrapperTracing() && - ThreadState::Current()->IsWrapperTracing()) { - // If incremental marking is enabled we need to emit the write barrier since - // the swap was performed on HeapVector<Member<T>>. - for (auto item : a) { - ScriptWrappableMarkingVisitor::WriteBarrier(item.Get()); - } - } + // TODO(mlippautz): Remove this method } } // namespace blink
diff --git a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h index 043f11b..e1868ff 100644 --- a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h +++ b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
@@ -8,7 +8,6 @@ #include <utility> #include "base/macros.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h" #include "v8/include/v8.h" @@ -76,14 +75,10 @@ protected: ALWAYS_INLINE void InternalSet(v8::Isolate* isolate, v8::Local<T> handle) { handle_.Reset(isolate, handle); - ScriptWrappableMarkingVisitor::WriteBarrier(isolate, - UnsafeCast<v8::Value>()); UnifiedHeapMarkingVisitor::WriteBarrier(UnsafeCast<v8::Value>()); } ALWAYS_INLINE void WriteBarrier() const { - ScriptWrappableMarkingVisitor::WriteBarrier(v8::Isolate::GetCurrent(), - UnsafeCast<v8::Value>()); UnifiedHeapMarkingVisitor::WriteBarrier(UnsafeCast<v8::Value>()); }
diff --git a/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h b/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h index 5905a173..d44383a 100644 --- a/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h +++ b/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h
@@ -37,7 +37,6 @@ #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_binding.h" #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h" #include "third_party/blink/renderer/platform/platform_export.h" @@ -118,8 +117,6 @@ // The following write barrier is necessary as V8 might not see the newly // created object during garbage collection, e.g., when the object is black // allocated. - ScriptWrappableMarkingVisitor::WriteBarrier(isolate, wrapper_type_info, - wrappable); UnifiedHeapMarkingVisitor::WriteBarrier(isolate, wrapper_type_info, wrappable); }
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc index 1660cc4..600a10c 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc +++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
@@ -37,7 +37,6 @@ #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_binding.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" @@ -90,8 +89,6 @@ use_counter_disabled_(false), is_handling_recursion_level_error_(false), is_reporting_exception_(false), - script_wrappable_visitor_( - new ScriptWrappableMarkingVisitor(ThreadState::Current())), unified_heap_controller_( new UnifiedHeapController(ThreadState::Current())), runtime_call_stats_(base::DefaultTickClock::GetInstance()) { @@ -174,9 +171,6 @@ BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, BlinkGC::GCReason::kThreadTerminationGC); isolate->SetEmbedderHeapTracer(nullptr); - if (data->script_wrappable_visitor_->WrapperTracingInProgress()) - data->script_wrappable_visitor_->AbortTracingForTermination(); - data->script_wrappable_visitor_.reset(); data->unified_heap_controller_.reset(); }
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h index 40fc3b5..30a81fd 100644 --- a/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h +++ b/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h
@@ -34,7 +34,6 @@ #include "gin/public/isolate_holder.h" #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/bindings/v8_global_value_map.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/persistent.h" @@ -205,15 +204,6 @@ return active_script_wrappables_.Get(); } - ScriptWrappableMarkingVisitor* GetScriptWrappableMarkingVisitor() const { - return script_wrappable_visitor_.get(); - } - - void SwapScriptWrappableMarkingVisitor( - std::unique_ptr<ScriptWrappableMarkingVisitor>& other) { - script_wrappable_visitor_.swap(other); - } - UnifiedHeapController* GetUnifiedHeapController() const { return unified_heap_controller_.get(); } @@ -289,7 +279,6 @@ std::unique_ptr<Data> thread_debugger_; Persistent<ActiveScriptWrappableSet> active_script_wrappables_; - std::unique_ptr<ScriptWrappableMarkingVisitor> script_wrappable_visitor_; std::unique_ptr<UnifiedHeapController> unified_heap_controller_; RuntimeCallStats runtime_call_stats_;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc index c56be266..c96db6b 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -426,7 +426,7 @@ } sk_sp<SkSurface> CreateSkSurface() const override { - if (IsGpuContextLost()) + if (IsGpuContextLost() || !resource_) return nullptr; auto* gr = GetGrContext(); DCHECK(gr);
diff --git a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc index ea80294..4ec7b46 100644 --- a/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc +++ b/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
@@ -67,7 +67,9 @@ std::vector<FrameMetadata> frames = {FrameMetadata()}; sk_sp<DecodingImageGenerator> generator = DecodingImageGenerator::Create( std::move(frame), info, std::move(segment_reader), std::move(frames), - PaintImage::GetNextContentId(), true); + PaintImage::GetNextContentId(), true /* all_data_received */, + true /* is_eligible_for_accelerated_decoding */, + false /* can_yuv_decode */); return std::make_unique<SkiaPaintImageGenerator>( std::move(generator), PaintImage::kDefaultFrameIndex, PaintImage::kDefaultGeneratorClientId); @@ -80,10 +82,13 @@ scoped_refptr<SegmentReader> data, std::vector<FrameMetadata> frames, PaintImage::ContentId content_id, - bool all_data_received) { + bool all_data_received, + bool is_eligible_for_accelerated_decoding, + bool can_yuv_decode) { return sk_sp<DecodingImageGenerator>(new DecodingImageGenerator( std::move(frame_generator), info, std::move(data), std::move(frames), - content_id, all_data_received)); + content_id, all_data_received, is_eligible_for_accelerated_decoding, + can_yuv_decode)); } DecodingImageGenerator::DecodingImageGenerator( @@ -92,16 +97,24 @@ scoped_refptr<SegmentReader> data, std::vector<FrameMetadata> frames, PaintImage::ContentId complete_frame_content_id, - bool all_data_received) + bool all_data_received, + bool is_eligible_for_accelerated_decoding, + bool can_yuv_decode) : PaintImageGenerator(info, std::move(frames)), frame_generator_(std::move(frame_generator)), data_(std::move(data)), all_data_received_(all_data_received), - can_yuv_decode_(false), + is_eligible_for_accelerated_decoding_( + is_eligible_for_accelerated_decoding), + can_yuv_decode_(can_yuv_decode), complete_frame_content_id_(complete_frame_content_id) {} DecodingImageGenerator::~DecodingImageGenerator() = default; +bool DecodingImageGenerator::IsEligibleForAcceleratedDecoding() const { + return is_eligible_for_accelerated_decoding_; +} + sk_sp<SkData> DecodingImageGenerator::GetEncodedData() const { TRACE_EVENT0("blink", "DecodingImageGenerator::refEncodedData");
diff --git a/third_party/blink/renderer/platform/graphics/decoding_image_generator.h b/third_party/blink/renderer/platform/graphics/decoding_image_generator.h index 5e95cdc..4602ab7 100644 --- a/third_party/blink/renderer/platform/graphics/decoding_image_generator.h +++ b/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
@@ -61,13 +61,14 @@ scoped_refptr<SegmentReader>, std::vector<FrameMetadata>, PaintImage::ContentId, - bool all_data_received); + bool all_data_received, + bool is_eligible_for_accelerated_decoding, + bool can_yuv_decode); ~DecodingImageGenerator() override; - void SetCanYUVDecode(bool yes) { can_yuv_decode_ = yes; } - // PaintImageGenerator implementation. + bool IsEligibleForAcceleratedDecoding() const override; sk_sp<SkData> GetEncodedData() const override; bool GetPixels(const SkImageInfo&, void* pixels, @@ -92,12 +93,15 @@ scoped_refptr<SegmentReader>, std::vector<FrameMetadata>, PaintImage::ContentId, - bool all_data_received); + bool all_data_received, + bool is_eligible_for_accelerated_decoding, + bool can_yuv_decode); scoped_refptr<ImageFrameGenerator> frame_generator_; const scoped_refptr<SegmentReader> data_; // Data source. const bool all_data_received_; - bool can_yuv_decode_; + const bool is_eligible_for_accelerated_decoding_; + const bool can_yuv_decode_; const PaintImage::ContentId complete_frame_content_id_; DISALLOW_COPY_AND_ASSIGN(DecodingImageGenerator);
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc index ab66ebaa1..6b3e6666 100644 --- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc +++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
@@ -88,6 +88,7 @@ : metadata_decoder_(std::move(metadata_decoder)), repetition_count_(kAnimationNone), all_data_received_(false), + first_decoding_generator_created_(false), can_yuv_decode_(false), has_hot_spot_(false), image_is_high_bit_depth_(false), @@ -137,10 +138,13 @@ frames[i].duration = FrameDurationAtIndex(i); } + const bool is_eligible_for_accelerated_decoding = + !first_decoding_generator_created_ && all_data_received_; auto generator = DecodingImageGenerator::Create( frame_generator_, info, std::move(segment_reader), std::move(frames), - complete_frame_content_id_, all_data_received_); - generator->SetCanYUVDecode(can_yuv_decode_); + complete_frame_content_id_, all_data_received_, + is_eligible_for_accelerated_decoding, can_yuv_decode_); + first_decoding_generator_created_ = true; return generator; }
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h index 53013a4..2c08ab6 100644 --- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h +++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h
@@ -104,6 +104,7 @@ int repetition_count_; bool has_embedded_color_profile_ = false; bool all_data_received_; + bool first_decoding_generator_created_; bool can_yuv_decode_; bool has_hot_spot_; bool image_is_high_bit_depth_;
diff --git a/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc b/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc index 48aa0cf2..de2a0ee 100644 --- a/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc +++ b/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
@@ -185,6 +185,7 @@ cc::PaintCanvas* temp_canvas = recorder.beginRecording(100, 100); PaintImage image = CreatePaintImage(PaintImage::CompletionState::PARTIALLY_DONE); + ASSERT_TRUE(image); temp_canvas->drawImage(image, 0, 0); canvas_->drawPicture(recorder.finishRecordingAsPicture()); @@ -198,6 +199,49 @@ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), bitmap_.getColor(0, 0)); } +TEST_F(DeferredImageDecoderTest, isEligibleForHardwareDecodingNonIncremental) { + // The image is received completely. This is okay for hardware decoding since + // it's assumed that the software decoder hasn't done any work. + lazy_decoder_->SetData(data_, true); + PaintImage image = CreatePaintImage(); + ASSERT_TRUE(image); + EXPECT_TRUE(image.IsEligibleForAcceleratedDecoding()); +} + +TEST_F(DeferredImageDecoderTest, isEligibleForHardwareDecodingIncremental) { + // The image is received in two parts, but a PaintImageGenerator is created + // only after all the data is received. This is okay for hardware decoding + // since it's assumed that the software decoder hasn't done any work before + // the PaintImageGenerator is created. + scoped_refptr<SharedBuffer> partial_data = + SharedBuffer::Create(data_->Data(), data_->size() - 10); + lazy_decoder_->SetData(partial_data, false); + lazy_decoder_->SetData(data_, true); + PaintImage image = CreatePaintImage(); + ASSERT_TRUE(image); + EXPECT_TRUE(image.IsEligibleForAcceleratedDecoding()); +} + +TEST_F(DeferredImageDecoderTest, isNotEligibleForHardwareDecoding) { + // The image is received in two parts, and a PaintImageGenerator is created + // for each one. In real usage, it's likely that the software image decoder + // will start working with partial data, so there's no point in using the + // hardware accelerator (because if we did, we'd be doing double work: in + // software and in hardware). + scoped_refptr<SharedBuffer> partial_data = + SharedBuffer::Create(data_->Data(), data_->size() - 10); + lazy_decoder_->SetData(partial_data, false); + PaintImage image = + CreatePaintImage(PaintImage::CompletionState::PARTIALLY_DONE); + ASSERT_TRUE(image); + EXPECT_FALSE(image.IsEligibleForAcceleratedDecoding()); + + lazy_decoder_->SetData(data_, true); + image = CreatePaintImage(); + ASSERT_TRUE(image); + EXPECT_FALSE(image.IsEligibleForAcceleratedDecoding()); +} + static void RasterizeMain(cc::PaintCanvas* canvas, sk_sp<PaintRecord> record) { canvas->drawPicture(record); }
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc index 7b6ef7e..9ea7b68 100644 --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc +++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -17,6 +17,7 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrContext.h" +#include "v8/include/v8.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.h b/third_party/blink/renderer/platform/heap/heap_allocator.h index fad71cb..4c04c8d 100644 --- a/third_party/blink/renderer/platform/heap/heap_allocator.h +++ b/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_ALLOCATOR_H_ #include "build/build_config.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap_buildflags.h" #include "third_party/blink/renderer/platform/heap/marking_visitor.h" @@ -140,7 +139,6 @@ template <typename T> static void BackingWriteBarrier(TraceWrapperMember<T>* address, size_t size) { MarkingVisitor::WriteBarrier(address); - ScriptWrappableMarkingVisitor::WriteBarrier(address, size); } template <typename T>
diff --git a/third_party/blink/renderer/platform/heap/run_all_tests.cc b/third_party/blink/renderer/platform/heap/run_all_tests.cc index ea68f22d..ac9ae033 100644 --- a/third_party/blink/renderer/platform/heap/run_all_tests.cc +++ b/third_party/blink/renderer/platform/heap/run_all_tests.cc
@@ -43,8 +43,7 @@ void Initialize() override { base::TestSuite::Initialize(); content::SetUpBlinkTestEnvironment(); - blink::ThreadState::Current()->RegisterTraceDOMWrappers(nullptr, nullptr, - nullptr, nullptr); + blink::ThreadState::Current()->RegisterTraceDOMWrappers(nullptr, nullptr); } void Shutdown() override { blink::ThreadState::Current()->CollectAllGarbage();
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc index 10bc122..9926bcc2 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.cc +++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -176,10 +176,6 @@ gc_state_(kNoGCScheduled), gc_phase_(GCPhase::kNone), reason_for_scheduled_gc_(BlinkGC::GCReason::kMaxValue), - isolate_(nullptr), - trace_dom_wrappers_(nullptr), - invalidate_dead_objects_in_wrappers_marking_deque_(nullptr), - perform_cleanup_(nullptr), #if defined(ADDRESS_SANITIZER) asan_fake_stack_(__asan_get_current_fake_stack()), #endif @@ -190,7 +186,6 @@ DCHECK(CheckThread()); DCHECK(!**thread_specific_); **thread_specific_ = this; - heap_ = std::make_unique<ThreadHeap>(this); } @@ -1403,9 +1398,6 @@ // static AtomicEntryFlag ThreadState::incremental_marking_flag_; -// static -AtomicEntryFlag ThreadState::wrapper_tracing_flag_; - void ThreadState::EnableIncrementalMarkingBarrier() { CHECK(!IsIncrementalMarking()); incremental_marking_flag_.Enter(); @@ -1418,18 +1410,6 @@ SetIncrementalMarking(false); } -void ThreadState::EnableWrapperTracingBarrier() { - CHECK(!IsWrapperTracing()); - wrapper_tracing_flag_.Enter(); - SetWrapperTracing(true); -} - -void ThreadState::DisableWrapperTracingBarrier() { - CHECK(IsWrapperTracing()); - wrapper_tracing_flag_.Exit(); - SetWrapperTracing(false); -} - void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) { VLOG(2) << "[state:" << this << "] " << "IncrementalMarking: Start"; @@ -1719,9 +1699,6 @@ if (marking_type == BlinkGC::kTakeSnapshot) BlinkGCMemoryDumpProvider::Instance()->ClearProcessDumpForCurrentGC(); - if (isolate_ && perform_cleanup_) - perform_cleanup_(isolate_); - if (stack_state == BlinkGC::kNoHeapPointersOnStack) { Heap().FlushNotFullyConstructedObjects(); } @@ -1802,9 +1779,6 @@ Heap().stats_collector()->NotifyMarkingCompleted(); WTF::Partitions::ReportMemoryUsageHistogram(); - if (invalidate_dead_objects_in_wrappers_marking_deque_) - invalidate_dead_objects_in_wrappers_marking_deque_(isolate_); - DEFINE_THREAD_SAFE_STATIC_LOCAL( CustomCountHistogram, total_object_space_histogram, ("BlinkGC.TotalObjectSpace", 0, 4 * 1024 * 1024, 50));
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h index 34a98f5d..7133170 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.h +++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -185,15 +185,6 @@ return incremental_marking_flag_.MightBeEntered(); } - // Returns true if some thread (possibly the current thread) may be doing - // wrapper tracing. If false is returned, the *current* thread is definitely - // not doing wrapper tracing. See atomic_entry_flag.h for details. - // - // For an exact check, use ThreadState::IsWrapperTracing. - static bool IsAnyWrapperTracing() { - return wrapper_tracing_flag_.MightBeEntered(); - } - static void AttachMainThread(); // Associate ThreadState object with the current thread. After this @@ -252,9 +243,6 @@ current_gc_data_.reason == BlinkGC::GCReason::kUnifiedHeapGC; } - void EnableWrapperTracingBarrier(); - void DisableWrapperTracingBarrier(); - // Incremental GC. void ScheduleIncrementalMarkingStep(); void ScheduleIncrementalMarkingFinalize(); @@ -317,9 +305,6 @@ return in_atomic_pause() && IsSweepingInProgress(); } - bool IsWrapperTracing() const { return wrapper_tracing_; } - void SetWrapperTracing(bool value) { wrapper_tracing_ = value; } - bool IsIncrementalMarking() const { return incremental_marking_; } void SetIncrementalMarking(bool value) { incremental_marking_ = value; } @@ -371,17 +356,12 @@ Vector<size_t> dead_size; }; - void RegisterTraceDOMWrappers( - v8::Isolate* isolate, - void (*trace_dom_wrappers)(v8::Isolate*, Visitor*), - void (*invalidate_dead_objects_in_wrappers_marking_deque)(v8::Isolate*), - void (*perform_cleanup)(v8::Isolate*)) { + void RegisterTraceDOMWrappers(v8::Isolate* isolate, + void (*trace_dom_wrappers)(v8::Isolate*, + Visitor*)) { isolate_ = isolate; DCHECK(!isolate_ || trace_dom_wrappers); trace_dom_wrappers_ = trace_dom_wrappers; - invalidate_dead_objects_in_wrappers_marking_deque_ = - invalidate_dead_objects_in_wrappers_marking_deque; - perform_cleanup_ = perform_cleanup; } void FreePersistentNode(PersistentRegion*, PersistentNode*); @@ -447,9 +427,6 @@ // Stores whether some ThreadState is currently in incremental marking. static AtomicEntryFlag incremental_marking_flag_; - // Same semantic as |incremental_marking_flag_|. - static AtomicEntryFlag wrapper_tracing_flag_; - static WTF::ThreadSpecific<ThreadState*>* thread_specific_; // We can't create a static member of type ThreadState here because it will @@ -567,7 +544,6 @@ bool object_resurrection_forbidden_ = false; bool in_atomic_pause_ = false; bool sweep_forbidden_ = false; - bool wrapper_tracing_ = false; bool incremental_marking_ = false; bool should_optimize_for_load_time_ = false; size_t no_allocation_count_ = 0; @@ -588,10 +564,8 @@ // for an object, by processing the ordered_pre_finalizers_ back-to-front. LinkedHashSet<PreFinalizer> ordered_pre_finalizers_; - v8::Isolate* isolate_; - void (*trace_dom_wrappers_)(v8::Isolate*, Visitor*); - void (*invalidate_dead_objects_in_wrappers_marking_deque_)(v8::Isolate*); - void (*perform_cleanup_)(v8::Isolate*); + v8::Isolate* isolate_ = nullptr; + void (*trace_dom_wrappers_)(v8::Isolate*, Visitor*) = nullptr; #if defined(ADDRESS_SANITIZER) void* asan_fake_stack_;
diff --git a/third_party/blink/renderer/platform/heap/trace_traits.h b/third_party/blink/renderer/platform/heap/trace_traits.h index ca13ce1..b5e315e 100644 --- a/third_party/blink/renderer/platform/heap/trace_traits.h +++ b/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_TRACE_TRAITS_H_ #include "base/optional.h" -#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h" #include "third_party/blink/renderer/platform/heap/gc_info.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/visitor.h"
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.h b/third_party/blink/renderer/platform/heap/unified_heap_controller.h index 410acf3..e22350968 100644 --- a/third_party/blink/renderer/platform/heap/unified_heap_controller.h +++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.h
@@ -33,11 +33,6 @@ DISALLOW_IMPLICIT_CONSTRUCTORS(UnifiedHeapController); public: - // Temporarily expose that logic to allow reuse by - // ScriptWrappableMarkingVisitor. - static bool IsRootForNonTracingGCInternal( - const v8::TracedGlobal<v8::Value>& handle); - explicit UnifiedHeapController(ThreadState*); // v8::EmbedderHeapTracer implementation. @@ -52,6 +47,9 @@ ThreadState* thread_state() const { return thread_state_; } private: + static bool IsRootForNonTracingGCInternal( + const v8::TracedGlobal<v8::Value>& handle); + ThreadState* const thread_state_; // Returns whether the Blink heap has been fully processed.
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc index 8d04c950..a90a7591 100644 --- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc +++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -260,7 +260,8 @@ } void RawResource::ResponseBodyReceived( - ResponseBodyLoaderDrainableInterface& body_loader) { + ResponseBodyLoaderDrainableInterface& body_loader, + scoped_refptr<base::SingleThreadTaskRunner> loader_task_runner) { DCHECK_LE(Clients().size(), 1u); RawResourceClient* client = ResourceClientWalker<RawResourceClient>(Clients()).Next();
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h index 93bde4e..afd8b23 100644 --- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h +++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -121,7 +121,9 @@ } void WillNotFollowRedirect() override; void ResponseReceived(const ResourceResponse&) override; - void ResponseBodyReceived(ResponseBodyLoaderDrainableInterface&) override; + void ResponseBodyReceived( + ResponseBodyLoaderDrainableInterface&, + scoped_refptr<base::SingleThreadTaskRunner> loader_task_runner) override; void DidSendData(uint64_t bytes_sent, uint64_t total_bytes_to_be_sent) override; void DidDownloadData(uint64_t) override;
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc index e069aa81..884a91b 100644 --- a/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc +++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc
@@ -260,7 +260,7 @@ raw->MatchPreload(params, platform_->test_task_runner().get()); raw->AddClient(dummy_client, platform_->test_task_runner().get()); - raw->ResponseBodyReceived(*body_loader); + raw->ResponseBodyReceived(*body_loader, platform_->test_task_runner()); raw->FinishForTest(); EXPECT_FALSE(dummy_client->Called());
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h index c22b9330..213b81e 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -267,7 +267,8 @@ virtual void ResponseReceived(const ResourceResponse&); virtual void ResponseBodyReceived( - ResponseBodyLoaderDrainableInterface& body_loader) {} + ResponseBodyLoaderDrainableInterface& body_loader, + scoped_refptr<base::SingleThreadTaskRunner> loader_task_runner) {} void SetResponse(const ResourceResponse&); const ResourceResponse& GetResponse() const { return response_; }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc index 6ecc749..d61cb18 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -466,7 +466,8 @@ response_body_loader_ = MakeGarbageCollected<ResponseBodyLoader>( bytes_consumer, response_body_loader_client, task_runner_for_body_loader_); - resource_->ResponseBodyReceived(*response_body_loader_); + resource_->ResponseBodyReceived(*response_body_loader_, + task_runner_for_body_loader_); if (response_body_loader_->IsDrained()) { // When streaming, unpause virtual time early to prevent deadlocking // against stream consumer in case stream has backpressure enabled.
diff --git a/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc b/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc index efd52daa..67b20abd 100644 --- a/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc +++ b/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc
@@ -211,4 +211,13 @@ EqualIgnoringASCIICase(mime_type, "image/pjpeg"); } +bool MIMETypeRegistry::IsLosslessImageMIMEType(const String& mime_type) { + return EqualIgnoringASCIICase(mime_type, "image/bmp") || + EqualIgnoringASCIICase(mime_type, "image/gif") || + EqualIgnoringASCIICase(mime_type, "image/png") || + EqualIgnoringASCIICase(mime_type, "image/webp") || + EqualIgnoringASCIICase(mime_type, "image/x-xbitmap") || + EqualIgnoringASCIICase(mime_type, "image/x-png"); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/network/mime/mime_type_registry.h b/third_party/blink/renderer/platform/network/mime/mime_type_registry.h index 8b54b9f..6d10852c 100644 --- a/third_party/blink/renderer/platform/network/mime/mime_type_registry.h +++ b/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
@@ -108,6 +108,11 @@ // size will be restricted via the 'unoptimized-lossy-images' feature // policy. (JPEG) static bool IsLossyImageMIMEType(const String& mime_type); + + // Checks to see if a mime type is an image type with lossless (or no) + // compression, whose size may be restricted via the + // 'unoptimized-lossless-images' feature policy. (BMP, GIF, PNG, WEBP) + static bool IsLosslessImageMIMEType(const String& mime_type); }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/third_party/blink/renderer/platform/testing/testing_platform_support.cc index f44a0dc..6843b76068 100644 --- a/third_party/blink/renderer/platform/testing/testing_platform_support.cc +++ b/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -180,8 +180,7 @@ ProcessHeap::Init(); ThreadState::AttachMainThread(); - ThreadState::Current()->RegisterTraceDOMWrappers(nullptr, nullptr, nullptr, - nullptr); + ThreadState::Current()->RegisterTraceDOMWrappers(nullptr, nullptr); http_names::Init(); fetch_initiator_type_names::Init();
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 81f325f..89de3be 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -254,7 +254,7 @@ Bug(none) virtual/threaded/fast/scroll-behavior/first-scroll-runs-on-compositor.html [ Failure ] Bug(none) virtual/threaded/compositing/visibility/layer-visible-content.html [ Failure ] Bug(none) virtual/threaded/compositing/visibility/visibility-image-layers-dynamic.html [ Failure ] -Bug(none) virtual/threaded/synthetic_gestures/smooth-scroll-tiny-delta.html [ Failure ] +Bug(none) virtual/threaded/synthetic_gestures/smooth-scroll-tiny-delta.html [ Failure Timeout ] Bug(none) fast/block/float/float-change-composited-scrolling.html [ Failure ] @@ -400,6 +400,15 @@ crbug.com/924680 virtual/threaded/fast/events/pinch/pinch-zoom-into-center.html [ Failure ] crbug.com/924680 virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad.html [ Failure ] +# These were introduced when we started setting the scrolls_outer_viewport bit on ScrollNodes +Bug(none) virtual/fractional_scrolling_threaded/fast/scrolling/hover-during-scroll.html [ Failure ] +Bug(none) virtual/fractional_scrolling_threaded/fast/scrolling/no-hover-during-scroll.html [ Failure ] +Bug(none) virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html [ Failure ] +Bug(none) virtual/threaded/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html [ Failure ] +Bug(none) virtual/threaded/fast/scrolling/hover-during-scroll.html [ Failure ] +Bug(none) virtual/threaded/fast/scrolling/no-hover-during-scroll.html [ Failure ] +Bug(none) virtual/threaded/synthetic_gestures/animated-wheel-tiny-delta.html [ Failure ] + # Backdrop filter crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index e2c3b56..6eaa0558 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -3562,9 +3562,6 @@ crbug.com/626703 external/wpt/css/css-scoping/shadow-directionality-002.tentative.html [ Failure ] crbug.com/626703 external/wpt/css/css-scoping/shadow-directionality-001.tentative.html [ Failure ] crbug.com/626703 external/wpt/screen-orientation/orientation-reading.html [ Timeout ] -crbug.com/626703 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_parent [ Timeout ] -crbug.com/626703 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_top [ Timeout ] -crbug.com/626703 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_self [ Failure ] crbug.com/626703 external/wpt/html/browsers/browsing-the-web/unloading-documents/prompt-and-unload-script-closeable.html [ Timeout ] crbug.com/626703 external/wpt/css/css-animations/CSSAnimation-effect.tentative.html [ Timeout ] crbug.com/626703 external/wpt/css/css-animations/event-dispatch.tentative.html [ Timeout ] @@ -3907,7 +3904,6 @@ crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/logical-physical-mapping-001.html [ Failure ] crbug.com/626703 external/wpt/encoding/eof-utf-8-three.html [ Failure ] crbug.com/626703 external/wpt/encoding/eof-utf-8-two.html [ Failure ] -crbug.com/626703 external/wpt/html/browsers/the-window-object/window-open-noopener.html [ Timeout ] crbug.com/626703 external/wpt/html/browsers/windows/noreferrer-window-name.html [ Timeout ] crbug.com/626703 crbug.com/930297 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html [ Timeout Crash ] crbug.com/626703 crbug.com/930297 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html [ Timeout Crash ] @@ -5995,9 +5991,6 @@ crbug.com/932078 virtual/insecure-device-sensor-events/http/tests/security/powerfulFeatureRestrictions/device-orientation-handler-not-fired-on-insecure-origin.html [ Skip ] crbug.com/932078 virtual/outofblink-cors/http/tests/security/powerfulFeatureRestrictions/device-orientation-on-insecure-origin.html [ Skip ] -# Wasm threads enabled by default -crbug.com/754910 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-disabled.html [ Skip ] - # These tests depend on targeting javascript: url navigations at the specified window. crbug.com/935064 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html [ Failure ] crbug.com/935064 external/wpt/content-security-policy/inheritance/iframe-all-local-schemes.sub.html [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html b/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html index 59d655f..7d2d059 100644 --- a/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html +++ b/third_party/blink/web_tests/accessibility/contenteditable-caret-position.html
@@ -16,6 +16,12 @@ <p id="paragraph2">Line 3</p> </div> + <div id="contenteditable-div-2" contenteditable role="textbox" + style="max-width: 5px; overflow-wrap: normal;"> + Line 1<br> + Line 2 + </div> + </div> <script> @@ -45,7 +51,9 @@ textbox.focus(); let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox"); + assert_equals(textboxAccessible.selectionAnchorObject, textboxAccessible); assert_equals(textboxAccessible.selectionAnchorOffset, 0); + assert_equals(textboxAccessible.selectionFocusObject, textboxAccessible); assert_equals(textboxAccessible.selectionFocusOffset, 0); }, "Moving the focus to an ARIA textbox should place the caret at its beginning."); @@ -229,58 +237,153 @@ test_after_layout_and_paint(function() { - let selection = window.getSelection(); - let selectionRange = document.createRange(); - let mainAccessible = accessibilityController.accessibleElementById("main"); - let rootAccessible = accessibilityController.rootElement; + const selection = window.getSelection(); + const selectionRange = document.createRange(); + const mainAccessible = accessibilityController.accessibleElementById("main"); + const rootAccessible = accessibilityController.rootElement; - let contenteditable = document.getElementById("contenteditable-div"); + const contenteditable = document.getElementById("contenteditable-div"); contenteditable.focus(); - // The offset from the beginning of the main div to the first character of - // contenteditable-div. - var mainOffset = 9; // The offset from the newline character between the two lines of the // first paragraph to the first character of its second line. // (Needed for skipping wide space.) - let line2Offset = 13; + const line2StartOffset = 13; - let line1 = document.getElementById("paragraph1").firstChild; - let line2 = document.getElementById("paragraph1").lastChild; - let line3 = document.getElementById("paragraph2").firstChild; - let contenteditableLines = [ line1, line2, line3 ]; - let contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div"); + const line1 = document.getElementById("paragraph1").firstChild; + const line2 = document.getElementById("paragraph1").lastChild; + const line3 = document.getElementById("paragraph2").firstChild; + const contenteditableLines = [ line1, line2, line3 ]; + + const contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div"); + const paragraph1Accessible = accessibilityController.accessibleElementById("paragraph1"); + const paragraph2Accessible = accessibilityController.accessibleElementById("paragraph2"); + const line1Accessible = paragraph1Accessible.childAtIndex(0); + const line2Accessible = paragraph1Accessible.childAtIndex(2); + const line3Accessible = paragraph2Accessible.childAtIndex(0); + const expectations = [ + line1Accessible, line2Accessible, line3Accessible + ]; for (let lineNumber = 0; lineNumber < 3; ++lineNumber) { - let lineOffset = lineNumber * 7; - // Paragraphs should be separated by an empty line. - if (lineNumber == 2) - ++lineOffset; - for (let characterOffset = 0; characterOffset < 7; ++characterOffset) { - // Any widespace in the DOM should be stripped out. + // Any widespace in the DOM should be stripped out in the + // accessibility tree. let selectionOffset = characterOffset; if (lineNumber == 1) - selectionOffset += line2Offset; + selectionOffset += line2StartOffset; selectionRange.setStart(contenteditableLines[lineNumber], selectionOffset); selectionRange.setEnd(contenteditableLines[lineNumber], selectionOffset); selection.removeAllRanges(); selection.addRange(selectionRange); + assert_equals(contenteditableAccessible.selectionAnchorObject, expectations[lineNumber]); assert_equals(contenteditableAccessible.selectionAnchorOffset, characterOffset); + assert_equals(contenteditableAccessible.selectionFocusObject, expectations[lineNumber]); assert_equals(contenteditableAccessible.selectionFocusOffset, characterOffset); + assert_equals(mainAccessible.selectionAnchorObject, expectations[lineNumber]); assert_equals(mainAccessible.selectionAnchorOffset, characterOffset); + assert_equals(mainAccessible.selectionFocusObject, expectations[lineNumber]); assert_equals(mainAccessible.selectionFocusOffset, characterOffset); - assert_equals(rootAccessible.selectionAnchorObject.name, - contenteditableLines[lineNumber].textContent.trim()); + assert_equals(rootAccessible.selectionAnchorObject, expectations[lineNumber]); assert_equals(rootAccessible.selectionAnchorOffset, characterOffset); - assert_equals(rootAccessible.selectionFocusObject.name, - contenteditableLines[lineNumber].textContent.trim()); + assert_equals(rootAccessible.selectionFocusObject, expectations[lineNumber]); assert_equals(rootAccessible.selectionFocusOffset, characterOffset); } } - }, "Test moving the caret across two paragraphs."); + }, "Test moving the caret across two paragraphs by re-creating the selection."); + + test_after_layout_and_paint(function() + { + const selection = window.getSelection(); + const selectionRange = document.createRange(); + const mainAccessible = accessibilityController.accessibleElementById("main"); + const rootAccessible = accessibilityController.rootElement; + + const contenteditable = document.getElementById("contenteditable-div"); + contenteditable.focus(); + + const line1 = document.getElementById("paragraph1").firstChild; + selectionRange.setStart(line1, 0); + selectionRange.setEnd(line1, 0); + selection.removeAllRanges(); + selection.addRange(selectionRange); + + const contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div"); + const paragraph1Accessible = accessibilityController.accessibleElementById("paragraph1"); + const paragraph2Accessible = accessibilityController.accessibleElementById("paragraph2"); + const line1Accessible = paragraph1Accessible.childAtIndex(0); + const line2Accessible = paragraph1Accessible.childAtIndex(2); + const line3Accessible = paragraph2Accessible.childAtIndex(0); + const expectations = [ + line1Accessible, line2Accessible, line3Accessible + ]; + + for (let lineNumber = 0; lineNumber < 3; ++lineNumber) { + for (let characterOffset = 0; characterOffset < 7; ++characterOffset) { + assert_equals(contenteditableAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(contenteditableAccessible.selectionAnchorOffset, characterOffset); + assert_equals(contenteditableAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(contenteditableAccessible.selectionFocusOffset, characterOffset); + + assert_equals(mainAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(mainAccessible.selectionAnchorOffset, characterOffset); + assert_equals(mainAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(mainAccessible.selectionFocusOffset, characterOffset); + + assert_equals(rootAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(rootAccessible.selectionAnchorOffset, characterOffset); + assert_equals(rootAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(rootAccessible.selectionFocusOffset, characterOffset); + + selection.modify('move', 'forward', 'character'); + } + } + + }, "Test moving the caret across two paragraphs by modifying the existing selection."); + + test_after_layout_and_paint(function() + { + const selection = window.getSelection(); + const selectionRange = document.createRange(); + const mainAccessible = accessibilityController.accessibleElementById("main"); + const rootAccessible = accessibilityController.rootElement; + + const contenteditable = document.getElementById('contenteditable-div-2'); + contenteditable.focus(); + selectionRange.setStart(contenteditable, 0); + selectionRange.setEnd(contenteditable, 0); + selection.removeAllRanges(); + selection.addRange(selectionRange); + + const contenteditableAccessible = accessibilityController.accessibleElementById('contenteditable-div-2'); + const line1Accessible = contenteditableAccessible.childAtIndex(0); + const line2Accessible = contenteditableAccessible.childAtIndex(2); + const expectations = [ line1Accessible, line2Accessible ]; + + for (let lineNumber = 0; lineNumber < 2; ++lineNumber) { + for (let characterOffset = 0; characterOffset < 7; ++characterOffset) { + assert_equals(contenteditableAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(contenteditableAccessible.selectionAnchorOffset, characterOffset); + assert_equals(contenteditableAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(contenteditableAccessible.selectionFocusOffset, characterOffset); + + assert_equals(mainAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(mainAccessible.selectionAnchorOffset, characterOffset); + assert_equals(mainAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(mainAccessible.selectionFocusOffset, characterOffset); + + assert_equals(rootAccessible.selectionAnchorObject, expectations[lineNumber]); + assert_equals(rootAccessible.selectionAnchorOffset, characterOffset); + assert_equals(rootAccessible.selectionFocusObject, expectations[lineNumber]); + assert_equals(rootAccessible.selectionFocusOffset, characterOffset); + + selection.modify('move', 'forward', 'character'); + } + } + + }, "Test moving the caret across two lines that wrap by modifying the existing selection."); </script>
diff --git a/third_party/blink/web_tests/accessibility/contenteditable-selection.html b/third_party/blink/web_tests/accessibility/contenteditable-selection.html index 16a92ca..e2e2e90 100644 --- a/third_party/blink/web_tests/accessibility/contenteditable-selection.html +++ b/third_party/blink/web_tests/accessibility/contenteditable-selection.html
@@ -27,8 +27,6 @@ let textbox = document.getElementById("contenteditable-textbox"); let textboxAccessible = accessibilityController.accessibleElementById("contenteditable-textbox"); - let line1Accessible = accessibilityController.accessibleElementById("contenteditable-line1"); - let line1TextAccessible = line1Accessible.childAtIndex(0); // Select the entire contents of the outer ARIA textbox. // These include another ARIA textbox and a textarea node @@ -38,21 +36,22 @@ selection.removeAllRanges(); selection.addRange(selectionRange); + assert_equals(textboxAccessible.selectionAnchorObject, textboxAccessible); assert_equals(textboxAccessible.selectionAnchorOffset, 0); - // 7 for the "Line 1" text div + 1 for the textarea node. - // (The textarea node should be treated as a single unit.) - assert_equals(textboxAccessible.selectionFocusOffset, 8); + assert_equals(textboxAccessible.selectionFocusObject, textboxAccessible); + // Two nodes have been selected: the two text fields inside the outer one. + assert_equals(textboxAccessible.selectionFocusOffset, 2); // Selection offsets should be the same when retrieved from the parent object. - assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible); + assert_equals(mainAccessible.selectionAnchorObject, textboxAccessible); assert_equals(mainAccessible.selectionAnchorOffset, 0); - assert_equals(mainAccessible.selectionAnchorObject, line1TextAccessible); - assert_equals(mainAccessible.selectionFocusOffset, 8); + assert_equals(mainAccessible.selectionFocusObject, textboxAccessible); + assert_equals(mainAccessible.selectionFocusOffset, 2); - assert_equals(rootAccessible.selectionAnchorObject, line1TextAccessible); + assert_equals(rootAccessible.selectionAnchorObject, textboxAccessible); assert_equals(rootAccessible.selectionAnchorOffset, 0); assert_equals(rootAccessible.selectionFocusObject, textboxAccessible); - assert_equals(rootAccessible.selectionFocusOffset, 8); + assert_equals(rootAccessible.selectionFocusOffset, 2); }, "Test selectNodeContents on an ARIA textbox."); test_after_layout_and_paint(() => @@ -73,18 +72,20 @@ selection.removeAllRanges(); selection.addRange(selectionRange); + assert_equals(contenteditableAccessible.selectionAnchorObject, contenteditableAccessible); assert_equals(contenteditableAccessible.selectionAnchorOffset, 0); - assert_equals(contenteditableAccessible.selectionFocusOffset, 6); + assert_equals(contenteditableAccessible.selectionFocusObject, contenteditableAccessible); + assert_equals(contenteditableAccessible.selectionFocusOffset, 2); + assert_equals(mainAccessible.selectionAnchorObject, contenteditableAccessible); assert_equals(mainAccessible.selectionAnchorOffset, 0); - assert_equals(mainAccessible.selectionFocusOffset, 6); + assert_equals(mainAccessible.selectionFocusObject, contenteditableAccessible); + assert_equals(mainAccessible.selectionFocusOffset, 2); - assert_equals(rootAccessible.selectionAnchorObject.name, - line1.textContent); + assert_equals(rootAccessible.selectionAnchorObject, contenteditableAccessible); assert_equals(rootAccessible.selectionAnchorOffset, 0); - assert_equals(rootAccessible.selectionFocusObject.name, - line3.textContent); - assert_equals(rootAccessible.selectionFocusOffset, 6); + assert_equals(rootAccessible.selectionFocusObject, contenteditableAccessible); + assert_equals(rootAccessible.selectionFocusOffset, 2); }, "Test selectNodeContents on a contenteditable."); test_after_layout_and_paint(() => @@ -216,28 +217,40 @@ let line2 = document.getElementById("paragraph1").lastChild; let line3 = document.getElementById("paragraph2").firstChild; let contenteditableLines = [ line1, line2, line3 ]; + let contenteditableAccessible = accessibilityController.accessibleElementById("contenteditable-div"); + let paragraph1Accessible = accessibilityController.accessibleElementById("paragraph1"); + let paragraph2Accessible = accessibilityController.accessibleElementById("paragraph2"); + let line1Accessible = paragraph1Accessible.childAtIndex(0); + let line2Accessible = paragraph1Accessible.childAtIndex(2); + let line3Accessible = paragraph2Accessible.childAtIndex(0); + let expectations = [ + [ line1Accessible, 0, paragraph1Accessible, 1 ], + [ line2Accessible, 0, paragraph1Accessible, 3 ], + [ line3Accessible, 0, paragraph2Accessible, 1 ], + ]; // Select entire lines in the second content editable. for (let testCase = 0; testCase < 2; ++testCase) { - for (let i = 0; i < contenteditableLines.length; ++i) { selectionRange.selectNode(contenteditableLines[i]); selection.removeAllRanges(); selection.addRange(selectionRange); - assert_equals(contenteditableAccessible.selectionAnchorOffset, 0); - assert_equals(contenteditableAccessible.selectionFocusOffset, 6); + assert_equals(contenteditableAccessible.selectionAnchorObject, expectations[i][0]); + assert_equals(contenteditableAccessible.selectionAnchorOffset, expectations[i][1]); + assert_equals(contenteditableAccessible.selectionFocusObject, expectations[i][2]); + assert_equals(contenteditableAccessible.selectionFocusOffset, expectations[i][3]); - assert_equals(mainAccessible.selectionAnchorOffset, 0); - assert_equals(mainAccessible.selectionFocusOffset, 6); + assert_equals(mainAccessible.selectionAnchorObject, expectations[i][0]); + assert_equals(mainAccessible.selectionAnchorOffset, expectations[i][1]); + assert_equals(mainAccessible.selectionFocusObject, expectations[i][2]); + assert_equals(mainAccessible.selectionFocusOffset, expectations[i][3]); - assert_equals(rootAccessible.selectionAnchorObject.name, - contenteditableLines[i].textContent); - assert_equals(rootAccessible.selectionAnchorOffset, 0); - assert_equals(rootAccessible.selectionFocusObject.name, - contenteditableLines[i].textContent); - assert_equals(rootAccessible.selectionFocusOffset, 6); + assert_equals(rootAccessible.selectionAnchorObject, expectations[i][0]); + assert_equals(rootAccessible.selectionAnchorOffset, expectations[i][1]); + assert_equals(rootAccessible.selectionFocusObject, expectations[i][2]); + assert_equals(rootAccessible.selectionFocusOffset, expectations[i][3]); } // For a sanity check, try the same test with contenteditable="false".
diff --git a/third_party/blink/web_tests/accessibility/list-with-selection.html b/third_party/blink/web_tests/accessibility/list-with-selection.html index 9c051d04..f7a6af32 100644 --- a/third_party/blink/web_tests/accessibility/list-with-selection.html +++ b/third_party/blink/web_tests/accessibility/list-with-selection.html
@@ -35,11 +35,11 @@ // Select both items in the list. axList.setSelectedTextRange(0, 2); - let selectionText = 'Item1\nItem2'; assert_equals(axList.selectionAnchorOffset, 0, id); - assert_equals(axList.selectionFocusOffset, 5, id); + assert_equals(axList.selectionFocusOffset, 2, id); let selection = window.getSelection(); + let selectionText = 'Item1\nItem2'; assert_equals(selection.toString(), selectionText, id); if (window.testRunner)
diff --git a/third_party/blink/web_tests/accessibility/selection-affinity.html b/third_party/blink/web_tests/accessibility/selection-affinity.html index dc1a25e..737fb0e 100644 --- a/third_party/blink/web_tests/accessibility/selection-affinity.html +++ b/third_party/blink/web_tests/accessibility/selection-affinity.html
@@ -65,7 +65,9 @@ var axRoot = accessibilityController.rootElement; assert_equals(axRoot.selectionAnchorObject.role, "AXRole: AXStaticText"); assert_equals(axRoot.selectionAnchorObject.name, "a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z"); + assert_equals(axRoot.selectionFocusOffset, endCharIndex - startCharIndex); assert_equals(axRoot.selectionAnchorOffset, endCharIndex - startCharIndex); + assert_equals(axRoot.selectionFocusAffinity, "upstream"); assert_equals(axRoot.selectionAnchorAffinity, "upstream"); // Click to place the cursor at the beginning of the next line. @@ -83,6 +85,8 @@ assert_equals(axRoot.selectionAnchorObject.role, "AXRole: AXStaticText"); assert_equals(axRoot.selectionAnchorObject.name, "a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z"); assert_equals(axRoot.selectionAnchorOffset, endCharIndex - startCharIndex); + assert_equals(axRoot.selectionFocusOffset, endCharIndex - startCharIndex); assert_equals(axRoot.selectionAnchorAffinity, "downstream"); + assert_equals(axRoot.selectionFocusAffinity, "downstream"); }, "Test upstream and downstream affinity of text selection."); </script>
diff --git a/third_party/blink/web_tests/accessibility/set-selection-child-offset.html b/third_party/blink/web_tests/accessibility/set-selection-child-offset.html index d151d9f3..127aa76 100644 --- a/third_party/blink/web_tests/accessibility/set-selection-child-offset.html +++ b/third_party/blink/web_tests/accessibility/set-selection-child-offset.html
@@ -3,132 +3,79 @@ <script src="../resources/testharnessreport.js"></script> <script src="../resources/run-after-layout-and-paint.js"></script> -<div role="region"> - <span role="presentation" id="span1">this is a<a id="link" href="#1">test</a></span> - <span role="presentation" id="span2">of selection</span> +<div role="region" id="region"> + <span role="none" id="span1">This is a<a id="link" href="#1">test</a></span> + <span role="none" id="span2">of selection.</span> </div> <script> - var sel = null; - function updateSelectedRangeFromPage() { - sel = window.getSelection().getRangeAt(0); - } + function verifySelection(anchorNode, anchorOffset, focusNode, focusOffset, selectionString) { + const selection = getSelection(); + assert_equals(selection.anchorNode, anchorNode, 'anchorNode'); + assert_equals(selection.anchorOffset, anchorOffset, 'anchorOffset'); + assert_equals(selection.focusNode, focusNode, 'focusNode'); + assert_equals(selection.focusOffset, focusOffset, 'focusOffset'); + assert_equals(selection.toString(), selectionString, 'getSelection.toString()'); + } - // The accessibility tree contains a region with 3 children, select each. - test_after_layout_and_paint(function() - { - assert_not_equals(window.accessibilityController, undefined, 'This test requires accessibilityController'); + setup(() => { + window.axRegion = accessibilityController.accessibleElementById('region'); + window.span1 = document.querySelector('span'); + window.text1 = span1.firstChild; + window.span2 = document.querySelectorAll('span')[1]; + window.text2 = span2.firstChild; + window.link = document.querySelector('a'); + window.linkText = link.firstChild; + window.lineBreak = span1.nextSibling; + }); - var axRegion = accessibilityController.accessibleElementById("link").parentElement(); - assert_equals(axRegion.role, "AXRole: AXRegion"); + test_after_layout_and_paint(() => { + axRegion.setSelection(axRegion, 0, axRegion, 0); + verifySelection(text1, 0, text1, 0, ''); + }, 'Test creating a collapsed selection before the first character of the first span.'); - // An insertion point before the first child. - axRegion.setSelection(axRegion, 0, axRegion, 0); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), ""); - assert_true(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "this is a"); + test(() => { + axRegion.setSelection(axRegion, 0, axRegion, 1); + verifySelection(text1, 0, span1, 1, 'This is a'); + }, 'Test creating a selection around the text in the first span.'); - // A straight-forward selection of the first child. - axRegion.setSelection(axRegion, 0, axRegion, 1); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "this is a"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "this is a"); - assert_equals(sel.endOffset, 9); - assert_equals(sel.endContainer.textContent, "this is a"); + test(() => { + axRegion.setSelection(axRegion, 1, axRegion, 1); + verifySelection(span1, 1, span1, 1, ''); + }, 'Test creating a collapsed selection before the link.'); - // Another insertion point after the first child, before the second. - axRegion.setSelection(axRegion, 1, axRegion, 1); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), ""); - assert_true(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.startOffset, 9); - assert_equals(sel.startContainer.textContent, "this is a"); + test(() => { + axRegion.setSelection(axRegion, 1, axRegion, 2); + verifySelection(span1, 1, lineBreak, 0, 'test'); + }, 'Test creating a selection around the link.'); - // Select the second child. - axRegion.setSelection(axRegion, 1, axRegion, 3); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "test\n"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "test"); - assert_equals(sel.startContainer.compareDocumentPosition(sel.endContainer), 4 /* before */); - assert_equals(sel.endOffset, 1); - assert_equals(sel.endContainer.textContent, "\n "); + test(() => { + axRegion.setSelection(axRegion, 3, axRegion, 3); + verifySelection(text2, 0, text2, 0, ''); + }, 'Test creating a collapsed selection before the second span.'); - // Next, another insertion point between second and third child. - axRegion.setSelection(axRegion, 3, axRegion, 3); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), ""); - assert_true(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.startOffset, 1); - assert_equals(sel.startContainer.textContent, "\n "); + test(() => { + axRegion.setSelection(axRegion, 3, axRegion, 4); + verifySelection(text2, 0, span2, 1, 'of selection.'); + }, 'Test creating a selection around the second span.'); - // Select the third child. - axRegion.setSelection(axRegion, 3, axRegion, 4); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "of selection"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "of selection"); - assert_equals(sel.endOffset, 12); - assert_equals(sel.endContainer.textContent, "of selection"); + test(() => { + axRegion.setSelection(axRegion, 0, axRegion, 3); + verifySelection(text1, 0, text2, 0, 'This is atest '); + }, 'Test creating a selection from the first span and the link up to the second span.'); - // Select the first and second children. - axRegion.setSelection(axRegion, 0, axRegion, 3); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "this is atest\n"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "this is a"); - assert_equals(sel.endOffset, 1); - assert_equals(sel.endContainer.textContent, "\n "); + test(() => { + axRegion.setSelection(axRegion, 1, axRegion, 4); + verifySelection(span1, 1, span2, 1, 'test of selection.'); + }, 'Test creating a selection around the link and the second span.'); - // Select the second and third children. - axRegion.setSelection(axRegion, 1, axRegion, 4); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "test\n of selection"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "test"); - assert_equals(sel.endOffset, 12); - assert_equals(sel.endContainer.textContent, "of selection"); + test(() => { + axRegion.setSelection(axRegion, 0, axRegion, 4); + verifySelection(text1, 0, span2, 1, 'This is atest of selection.'); + }, 'Test creating a selection around both spans.'); - // Select everything. - axRegion.setSelection(axRegion, 0, axRegion, 4); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), "this is atest\n of selection"); - assert_false(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.endContainer.constructor, Text); - assert_equals(sel.startOffset, 0); - assert_equals(sel.startContainer.textContent, "this is a"); - assert_equals(sel.endOffset, 12); - assert_equals(sel.endContainer.textContent, "of selection"); - - // Last, the insertion point after third child. - axRegion.setSelection(axRegion, 4, axRegion, 4); - updateSelectedRangeFromPage(); - assert_equals(sel.toString(), ""); - assert_true(sel.collapsed); - assert_equals(sel.startContainer.constructor, Text); - assert_equals(sel.startOffset, 12); - assert_equals(sel.startContainer.textContent, "of selection"); - }, "Select child node at index."); + test(() => { + axRegion.setSelection(axRegion, 4, axRegion, 4); + verifySelection(span2, 1, span2, 1, ''); + }, 'Test creating a collapsed selection after the second span.'); </script>
diff --git a/third_party/blink/web_tests/accessibility/set-selection-link.html b/third_party/blink/web_tests/accessibility/set-selection-link.html index 07e06478..a528e38 100644 --- a/third_party/blink/web_tests/accessibility/set-selection-link.html +++ b/third_party/blink/web_tests/accessibility/set-selection-link.html
@@ -3,27 +3,25 @@ <script src="../resources/testharnessreport.js"></script> <script src="../resources/run-after-layout-and-paint.js"></script> -<div id="main" role="main"> - - <p id="para">This<br> is a<a href="#g">test</a>of selection</p> - -</div> +<p id="para">This<br> is a<a href="#g">test</a>of selection</p> <script> - test_after_layout_and_paint(function() - { - assert_not_equals(window.accessibilityController, undefined, 'This test requires accessibilityController'); + test_after_layout_and_paint(() => { + const lastText = document.getElementById('para').lastChild; + assert_class_string(lastText, 'Text'); - var axPara = accessibilityController.accessibleElementById("para"); - var axLastText = axPara.childAtIndex(4); - assert_equals(axLastText.role, "AXRole: AXStaticText"); - assert_equals(axLastText.name, "of selection"); + const axPara = accessibilityController.accessibleElementById('para'); + const axLastText = axPara.childAtIndex(4); + assert_equals(axLastText.role, 'AXRole: AXStaticText'); + assert_equals(axLastText.name, 'of selection'); - axLastText.setSelectedTextRange(0, 2); + axLastText.setSelection(axLastText, 0, axLastText, 2); - var selection = window.getSelection(); - var range = selection.getRangeAt(0); - - assert_equals(range.toString(), "of"); - }, "Select text after a link."); + const selection = getSelection(); + assert_equals(selection.anchorNode, lastText, 'anchorNode'); + assert_equals(selection.anchorOffset, 0, 'anchorOffset'); + assert_equals(selection.focusNode, lastText, 'focusNode'); + assert_equals(selection.focusOffset, 2, 'focusOffset'); + assert_equals(selection.toString(), 'of', 'getSelection().toString()'); + }, 'Select text after a link.'); </script>
diff --git a/third_party/blink/web_tests/accessibility/set-selection-whitespace.html b/third_party/blink/web_tests/accessibility/set-selection-whitespace.html index f9d9fa8..9706d0f5 100644 --- a/third_party/blink/web_tests/accessibility/set-selection-whitespace.html +++ b/third_party/blink/web_tests/accessibility/set-selection-whitespace.html
@@ -3,71 +3,78 @@ <script src="../resources/testharnessreport.js"></script> <script src="../resources/run-after-layout-and-paint.js"></script> -<div id="main" role="main"> +<div id="editable1" contenteditable="true">Hello1</div> - <div id="editable1" contenteditable="true">Hello1</div> +<div id="editable2" contenteditable="true"> Hello2 </div> - <div id="editable2" contenteditable="true"> Hello2 </div> - - <h2 id="heading"> Hello2 </h2> - -</div> +<h2 id="heading"> Hello2 </h2> <script> - test_after_layout_and_paint(function() - { - var axEditable1 = accessibilityController.accessibleElementById("editable1"); - var axTextNode1 = axEditable1.childAtIndex(0); - assert_equals(axTextNode1.name, "Hello1"); - axTextNode1.setSelectedTextRange(0, 6); + test_after_layout_and_paint(() => { + const editable1 = document.getElementById('editable1'); + assert_class_string(editable1, 'HTMLDivElement'); + const textNode1 = editable1.firstChild; + assert_class_string(textNode1, 'Text'); - var selection = window.getSelection(); - var range = selection.getRangeAt(0); - assert_equals(range.toString(), "Hello1"); + const axEditable1 = accessibilityController.accessibleElementById('editable1'); + const axTextNode1 = axEditable1.childAtIndex(0); + assert_equals(axTextNode1.role, 'AXRole: AXStaticText'); + assert_equals(axTextNode1.name, 'Hello1'); - var editable1 = document.getElementById("editable1"); - var textNode1 = editable1.firstChild; - assert_equals(range.startContainer, textNode1); - assert_equals(range.startOffset, 0); - assert_equals(range.endContainer, textNode1); - assert_equals(range.endOffset, 6); - }, "Using accessible APIs to set selection works when there's no extra whitespace."); + axTextNode1.setSelection(axTextNode1, 0, axTextNode1, 6); - test_after_layout_and_paint(function() - { - var axEditable2 = accessibilityController.accessibleElementById("editable2"); - var axTextNode2 = axEditable2.childAtIndex(0); - assert_equals(axTextNode2.name, "Hello2"); - axTextNode2.setSelectedTextRange(0, 6); + const selection = getSelection(); + // "Before object" positions at a text node are always adjusted to be before + // the first character of the text node itself. + // This ensures that to otherwise identical AX positions will have the same + // DOM position. + assert_equals(selection.anchorNode, textNode1, 'anchorNode'); + assert_equals(selection.anchorOffset, 0, 'anchorOffset'); + assert_equals(selection.focusNode, textNode1, 'focusNode'); + assert_equals(selection.focusOffset, 6, 'focusOffset'); + assert_equals(selection.toString(), 'Hello1', 'getSelection.toString()'); + }, 'Using accessible APIs to set selection works when there is no extra whitespace.'); - var selection = window.getSelection(); - var range = selection.getRangeAt(0); - assert_equals(range.toString(), "Hello2"); +test_after_layout_and_paint(() => { + const editable2 = document.getElementById('editable2'); + assert_class_string(editable2, 'HTMLDivElement'); + const textNode2 = editable2.firstChild; + assert_class_string(textNode2, 'Text'); - var editable2 = document.getElementById("editable2"); - var textNode2 = editable2.firstChild; - assert_equals(range.startContainer, textNode2); - assert_equals(range.startOffset, 2); - assert_equals(range.endContainer, textNode2); - assert_equals(range.endOffset, 8); - }, "Using accessible APIs to set selection works even with non-visible whitespace."); -</script> + const axEditable2 = accessibilityController.accessibleElementById('editable2'); + const axTextNode2 = axEditable2.childAtIndex(0); + assert_equals(axTextNode2.role, 'AXRole: AXStaticText'); + assert_equals(axTextNode2.name, 'Hello2'); -<script> - test_after_layout_and_paint(function() - { - var axHeading = accessibilityController.accessibleElementById("heading"); - axHeading.setSelectedTextRange(0, 1); // Should select the whole element. + axTextNode2.setSelection(axTextNode2, 0, axTextNode2, 6); - var selection = window.getSelection(); - var range = selection.getRangeAt(0); - assert_equals(range.toString(), "Hello2"); + const selection = getSelection(); + // Anchor and focus offsets are DOM base and are therefore adjusted to + // account for whitespace. + assert_equals(selection.anchorNode, textNode2, 'anchorNode'); + assert_equals(selection.anchorOffset, 2, 'anchorOffset'); + assert_equals(selection.focusNode, textNode2, 'focusNode'); + assert_equals(selection.focusOffset, 8, 'focusOffset'); + assert_equals(selection.toString(), 'Hello2', 'getSelection.toString()'); + }, 'Using accessible APIs to set selection works even with non-visible whitespace.'); - var heading = document.getElementById("heading"); - var textNode = heading.firstChild; - assert_equals(range.startContainer, textNode); - assert_equals(range.startOffset, 2); - assert_equals(range.endContainer, textNode); - assert_equals(range.endOffset, 8); - }, "Use accessible APIs to set selection on element rather than static text node."); + test_after_layout_and_paint(() => { + const heading = document.getElementById('heading'); + assert_class_string(heading, 'HTMLHeadingElement'); + const headingText = heading.firstChild; + assert_class_string(headingText, 'Text'); + + const axHeading = accessibilityController.accessibleElementById('heading'); + assert_equals(axHeading.role, 'AXRole: AXHeading'); + + // Select all the children in the heading. + axHeading.setSelection(axHeading, 0, axHeading, 1); + + const selection = getSelection(); + assert_equals(selection.anchorNode, headingText, 'anchorNode'); + assert_equals(selection.anchorOffset, 2, 'anchorOffset'); + assert_equals(selection.focusNode, heading, 'focusNode'); + assert_equals(selection.focusOffset, 1, 'focusOffset'); + assert_equals(selection.toString(), 'Hello2', 'getSelection.toString()'); + }, 'Use accessible APIs to set selection on all the children of an element rather than a static text node.'); </script>
diff --git a/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt b/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt index ef877e2..35a8a63 100644 --- a/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt +++ b/third_party/blink/web_tests/accessibility/textarea-caret-position-expected.txt
@@ -86,6 +86,10 @@ PASS textareaAccessible.selectionAnchorOffset is 20 PASS textareaAccessible is textareaAccessible.selectionFocusObject PASS textareaAccessible.selectionFocusOffset is 20 +PASS emptyTextareaAccessible is textareaAccessible.selectionAnchorObject +PASS textareaAccessible.selectionAnchorOffset is 0 +PASS emptyTextareaAccessible is textareaAccessible.selectionFocusObject +PASS textareaAccessible.selectionFocusOffset is 0 PASS textareaAccessible is textareaAccessible.selectionAnchorObject PASS textareaAccessible.selectionAnchorOffset is 20 PASS textareaAccessible is textareaAccessible.selectionFocusObject
diff --git a/third_party/blink/web_tests/accessibility/textarea-caret-position.html b/third_party/blink/web_tests/accessibility/textarea-caret-position.html index b1f72e6..db265126 100644 --- a/third_party/blink/web_tests/accessibility/textarea-caret-position.html +++ b/third_party/blink/web_tests/accessibility/textarea-caret-position.html
@@ -22,6 +22,8 @@ textarea.focus(); window.textareaAccessible = accessibilityController.accessibleElementById('textarea'); + window.emptyTextareaAccessible = + accessibilityController.accessibleElementById('textarea-empty'); for (let i = 0; i < 3; ++i) { for (let j = 0; j < 7; ++j) { @@ -39,14 +41,21 @@ let emptyTextarea = document.getElementById('textarea-empty'); emptyTextarea.focus(); - // Each textarea has its own independent caret. + // Each textarea remembers its own independent selection but + // textareas that are not focused don't expose their selection + // visually. + shouldBe("emptyTextareaAccessible", "textareaAccessible.selectionAnchorObject"); + shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 0); + shouldBe("emptyTextareaAccessible", "textareaAccessible.selectionFocusObject"); + shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 0); + + textarea.focus(); shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject"); shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 20); shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject"); shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 20); - window.emptyTextareaAccessible = - accessibilityController.accessibleElementById('textarea-empty'); + emptyTextarea.focus(); shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionAnchorObject"); shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset"); shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionFocusObject");
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt index 9c6f6ac..2e758649 100644 --- a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt +++ b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
@@ -30,6 +30,10 @@ PASS textareaAccessible.selectionAnchorOffset is 14 PASS textareaAccessible is textareaAccessible.selectionFocusObject PASS textareaAccessible.selectionFocusOffset is 21 +PASS emptyTextareaAccessible is textareaAccessible.selectionAnchorObject +PASS textareaAccessible.selectionAnchorOffset is 0 +PASS emptyTextareaAccessible is textareaAccessible.selectionFocusObject +PASS textareaAccessible.selectionFocusOffset is 0 PASS textareaAccessible is textareaAccessible.selectionAnchorObject PASS textareaAccessible.selectionAnchorOffset is 14 PASS textareaAccessible is textareaAccessible.selectionFocusObject
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection.html b/third_party/blink/web_tests/accessibility/textarea-selection.html index e90d515..e58317d7 100644 --- a/third_party/blink/web_tests/accessibility/textarea-selection.html +++ b/third_party/blink/web_tests/accessibility/textarea-selection.html
@@ -22,6 +22,8 @@ textarea.focus(); window.textareaAccessible = accessibilityController.accessibleElementById('textarea'); + window.emptyTextareaAccessible = + accessibilityController.accessibleElementById('textarea-empty'); // Select the entire contents. textarea.select(); @@ -53,15 +55,22 @@ } let emptyTextarea = document.getElementById('textarea-empty'); - emptyTextarea.focus(); - // Each textarea has its own independent selection. + emptyTextarea.focus(); + // Each textarea remembers its own independent selection but + // textareas that are not focused don't expose their selection + // visually. + shouldBe("emptyTextareaAccessible", "textareaAccessible.selectionAnchorObject"); + shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 0); + shouldBe("emptyTextareaAccessible", "textareaAccessible.selectionFocusObject"); + shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 0); + + textarea.focus(); shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject"); shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 14); shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject"); shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 21); - - window.emptyTextareaAccessible = - accessibilityController.accessibleElementById('textarea-empty'); + + emptyTextarea.focus(); shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionAnchorObject"); shouldBeZero("emptyTextareaAccessible.selectionAnchorOffset"); shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionFocusObject");
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json index 8de0fd9c..9e0866f 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -124820,11 +124820,6 @@ {} ] ], - "css/CSS2/floats/computed-float-position-absolute-expected.txt": [ - [ - {} - ] - ], "css/CSS2/floats/float-nowrap-1-notref.html": [ [ {} @@ -331463,10 +331458,6 @@ "59843ae54b64f6ce4f7e616d4be491c911ea84cf", "support" ], - "css/CSS2/floats/computed-float-position-absolute-expected.txt": [ - "f9b2c8a3ec6a6f8c4cb0c0e7097abab7f48b3303", - "support" - ], "css/CSS2/floats/computed-float-position-absolute.html": [ "ad9220b3a06085c458f7100c896100fb32f562e8", "testharness" @@ -469352,7 +469343,7 @@ "support" ], "tools/ci/jobs.py": [ - "2b54327ad20506c23f6698ddd6c5c5f1def8b09f", + "ddf110096440f5c798ffdf46ffb51e90b1365978", "support" ], "tools/ci/make_hosts_file.py": [ @@ -473736,7 +473727,7 @@ "support" ], "tools/wptrunner/tox.ini": [ - "d784c5b299e353f8c85aad5881caeab535522a52", + "5d343751c5488cdd1a73b9d3fcae94de46874a3e", "support" ], "tools/wptrunner/wptrunner.default.ini": [ @@ -474072,23 +474063,23 @@ "support" ], "tools/wptrunner/wptrunner/wptmanifest/tests/test_conditional.py": [ - "9da1a0f180ca23e8cadb2c9b430e983ba75dbca0", + "10b6319143230e3ffd8a1e976f804f3047771baf", "support" ], "tools/wptrunner/wptrunner/wptmanifest/tests/test_parser.py": [ - "a2a7dadc043b64ff8c9fdc2c4a3cb3837c52c4a0", + "c00320fa5ed9beac16221d4ea14d782e52a39b68", "support" ], "tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py": [ - "02280e51f845c8144b96bde8b588b9b7c376a345", + "f906afa34a36e5650e4c56d5b87982ca702520d9", "support" ], "tools/wptrunner/wptrunner/wptmanifest/tests/test_static.py": [ - "f63869636da79cdcdc961e137d4f044055d74de4", + "e0e83c83e80df2aa67fa75ac8681fa0d352d0332", "support" ], "tools/wptrunner/wptrunner/wptmanifest/tests/test_tokenizer.py": [ - "e0fb8559a1e793b1f4a26b39a1333f6e23d38b4b", + "b7c62c0041496be4f55f06699eabf9a3cf77d654", "support" ], "tools/wptrunner/wptrunner/wptrunner.py": [
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html index 1fd0277..805777f2 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
@@ -18,6 +18,7 @@ const img = document.createElement('img'); img.src = 'resources/square20.jpg'; img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'my_id'); document.body.appendChild(img); window.onload = t.step_func_done( () => { const entries = performance.getEntriesByType('element'); @@ -27,7 +28,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.jpg'; - checkElement(entry, pathname, 'my_image', beforeRender); + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender); + checkNaturalSize(entry, 20, 20); }); }, "Element Timing: image loads before onload.");
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html index a122819f..b1a5b7c 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html
@@ -18,10 +18,11 @@ t.step_func_done((entryList) => { assert_equals(entryList.getEntries().length, 1); const entry = entryList.getEntries()[0]; - checkElement(entry, pathname, 'my_image', 0); + checkElement(entry, pathname, 'my_image', 'the_id', 0); assert_equals(entry.startTime, 0, 'The startTime of a cross-origin image should be 0.'); checkRect(entry, [0, 100, 0, 100]); + checkNaturalSize(entry, 100, 100); }) ); observer.observe({entryTypes: ['element']}); @@ -33,6 +34,7 @@ const img = document.createElement('img'); img.src = pathname; img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'the_id'); document.body.appendChild(img); }); }, 'Cross-origin image element is NOT observable.');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html index 3ba12a7d0..0e24af06 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html
@@ -19,9 +19,10 @@ t.step_func_done((entryList) => { assert_equals(entryList.getEntries().length, 1); const entry = entryList.getEntries()[0]; - checkElement(entry, img_src, 'my_image', beforeRender); + checkElement(entry, img_src, 'my_image', 'my_id', beforeRender); // Assume viewport has size at least 20, so the element is fully visible. checkRect(entry, [0, 20, 0, 20]); + checkNaturalSize(entry, 20, 20); }) ); observer.observe({entryTypes: ['element']}); @@ -32,6 +33,7 @@ const img = document.createElement('img'); img.src = img_src; img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'my_id'); img.onload = t.step_func(() => { // After a short delay, assume that the entry was not dispatched. t.step_timeout(() => {
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html b/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html index 0bd99ab..9f0ef79e 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html
@@ -17,10 +17,10 @@ <div class="slideshow-container"> <div class='carousel-image'> - <img src="resources/circle.svg" elementtiming='image0'> + <img src="resources/circle.svg" elementtiming='image0' id='image0'> </div> <div class='carousel-image'> - <img src="resources/square100.png" elementtiming='image1'> + <img src="resources/square100.png" elementtiming='image1' id='image1'> </div> </div> @@ -37,13 +37,15 @@ const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { if (entry_count % 2 == 0) { - checkElement(entry, pathname0, 'image0', beforeRenderTimes[entry_count]); + checkElement(entry, pathname0, 'image0', 'image0', beforeRenderTimes[entry_count]); checkRect(entry, [0, 200, 0, 200]); + checkNaturalSize(entry, 200, 200); entry_count_per_element[0]++; } else { - checkElement(entry, pathname1, 'image1', beforeRenderTimes[entry_count]); + checkElement(entry, pathname1, 'image1', 'image1', beforeRenderTimes[entry_count]); checkRect(entry, [0, 100, 0, 100]); + checkNaturalSize(entry, 100, 100); entry_count_per_element[1]++; } entry_count++;
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html b/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html index 13c4a81..36cf1b1 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html
@@ -14,9 +14,10 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_svg', beforeRender); + checkElement(entry, pathname, 'my_svg', 'SVG', beforeRender); // Image size is 200x200 but SVG size is 100x100 so it is clipped. checkRect(entry, [0, 100, 0, 100]); + checkNaturalSize(entry, 200, 200); }) ); observer.observe({entryTypes: ['element']}); @@ -29,5 +30,5 @@ } </style> <svg width="100" height="100"> - <image href='resources/circle.svg' elementtiming='my_svg'/> + <image href='resources/circle.svg' elementtiming='my_svg' id='SVG'/> </svg>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html index d3e2c105..279fa03 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html
@@ -20,11 +20,12 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.png'; - checkElement(entry, pathname, 'not_fully_visible', beforeRender); + checkElement(entry, pathname, 'not_fully_visible', '', beforeRender); // Image will not be fully visible. It should start from the top left part // of the document, excluding the margin, and then overflow. checkRect(entry, [100, document.documentElement.clientWidth, 200, document.documentElement.clientHeight]); + checkNaturalSize(entry, 20, 20); }) ); observer.observe({entryTypes: ['element']});
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html b/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html index da46d785..94c872e 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html
@@ -20,6 +20,9 @@ assert_equals(rect.right, 100); assert_equals(rect.top, 0); assert_equals(rect.bottom, 100); + assert_equals(e.data.naturalWidth, 100); + assert_equals(e.data.naturalHeight, 100); + assert_equals(e.data.id, 'iframe_img_id'); t.done(); }); }, 'Element Timing entry in iframe has coordinates relative to the iframe.');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html b/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html new file mode 100644 index 0000000..6d77429e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html
@@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Element Timing: image with scaling.</title> +<head> +<style> +/* The margin of 50px allows the rect to be fully shown when the div is scaled. */ +body { + margin: 50px; +} +.my_div { + width: 100px; + height: 50px; + transform: scale(2); +} +</style> +</head> +<body> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/element-timing-helpers.js"></script> +<script> + const beforeRender = performance.now(); + async_test(function (t) { + const observer = new PerformanceObserver( + t.step_func_done(function(entryList) { + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + const index = window.location.href.lastIndexOf('/'); + const pathname = window.location.href.substring(0, index - 14) + + 'images/black-rectangle.png'; + checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender); + checkRect(entry, [0, 200, 25, 125]); + checkNaturalSize(entry, 100, 50); + }) + ); + observer.observe({entryTypes: ['element']}); + }, 'Image intersectionRect is affected by scaling, but not its intrinsic size.'); +</script> +<div class="my_div" id='div_id'> + <img src="/images/black-rectangle.png" elementtiming="rectangle" id='rect_id'/> +</div> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html b/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html new file mode 100644 index 0000000..70b635e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html
@@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Element Timing: image with rotation.</title> +<head> +<style> +body { + margin: 0px; +} +.my_div { + width: 100px; + height: 50px; + transform: rotate(45deg); + transform-origin: top left; +} +</style> +</head> +<body> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/element-timing-helpers.js"></script> +<script> + const beforeRender = performance.now(); + async_test(function (t) { + const observer = new PerformanceObserver( + t.step_func_done(function(entryList) { + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + const index = window.location.href.lastIndexOf('/'); + const pathname = window.location.href.substring(0, index - 14) + + 'images/black-rectangle.png'; + checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender); + checkNaturalSize(entry, 100, 50); + const rect = entry.intersectionRect; + // The div rotates with respect to the origin, so part of it will be invisible. + // The width of the visible part will be 100 / sqrt(2) and the height will be + // 100 / sqrt(2) + 50 / sqrt(2). + assert_equals(rect.left, 0); + // Checking precision only to the nearest integer. + assert_equals(Math.round(rect.right), 71); + assert_equals(rect.top, 0); + assert_equals(Math.round(rect.bottom), 106); + }) + ); + observer.observe({entryTypes: ['element']}); + }, 'Image intersectionRect is affected by rotation, but not its intrinsic size.'); +</script> +<div class="my_div" id="div_id"> + <img src="/images/black-rectangle.png" elementtiming="rectangle" id="rect_id"/> +</div> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html index 18c72cd..dbcad24 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html
@@ -22,7 +22,8 @@ const observer = new PerformanceObserver( t.step_func(function(entryList) { entryList.getEntries().forEach(entry => { - checkElement(entry, pathname, entry.identifier, beforeRender); + checkElement(entry, pathname, entry.identifier, 'image_id', beforeRender); + checkNaturalSize(entry, 100, 100); if (entry.identifier === 'my_image') { ++numEntries; responseEnd1 = entry.responseEnd; @@ -46,11 +47,13 @@ const img = document.createElement('img'); img.src = 'resources/square100.png'; img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'image_id'); document.body.appendChild(img); const img2 = document.createElement('img'); img2.src = 'resources/square100.png'; img2.setAttribute('elementtiming', 'my_image2'); + img2.setAttribute('id', 'image_id'); document.body.appendChild(img2); beforeRender = performance.now();
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html index 9170b36..39fea05 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html
@@ -20,19 +20,21 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square100.png'; - checkElement(entry, pathname, 'my_image', beforeRender); + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender); // Assume viewport has size at least 100, so the element is fully visible. checkRect(entry, [0, 100, 0, 100]); + checkNaturalSize(entry, 100, 100); }) ); observer.observe({entryTypes: ['element']}); // We add the image during onload to be sure that the observer is registered // in time for it to observe the element timing. window.onload = () => { - // Add image of width and height equal to 100. + // Add image of width equal to 100 and height equal to 100. const img = document.createElement('img'); img.src = 'resources/square100.png'; img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'my_id'); document.body.appendChild(img); beforeRender = performance.now(); };
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html index fb288438..a08274c 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html
@@ -20,10 +20,11 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.jpg'; - checkElement(entry, pathname, '', beforeRender); + checkElement(entry, pathname, '', 'large_one', beforeRender); // Assume viewport hasn't changed, so the element occupies all of it. checkRect(entry, [0, document.documentElement.clientWidth, 0, document.documentElement.clientHeight]); + checkNaturalSize(entry, 20, 20); }) ); observer.observe({entryTypes: ['element']}); @@ -35,6 +36,7 @@ img.src = 'resources/square20.jpg'; img.width = document.documentElement.clientWidth; img.height = document.documentElement.clientHeight; + img.setAttribute('id', 'large_one'); document.body.appendChild(img); beforeRender = performance.now(); };
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html index aa91aa83..05c54ac0 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html
@@ -34,7 +34,8 @@ image1Observed = 1; const pathname1 = window.location.href.substring(0, index) + '/resources/square100.png'; - checkElement(entry, pathname1, 'image1', beforeRender); + // The images do not contain ID, so expect an empty ID. + checkElement(entry, pathname1, 'image1', 'img1', beforeRender); // This image is horizontally centered. // Using abs and comparing to 1 because the viewport sizes could be odd. // If a size is odd, then image cannot be in the pure center, but left @@ -48,6 +49,7 @@ assert_equals(entry.intersectionRect.top, 0, 'top of rect for image1'); assert_equals(entry.intersectionRect.bottom, 100, 'bottom of rect for image1'); + checkNaturalSize(entry, 100, 100); } else if (entry.identifier === 'image2') { if (image2Observed) { @@ -57,9 +59,10 @@ image2Observed = 1; const pathname2 = window.location.href.substring(0, index) + '/resources/square20.png'; - checkElement(entry, pathname2, 'image2', beforeRender); + checkElement(entry, pathname2, 'image2', 'img2', beforeRender); // This image should be below image 1, and should respect the margin. checkRect(entry, [50, 250, 250, 450], "of image2"); + checkNaturalSize(entry, 20, 20); } else if (entry.identifier === 'image3') { if (image3Observed) { @@ -69,9 +72,10 @@ image3Observed = 1; const pathname3 = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname3, 'image3', beforeRender); + checkElement(entry, pathname3, 'image3', 'img3', beforeRender); // This image is just to the right of image2. checkRect(entry, [250, 450, 250, 450], "of image3"); + checkNaturalSize(entry, 200, 200); } else { assert_unreached("Received an unexpected identifier."); @@ -87,6 +91,7 @@ function addImage(number, source, width=0) { const img = document.createElement('img'); img.src = source; + // Set a different id and elementtiming value for each image. img.id = 'img' + number; img.setAttribute('elementtiming', 'image' + number); if (width !== 0)
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html index fdfe25ec..45e800d 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html
@@ -14,9 +14,10 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_svg', beforeRender); + checkElement(entry, pathname, 'my_svg', 'svg_id', beforeRender); // Assume viewport has size at least 200, so the element is fully visible. checkRect(entry, [0, 200, 0, 200]); + checkNaturalSize(entry, 200, 200); }) ); observer.observe({entryTypes: ['element']}); @@ -29,5 +30,5 @@ } </style> <svg width="300" height="300"> - <image href='resources/circle.svg' elementtiming='my_svg'/> + <image href='resources/circle.svg' elementtiming='my_svg' id='svg_id'/> </svg>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html index 4096138..d3a6993 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html
@@ -14,9 +14,10 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_poster', beforeRender); + checkElement(entry, pathname, 'my_poster', 'the_poster', beforeRender); // Assume viewport has size at least 200, so the element is fully visible. checkRect(entry, [0, 200, 0, 200]); + checkNaturalSize(entry, 200, 200); }) ); observer.observe({entryTypes: ['element']}); @@ -28,4 +29,4 @@ margin: 0; } </style> -<video elementtiming='my_poster' src='/media/test.mp4' poster='resources/circle.svg'/> +<video elementtiming='my_poster' id='the_poster' src='/media/test.mp4' poster='resources/circle.svg'/>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html index cf54e1e..c0a7d4f 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html
@@ -24,7 +24,8 @@ img_src; // Since the image is only fully loaded after the sleep, the render timestamp // must be greater than |beforeRender| + |sleep|. - checkElement(entry, pathname, 'my_image', beforeRender + sleep); + checkElement(entry, pathname, 'my_image', '', beforeRender + sleep); + checkNaturalSize(entry, 20, 20); }) ); observer.observe({entryTypes: ['element']});
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html b/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html new file mode 100644 index 0000000..b0280845 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html
@@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Element Timing: observe a rectangular image</title> +<body> +<style> +body { + margin: 20px; +} +</style> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/element-timing-helpers.js"></script> +<script> + let beforeRender; + async_test(function (t) { + const observer = new PerformanceObserver( + t.step_func_done(function(entryList) { + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + const index = window.location.href.lastIndexOf('/'); + // Subtracting 14 to remove 'element-timing'. + const pathname = window.location.href.substring(0, index - 14) + + 'images/black-rectangle.png'; + checkElement(entry, pathname, 'my_image', 'rectangle', beforeRender); + // Assume viewport has size at least 100, so the element is fully visible. + checkRect(entry, [20, 120, 20, 70]); + checkNaturalSize(entry, 100, 50); + }) + ); + observer.observe({entryTypes: ['element']}); + // We add the image during onload to be sure that the observer is registered + // in time for it to observe the element timing. + window.onload = () => { + // Add image of width equal to 100 and height equal to 50. + const img = document.createElement('img'); + img.src = '/images/black-rectangle.png'; + img.id = 'rectangle'; + img.setAttribute('elementtiming', 'my_image'); + document.body.appendChild(img); + beforeRender = performance.now(); + }; + }, 'Element with rectangular image has correct rect and instrinsic size.'); +</script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js index e952930..66605df 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js +++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
@@ -1,10 +1,11 @@ // Checks that this is an ElementTiming entry with name |expectedName|. It also // does a very basic check on |startTime|: after |beforeRender| and before now(). -function checkElement(entry, expectedName, expectedIdentifier, beforeRender) { +function checkElement(entry, expectedName, expectedIdentifier, expectedID, beforeRender) { assert_equals(entry.entryType, 'element'); assert_equals(entry.name, expectedName); assert_equals(entry.identifier, expectedIdentifier); assert_equals(entry.duration, 0); + assert_equals(entry.id, expectedID); assert_greater_than_equal(entry.startTime, beforeRender); assert_greater_than_equal(performance.now(), entry.startTime); const rt_entries = performance.getEntriesByName(expectedName, 'resource'); @@ -12,7 +13,7 @@ assert_equals(rt_entries[0].responseEnd, entry.responseEnd); } -// Checks that the rect matches the desired values [left right top bottom] +// Checks that the rect matches the desired values [left right top bottom]. function checkRect(entry, expected, description="") { assert_equals(entry.intersectionRect.left, expected[0], 'left of rect ' + description); @@ -23,3 +24,9 @@ assert_equals(entry.intersectionRect.bottom, expected[3], 'bottom of rect ' + description); } + +// Checks that the intrinsic size matches the desired values. +function checkNaturalSize(entry, width, height) { + assert_equals(entry.naturalWidth, width); + assert_equals(entry.naturalHeight, height); +}
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html index 3c43a41c..25bd6793 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html
@@ -12,10 +12,13 @@ 'length' : entryList.getEntries().length, 'entryType' : entryList.getEntries()[0].entryType, 'rect' : entryList.getEntries()[0].intersectionRect, + 'naturalWidth' : entryList.getEntries()[0].naturalWidth, + 'naturalHeight' : entryList.getEntries()[0].naturalHeight, + 'id': entryList.getEntries()[0].id, }, '*'); }); observer.observe({entryTypes: ['element']}); </script> -<img src=square100.png elementtiming=my_image/> +<img src=square100.png id=iframe_img_id elementtiming=my_image/> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-options.html b/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-options.html index c6ce1fdc..6a0bfa1 100644 --- a/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-options.html +++ b/third_party/blink/web_tests/external/wpt/fullscreen/api/element-request-fullscreen-options.html
@@ -8,11 +8,14 @@ // no normative requirements on what navigationUI should do, just test for // basic support. (One could also check that the three allowed enum valid are // supported and no others, but that would overlap with UA-specific tests.) -test(() => { +promise_test(() => { let invoked = false; - document.body.requestFullscreen({ + return document.body.requestFullscreen({ get navigationUI() { invoked = true; return "irrelevant-value"; } + }).then(() => { + assert_unreached("promise should be rejected due to invalid navigationUI value"); + }, () => { + assert_true(invoked, "navigationUI getter invoked"); }); - assert_true(invoked, "navigationUI getter invoked"); }); </script>
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt index 32878d3..7d03cc1 100644 --- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt +++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 71 tests; 67 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS idlharness PASS idl_test setup PASS Partial interface Performance: original interface defined @@ -20,7 +20,7 @@ PASS PerformanceObserver interface: existence and properties of interface prototype object PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property -FAIL PerformanceObserver interface: operation observe(PerformanceObserverInit) assert_equals: property has wrong .length expected 0 but got 1 +PASS PerformanceObserver interface: operation observe(PerformanceObserverInit) PASS PerformanceObserver interface: operation disconnect() PASS PerformanceObserver interface: operation takeRecords() PASS PerformanceObserver interface: attribute supportedEntryTypes
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt index 32878d3..7d03cc1 100644 --- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 71 tests; 67 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS idlharness PASS idl_test setup PASS Partial interface Performance: original interface defined @@ -20,7 +20,7 @@ PASS PerformanceObserver interface: existence and properties of interface prototype object PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property -FAIL PerformanceObserver interface: operation observe(PerformanceObserverInit) assert_equals: property has wrong .length expected 0 but got 1 +PASS PerformanceObserver interface: operation observe(PerformanceObserverInit) PASS PerformanceObserver interface: operation disconnect() PASS PerformanceObserver interface: operation takeRecords() PASS PerformanceObserver interface: attribute supportedEntryTypes
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt index 32878d3..7d03cc1 100644 --- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 71 tests; 67 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS idlharness PASS idl_test setup PASS Partial interface Performance: original interface defined @@ -20,7 +20,7 @@ PASS PerformanceObserver interface: existence and properties of interface prototype object PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property -FAIL PerformanceObserver interface: operation observe(PerformanceObserverInit) assert_equals: property has wrong .length expected 0 but got 1 +PASS PerformanceObserver interface: operation observe(PerformanceObserverInit) PASS PerformanceObserver interface: operation disconnect() PASS PerformanceObserver interface: operation takeRecords() PASS PerformanceObserver interface: attribute supportedEntryTypes
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt index 32878d3..7d03cc1 100644 --- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
@@ -1,5 +1,5 @@ This is a testharness.js-based test. -Found 71 tests; 67 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN. +Found 71 tests; 68 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN. PASS idlharness PASS idl_test setup PASS Partial interface Performance: original interface defined @@ -20,7 +20,7 @@ PASS PerformanceObserver interface: existence and properties of interface prototype object PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property -FAIL PerformanceObserver interface: operation observe(PerformanceObserverInit) assert_equals: property has wrong .length expected 0 but got 1 +PASS PerformanceObserver interface: operation observe(PerformanceObserverInit) PASS PerformanceObserver interface: operation disconnect() PASS PerformanceObserver interface: operation takeRecords() PASS PerformanceObserver interface: attribute supportedEntryTypes
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/jobs.py b/third_party/blink/web_tests/external/wpt/tools/ci/jobs.py index 2b54327..ddf11009 100644 --- a/third_party/blink/web_tests/external/wpt/tools/ci/jobs.py +++ b/third_party/blink/web_tests/external/wpt/tools/ci/jobs.py
@@ -25,7 +25,7 @@ "manifest_upload": [".*"], "resources_unittest": ["resources/", "tools/"], "tools_unittest": ["tools/"], - "wptrunner_unittest": ["tools/wptrunner/*"], + "wptrunner_unittest": ["tools/"], "build_css": ["css/"], "update_built": ["2dcontext/", "html/",
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/tox.ini b/third_party/blink/web_tests/external/wpt/tools/wptrunner/tox.ini index d784c5b..5d34375 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/tox.ini +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/tox.ini
@@ -2,7 +2,8 @@ xfail_strict=true [tox] -envlist = py27-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo} +envlist = py27-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo},py36-base +skip_missing_interpreters = true [testenv] deps =
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_conditional.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_conditional.py index 9da1a0f..10b63191 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_conditional.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_conditional.py
@@ -1,9 +1,13 @@ +import pytest +import sys import unittest from ..backends import conditional from ..node import BinaryExpressionNode, BinaryOperatorNode, VariableNode, NumberNode +@pytest.mark.xfail(sys.version[0] == "3", + reason="wptmanifest.parser doesn't support py3") class TestConditional(unittest.TestCase): def compile(self, input_text): return conditional.compile(input_text)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_parser.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_parser.py index a2a7dad..c00320f 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_parser.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_parser.py
@@ -1,3 +1,5 @@ +import pytest +import sys import unittest from six.moves import cStringIO as StringIO @@ -8,6 +10,8 @@ # use test_serializer for the majority of cases +@pytest.mark.xfail(sys.version[0] == "3", + reason="wptmanifest.parser doesn't support py3") class TestExpression(unittest.TestCase): def setUp(self): self.parser = parser.Parser()
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py index 02280e5..f906afa 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py
@@ -6,6 +6,8 @@ from .. import parser, serializer +@pytest.mark.xfail(sys.version[0] == "3", + reason="wptmanifest.parser doesn't support py3") class TokenizerTest(unittest.TestCase): def setUp(self): self.serializer = serializer.ManifestSerializer()
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_static.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_static.py index f638696..e0e83c83 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_static.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_static.py
@@ -1,3 +1,5 @@ +import pytest +import sys import unittest from ..backends import static @@ -6,6 +8,8 @@ # use test_serializer for the majority of cases +@pytest.mark.xfail(sys.version[0] == "3", + reason="wptmanifest.parser doesn't support py3") class TestStatic(unittest.TestCase): def compile(self, input_text, input_data): return static.compile(input_text, input_data)
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_tokenizer.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_tokenizer.py index e0fb855..b7c62c00 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_tokenizer.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/wptmanifest/tests/test_tokenizer.py
@@ -1,3 +1,5 @@ +import sys +import pytest import unittest from six.moves import cStringIO as StringIO @@ -6,6 +8,8 @@ from ..parser import token_types +@pytest.mark.xfail(sys.version[0] == "3", + reason="Tokenizer doesn't support py3") class TokenizerTest(unittest.TestCase): def setUp(self): self.tokenizer = parser.Tokenizer()
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-checkbox-radio.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-checkbox-radio.html new file mode 100644 index 0000000..d4b7a8f --- /dev/null +++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-checkbox-radio.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src='../../resources/testharness.js'></script> +<script src='../../resources/testharnessreport.js'></script> +<script src='resources/snav-testharness.js'></script> + +<div> + <input type='checkbox' id='cb' name='checkbox' checked=''> + <label for='checkbox'>checkbox</label> +</div> + +<div> + <input type='radio' name='r' value='A' id='a' checked=''> A<br> + <input type='radio' name='r' value='B' id='b'> B<br> + <input type='radio' name='r' value='C' id='c'> C +</div> + +<script> + snav.assertSnavEnabledAndTestable(false /* focuslessSpatNav */ ); + let cb = document.getElementById('cb'); + let a = document.getElementById('a'); + let b = document.getElementById('b'); + let c = document.getElementById('c'); + + test(() => { + snav.triggerMove('Down'); // Move interest to cb. + + assert_true(cb.checked); + + eventSender.keyDown('Enter'); + + assert_false(cb.checked); + + assert_true(a.checked); + assert_false(b.checked); + assert_false(c.checked); + + snav.triggerMove('Down'); + snav.triggerMove('Down'); // Move interest to b. + + eventSender.keyDown('Enter'); + + assert_false(a.checked); + assert_true(b.checked); + assert_false(c.checked); + + }, 'Ensure press enter key on checkbox and radio change the check state.'); + +</script>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-focusless-checkbox-radio.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-focusless-checkbox-radio.html new file mode 100644 index 0000000..d3a7d9d7 --- /dev/null +++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-focusless-checkbox-radio.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src='../../resources/testharness.js'></script> +<script src='../../resources/testharnessreport.js'></script> +<script src='resources/snav-testharness.js'></script> + +<div> + <input type='checkbox' id='cb' name='checkbox' checked=''> + <label for='checkbox'>checkbox</label> +</div> + +<div> + <input type='radio' name='r' value='A' id='a' checked=''> A<br> + <input type='radio' name='r' value='B' id='b'> B<br> + <input type='radio' name='r' value='C' id='c'> C +</div> + +<script> + snav.assertSnavEnabledAndTestable(true /* focuslessSpatNav */ ); + let cb = document.getElementById('cb'); + let a = document.getElementById('a'); + let b = document.getElementById('b'); + let c = document.getElementById('c'); + + test(() => { + snav.triggerMove('Down'); // Move interest to cb. + + assert_true(cb.checked); + + eventSender.keyDown('Enter'); + + assert_false(cb.checked); + + assert_true(a.checked); + assert_false(b.checked); + assert_false(c.checked); + + snav.triggerMove('Down'); + snav.triggerMove('Down'); // Move interest to b. + + eventSender.keyDown('Enter'); + + assert_false(a.checked); + assert_true(b.checked); + assert_false(c.checked); + + }, 'Ensure press enter key on checkbox and radio change the check state.'); + +</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content.js b/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content.js index ae898f8..c1a3ad177 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/blocked-mixed-content.js
@@ -7,20 +7,11 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: false, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: true, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; TestRunner.mainTarget.model(Security.SecurityModel) .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Secure, true, [], insecureContentStatus, null)); + Protocol.Security.SecurityState.Secure, true, [], null)); var request = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); request.setBlockedReason(Protocol.Network.BlockedReason.MixedContent);
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/main-origin-assigned-despite-request-missing.js b/third_party/blink/web_tests/http/tests/devtools/security/main-origin-assigned-despite-request-missing.js index ed39d1b4a..ddb95fe 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/main-origin-assigned-despite-request-missing.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/main-origin-assigned-despite-request-missing.js
@@ -7,20 +7,11 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - //** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: false, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; TestRunner.mainTarget.model(Security.SecurityModel) .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Secure, true, [], insecureContentStatus, null)); + Protocol.Security.SecurityState.Secure, true, [], null)); const page_url = TestRunner.resourceTreeModel.mainFrame.url; const page_origin = Common.ParsedURL.extractOrigin(page_url);
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload.js b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload.js index c196517..21cfa7e 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-active-and-passive-reload.js
@@ -8,16 +8,6 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: true, - displayedMixedContent: true, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; - TestRunner.addResult('\nBefore Refresh --------------'); var mixedExplanations = [ @@ -40,7 +30,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Neutral, true, mixedExplanations, null)); // At this point, the page has mixed content but no mixed requests have been recorded, so the user should be prompted to refresh. var explanations = @@ -55,7 +45,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Neutral, true, mixedExplanations, null)); var passive = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); passive.mixedContentType = 'optionally-blockable';
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload.js b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload.js index 6501110e..7e9af0d9 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-reload.js
@@ -8,16 +8,6 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: true, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; - TestRunner.addResult('\nBefore Refresh --------------'); var mixedExplanations = [{ @@ -31,7 +21,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Neutral, true, mixedExplanations, null)); // At this point, the page has mixed content but no mixed requests have been recorded, so the user should be prompted to refresh. var explanations = @@ -46,7 +36,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Neutral, true, mixedExplanations, null)); var request = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); request.mixedContentType = 'optionally-blockable';
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-sidebar.js b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-sidebar.js index eb988775..ed2bbae 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-sidebar.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/mixed-content-sidebar.js
@@ -8,16 +8,6 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: true, - displayedMixedContent: true, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; - var mixedExplanations = [ { securityState: Protocol.Security.SecurityState.Neutral, @@ -38,7 +28,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Neutral, true, mixedExplanations, null)); var passive = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); passive.mixedContentType = 'optionally-blockable';
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content.js b/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content.js index 90b9bdb6..cd45af7 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/security-blocked-mixed-content.js
@@ -7,20 +7,11 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: false, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; TestRunner.mainTarget.model(Security.SecurityModel) .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Secure, true, [], insecureContentStatus, null)); + Protocol.Security.SecurityState.Secure, true, [], null)); var request = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); request.setBlockedReason(Protocol.Network.BlockedReason.MixedContent);
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering.js b/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering.js index 0a63665..439b148 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/security-explanation-ordering.js
@@ -7,16 +7,6 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: false, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; - // Explanations from https://cbc.badssl.com/ as of 2016-06-13. // We explicitly place the explanation with the security state "info" // first to make sure it gets reordered. @@ -52,7 +42,7 @@ .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Secure, true, explanations, insecureContentStatus, null)); + Protocol.Security.SecurityState.Secure, true, explanations, null)); var request = new SDK.NetworkRequest(0, 'http://foo.test', 'https://foo.test', 0, 0, null); SecurityTestRunner.dispatchRequestFinished(request);
diff --git a/third_party/blink/web_tests/http/tests/devtools/security/security-summary.js b/third_party/blink/web_tests/http/tests/devtools/security/security-summary.js index 20c506c..1fa7c88a 100644 --- a/third_party/blink/web_tests/http/tests/devtools/security/security-summary.js +++ b/third_party/blink/web_tests/http/tests/devtools/security/security-summary.js
@@ -7,20 +7,11 @@ await TestRunner.loadModule('security_test_runner'); await TestRunner.showPanel('security'); - /** @type {!Protocol.Security.InsecureContentStatus} */ - var insecureContentStatus = { - ranMixedContent: false, - displayedMixedContent: false, - ranContentWithCertErrors: false, - displayedContentWithCertErrors: false, - ranInsecureContentStyle: Protocol.Security.SecurityState.Insecure, - displayedInsecureContentStyle: Protocol.Security.SecurityState.Neutral - }; TestRunner.mainTarget.model(Security.SecurityModel) .dispatchEventToListeners( Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState( - Protocol.Security.SecurityState.Secure, true, [], insecureContentStatus, 'Test: Summary Override Text')); + Protocol.Security.SecurityState.Secure, true, [], 'Test: Summary Override Text')); TestRunner.dumpDeepInnerHTML( Security.SecurityPanel._instance()._mainView.contentElement.getElementsByClassName('security-summary-text')[0]);
diff --git a/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png new file mode 100644 index 0000000..a632452 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.txt b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.txt new file mode 100644 index 0000000..fa5ae9f --- /dev/null +++ b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images-expected.txt
@@ -0,0 +1 @@ +CONSOLE ERROR: Feature policy violation: unoptimized-lossless-images is not allowed in this document.
diff --git a/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images.html b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images.html new file mode 100644 index 0000000..f42833d --- /dev/null +++ b/third_party/blink/web_tests/http/tests/images/feature-policy-unoptimized-lossless-images.html
@@ -0,0 +1,7 @@ +<!DOCTYPE html> +<style> +body { + font: 10px Ahem; +} +</style> +<body><iframe src="resources/frame-with-compression-test-images.html" allow="unoptimized-lossless-images 'none'" width="700" height="500"></iframe></body>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt deleted file mode 100644 index 3309d6c..0000000 --- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -FAIL InstalledApp related properties is not on interfaces before adding trial token via script. assert_equals: Property getInstalledRelatedApps exists on Navigator expected false but got true -PASS InstalledApp related properties is on interfaces after adding trial token via script. -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added.html index 87490ba..5465f78 100644 --- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added.html +++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added.html
@@ -11,9 +11,14 @@ let properties_to_check = {'Navigator': ['getInstalledRelatedApps']}; -test(t => { - OriginTrialsHelper.check_properties_missing(this, properties_to_check); -}, "InstalledApp related properties is not on interfaces before adding trial token via script."); +// Can only run this test if feature is not enabled via a Chrome flag. +// That is only the case when running this in a virtual test suite (by default, +// runtime enabled features are on for layout tests). +if (!self.internals.runtimeFlags.installedAppEnabled) { + test(t => { + OriginTrialsHelper.check_properties_missing(this, properties_to_check); + }, "InstalledApp related properties is not on interfaces before adding trial token via script."); +} OriginTrialsHelper.add_token(token);
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-disabled.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-disabled.html deleted file mode 100644 index b7d7e278..0000000 --- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-disabled.html +++ /dev/null
@@ -1,15 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> - -<script src="../../../../resources/testharness.js"></script> -<script src="../../../../resources/testharnessreport.js"></script> - -<script src="../../wasm/resources/wasm-constants.js"></script> -<script src="../../wasm/resources/wasm-module-builder.js"></script> -<script src="resources/wasm-threads-origin-trial.js"></script> -<script> -test(testWasmThreadsDisabled, - "Test that WebAssembly threads are disabled without origin trial token"); -promise_test(testWasmThreadsDisabledOnWorker, - "Test that WebAssembly threads are disabled in worker without origin trial token"); -</script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-enabled.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-enabled.html deleted file mode 100644 index 4a0892d0..0000000 --- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/wasm-threads-origin-trial-enabled.html +++ /dev/null
@@ -1,20 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<!-- Generate this token with the command: -generate_token.py http://127.0.0.1:8000 WebAssemblyThreads --expire-timestamp=2000000000 ---> - -<meta http-equiv="origin-trial" content="AvA8hsoHU+5qiE3oR2wNRZKcI7MOkHl4Vdu3IX8IKv90k4tpNFIaC3KblOaOh9ND63YD/539iVGJ930KCYwotgoAAABaeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViQXNzZW1ibHlUaHJlYWRzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" /> - -<script src="../../../../resources/testharness.js"></script> -<script src="../../../../resources/testharnessreport.js"></script> - -<script src="../../wasm/resources/wasm-constants.js"></script> -<script src="../../wasm/resources/wasm-module-builder.js"></script> -<script src="resources/wasm-threads-origin-trial.js"></script> -<script> -test(testWasmThreadsEnabled, - "Test that WebAssembly threads are enabled with origin trial token"); -promise_test(testWasmThreadsEnabledOnWorker, - "Test that WebAssembly threads are enabled in worker with origin trial token"); -</script>
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png b/third_party/blink/web_tests/platform/linux/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png new file mode 100644 index 0000000..a632452 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png b/third_party/blink/web_tests/platform/mac/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png new file mode 100644 index 0000000..c119fc3 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png b/third_party/blink/web_tests/platform/win/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png new file mode 100644 index 0000000..0243b0e --- /dev/null +++ b/third_party/blink/web_tests/platform/win/http/tests/images/feature-policy-unoptimized-lossless-images-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt b/third_party/blink/web_tests/virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt deleted file mode 100644 index d5604bd..0000000 --- a/third_party/blink/web_tests/virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/installedapp-origin-trial-interfaces-script-added-expected.txt +++ /dev/null
@@ -1,5 +0,0 @@ -This is a testharness.js-based test. -PASS InstalledApp related properties is not on interfaces before adding trial token via script. -PASS InstalledApp related properties is on interfaces after adding trial token via script. -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt index d779f94..324358f4 100644 --- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt +++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -34,6 +34,7 @@ sync-script sync-xhr top-navigation +unoptimized-lossless-images unoptimized-lossy-images unsized-media usb
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt index f5eeda5..7ee797b 100644 --- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5291,8 +5291,11 @@ setter onresourcetimingbufferfull interface PerformanceElementTiming : PerformanceEntry attribute @@toStringTag + getter id getter identifier getter intersectionRect + getter naturalHeight + getter naturalWidth getter responseEnd method constructor method toJSON
diff --git a/third_party/sqlite/BUILD.gn b/third_party/sqlite/BUILD.gn index d099d89..62fde59 100644 --- a/third_party/sqlite/BUILD.gn +++ b/third_party/sqlite/BUILD.gn
@@ -20,6 +20,10 @@ # https://www.sqlite.org/compile.html config("chromium_sqlite3_compile_options") { defines = [ + # Skip writing transaction rollback journals on f2fs. + # f2fs tends to be used on Android, and may be used on ChromeOS. + "SQLITE_ENABLE_BATCH_ATOMIC_WRITE", + "SQLITE_ENABLE_FTS3", # New unicode61 tokenizer with built-in tables.
diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c index 74e2713..a1529a6f 100644 --- a/third_party/sqlite/amalgamation/sqlite3.c +++ b/third_party/sqlite/amalgamation/sqlite3.c
@@ -158303,7 +158303,6 @@ ** query logic likewise merges doclists so that newer data knocks out ** older data. */ -#define CHROMIUM_FTS3_CHANGES 1 /************** Include fts3Int.h in the middle of fts3.c ********************/ /************** Begin file fts3Int.h *****************************************/ @@ -162951,11 +162950,7 @@ ** module with sqlite. */ if( SQLITE_OK==rc -#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST - /* fts3_tokenizer() disabled for security reasons. */ -#else && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) -#endif && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) @@ -162965,9 +162960,6 @@ rc = sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); -#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST - /* Disable fts4 and tokenizer vtab pending review. */ -#else if( rc==SQLITE_OK ){ rc = sqlite3_create_module_v2( db, "fts4", &fts3Module, (void *)pHash, 0 @@ -162976,7 +162968,6 @@ if( rc==SQLITE_OK ){ rc = sqlite3Fts3InitTok(db, (void *)pHash); } -#endif return rc; } @@ -221440,7 +221431,7 @@ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=221443 +#if __LINE__!=221434 #undef SQLITE_SOURCE_ID #define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0alt2" #endif
diff --git a/third_party/sqlite/patched/ext/fts3/fts3.c b/third_party/sqlite/patched/ext/fts3/fts3.c index c371d3e8..823e1b6a 100644 --- a/third_party/sqlite/patched/ext/fts3/fts3.c +++ b/third_party/sqlite/patched/ext/fts3/fts3.c
@@ -287,7 +287,6 @@ ** query logic likewise merges doclists so that newer data knocks out ** older data. */ -#define CHROMIUM_FTS3_CHANGES 1 #include "fts3Int.h" #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) @@ -4017,11 +4016,7 @@ ** module with sqlite. */ if( SQLITE_OK==rc -#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST - /* fts3_tokenizer() disabled for security reasons. */ -#else && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) -#endif && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) @@ -4031,9 +4026,6 @@ rc = sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); -#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST - /* Disable fts4 and tokenizer vtab pending review. */ -#else if( rc==SQLITE_OK ){ rc = sqlite3_create_module_v2( db, "fts4", &fts3Module, (void *)pHash, 0 @@ -4042,7 +4034,6 @@ if( rc==SQLITE_OK ){ rc = sqlite3Fts3InitTok(db, (void *)pHash); } -#endif return rc; }
diff --git a/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch b/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch index 602fc1f..c5c57d4 100644 --- a/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch +++ b/third_party/sqlite/patches/0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Scott Hess <shess@chromium.org> Date: Sat, 20 Jul 2013 11:42:21 -0700 -Subject: [PATCH 01/12] Virtual table supporting recovery of corrupted +Subject: [PATCH 01/11] Virtual table supporting recovery of corrupted databases. "recover" implements a virtual table which uses the SQLite pager layer @@ -40,11 +40,11 @@ @@ -77,6 +77,8 @@ LIBOBJ+= vdbe.o parse.o \ vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \ utf.o vtab.o window.o - + +LIBOBJ += recover.o recover_varint.o + LIBOBJ += sqlite3session.o - + # All of the source code files. @@ -410,6 +412,8 @@ TESTSRC2 = \ $(TOP)/src/prepare.c \ @@ -60,7 +60,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit +TESTFIXTURE_FLAGS += -DDEFAULT_ENABLE_RECOVER=1 - + testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c $(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS) \ diff --git a/third_party/sqlite/patched/src/main.c b/third_party/sqlite/patched/src/main.c @@ -70,7 +70,7 @@ @@ -3244,6 +3244,14 @@ static int openDatabase( } #endif - + +#ifdef DEFAULT_ENABLE_RECOVER + /* Initialize recover virtual table for testing. */ + extern int chrome_sqlite3_recoverVtableInit(sqlite3 *db); @@ -3900,6 +3900,6 @@ +} [list 4 1024 1 text [string length $substr] $substr] + +finish_test --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch b/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch index 1ee7be3..38b19e17 100644 --- a/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch +++ b/third_party/sqlite/patches/0002-Custom-shell.c-helpers-to-load-Chromium-s-ICU-data.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "tc@google.com" <tc@google.com> Date: Tue, 6 Jan 2009 22:39:41 +0000 -Subject: [PATCH 02/12] Custom shell.c helpers to load Chromium's ICU data. +Subject: [PATCH 02/11] Custom shell.c helpers to load Chromium's ICU data. History uses fts3 with an icu-based segmenter. These changes allow building a sqlite3 binary for Linux or Windows which can read those files. @@ -24,7 +24,7 @@ @@ -60,6 +60,13 @@ TLIBS = OPTS = -DNDEBUG=1 OPTS += -DHAVE_FDATASYNC=1 - + +# Support for loading Chromium ICU data in sqlite3. +ifeq ($(shell uname -s),Darwin) +SHELL_ICU = @@ -40,12 +40,12 @@ --- a/third_party/sqlite/patched/main.mk +++ b/third_party/sqlite/patched/main.mk @@ -556,7 +556,7 @@ libsqlite3.a: $(LIBOBJ) - + sqlite3$(EXE): shell.c libsqlite3.a sqlite3.h $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \ - shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) + shell.c $(SHELL_ICU) libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) - + sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ diff --git a/third_party/sqlite/patched/src/shell.c.in b/third_party/sqlite/patched/src/shell.c.in @@ -55,7 +55,7 @@ @@ -8891,6 +8891,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } #endif - + + /* Begin evanm patch. */ +#if !defined(__APPLE__) + extern int sqlite_shell_init_icu(); @@ -140,6 +140,6 @@ + + return 1; +} --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0004-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch b/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch similarity index 90% rename from third_party/sqlite/patches/0004-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch rename to third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch index 4989c3c7..6cd9acc 100644 --- a/third_party/sqlite/patches/0004-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch +++ b/third_party/sqlite/patches/0003-Fix-compilation-with-SQLITE_OMIT_WINDOWFUNC.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Victor Costan <pwnall@chromium.org> Date: Sun, 10 Feb 2019 13:12:57 -0800 -Subject: [PATCH 04/12] Fix compilation with SQLITE_OMIT_WINDOWFUNC. +Subject: [PATCH 03/11] Fix compilation with SQLITE_OMIT_WINDOWFUNC. --- third_party/sqlite/patched/src/resolve.c | 2 ++ @@ -14,7 +14,7 @@ @@ -1556,6 +1556,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } - + +#ifndef SQLITE_OMIT_WINDOWFUNC if( IN_RENAME_OBJECT ){ Window *pWin; @@ -24,9 +24,9 @@ } } +#endif - + /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0003-fts3-Disable-fts3_tokenizer-and-fts4.patch b/third_party/sqlite/patches/0003-fts3-Disable-fts3_tokenizer-and-fts4.patch deleted file mode 100644 index 1a9b16c..0000000 --- a/third_party/sqlite/patches/0003-fts3-Disable-fts3_tokenizer-and-fts4.patch +++ /dev/null
@@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Scott Hess <shess@chromium.org> -Date: Tue, 16 Dec 2014 13:02:27 -0800 -Subject: [PATCH 03/12] [fts3] Disable fts3_tokenizer and fts4. - -fts3_tokenizer allows a SQLite user to specify a pointer to call as a -function, which has obvious sercurity implications. Disable fts4 until -someone explicitly decides to own support for it. Disable fts3tokenize -virtual table until someone explicitly decides to own support for it. - -No original review URL because this was part of the initial Chromium commit. ---- - third_party/sqlite/patched/ext/fts3/fts3.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/third_party/sqlite/patched/ext/fts3/fts3.c b/third_party/sqlite/patched/ext/fts3/fts3.c -index 823e1b6a81fe..c371d3e8f0b5 100644 ---- a/third_party/sqlite/patched/ext/fts3/fts3.c -+++ b/third_party/sqlite/patched/ext/fts3/fts3.c -@@ -287,6 +287,7 @@ - ** query logic likewise merges doclists so that newer data knocks out - ** older data. - */ -+#define CHROMIUM_FTS3_CHANGES 1 - - #include "fts3Int.h" - #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) -@@ -4016,7 +4017,11 @@ int sqlite3Fts3Init(sqlite3 *db){ - ** module with sqlite. - */ - if( SQLITE_OK==rc -+#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST -+ /* fts3_tokenizer() disabled for security reasons. */ -+#else - && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) -+#endif - && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) -@@ -4026,6 +4031,9 @@ int sqlite3Fts3Init(sqlite3 *db){ - rc = sqlite3_create_module_v2( - db, "fts3", &fts3Module, (void *)pHash, hashDestroy - ); -+#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST -+ /* Disable fts4 and tokenizer vtab pending review. */ -+#else - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module_v2( - db, "fts4", &fts3Module, (void *)pHash, 0 -@@ -4034,6 +4042,7 @@ int sqlite3Fts3Init(sqlite3 *db){ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3InitTok(db, (void *)pHash); - } -+#endif - return rc; - } - --- -2.21.0.392.gf8f6787159e-goog -
diff --git a/third_party/sqlite/patches/0005-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch b/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch similarity index 92% rename from third_party/sqlite/patches/0005-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch rename to third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch index 39f54908..d2ee401 100644 --- a/third_party/sqlite/patches/0005-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch +++ b/third_party/sqlite/patches/0004-Fix-dbfuzz2.c-compilation-errors-on-Windows.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Victor Costan <pwnall@chromium.org> Date: Sun, 10 Feb 2019 15:18:43 -0800 -Subject: [PATCH 05/12] Fix dbfuzz2.c compilation errors on Windows. +Subject: [PATCH 04/11] Fix dbfuzz2.c compilation errors on Windows. --- third_party/sqlite/patched/test/dbfuzz2.c | 4 ++++ @@ -20,7 +20,7 @@ #include <sys/resource.h> +#endif #include "sqlite3.h" - + /* @@ -261,6 +263,7 @@ int LLVMFuzzerInitialize(int *pArgc, char ***pArgv){ szMax = strtol(argv[++i], 0, 0); @@ -38,6 +38,6 @@ } argv[j++] = argv[i]; } --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0006-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch b/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch similarity index 90% rename from third_party/sqlite/patches/0006-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch rename to third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch index 95ffdc6..f10b0df 100644 --- a/third_party/sqlite/patches/0006-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch +++ b/third_party/sqlite/patches/0005-Fix-Heap-buffer-overflow-in-vdbeRecordCompareInt.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Tue, 5 Mar 2019 13:49:51 -0800 -Subject: [PATCH 06/12] Fix Heap-buffer-overflow in vdbeRecordCompareInt +Subject: [PATCH 05/11] Fix Heap-buffer-overflow in vdbeRecordCompareInt This backports https://www.sqlite.org/src/info/c1ac00706bae45fe @@ -23,6 +23,6 @@ sqlite3_free(pCellKey); } assert( --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0007-fix-heap-buffer-overflow-in-cellsizeptr.patch b/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch similarity index 92% rename from third_party/sqlite/patches/0007-fix-heap-buffer-overflow-in-cellsizeptr.patch rename to third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch index 777daa3d..b265b86 100644 --- a/third_party/sqlite/patches/0007-fix-heap-buffer-overflow-in-cellsizeptr.patch +++ b/third_party/sqlite/patches/0006-fix-heap-buffer-overflow-in-cellsizeptr.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Tue, 5 Mar 2019 14:13:19 -0800 -Subject: [PATCH 07/12] fix heap-buffer-overflow in cellsizeptr +Subject: [PATCH 06/11] fix heap-buffer-overflow in cellsizeptr This backports https://www.sqlite.org/src/info/e7aca0714bc475e0 @@ -29,8 +29,8 @@ + memset(pNew+pageSize, 0, 8); + } } - + if( rc==SQLITE_OK ){ --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0008-fix-integer-overflow-in-checkList.patch b/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch similarity index 94% rename from third_party/sqlite/patches/0008-fix-integer-overflow-in-checkList.patch rename to third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch index ce027b73..2c67373 100644 --- a/third_party/sqlite/patches/0008-fix-integer-overflow-in-checkList.patch +++ b/third_party/sqlite/patches/0007-fix-integer-overflow-in-checkList.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Tue, 5 Mar 2019 14:17:05 -0800 -Subject: [PATCH 08/12] fix integer overflow in checkList +Subject: [PATCH 07/11] fix integer overflow in checkList This backports https://www.sqlite.org/src/info/05b87e0755638d31 @@ -28,7 +28,7 @@ while( iPage!=0 && pCheck->mxErr ){ DbPage *pOvflPage; @@ -9797,7 +9797,7 @@ static int checkTreePage( - + /* Check the content overflow list */ if( info.nPayload>info.nLocal ){ - int nPage; /* Number of pages on the overflow chain */ @@ -36,6 +36,6 @@ Pgno pgnoOvfl; /* First page of the overflow chain */ assert( pc + info.nSize - 4 <= usableSize ); nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4); --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0009-Fix-Heap-use-after-free-in-releasePageNotNull.patch b/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch similarity index 92% rename from third_party/sqlite/patches/0009-Fix-Heap-use-after-free-in-releasePageNotNull.patch rename to third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch index d2f603319..cce0e58 100644 --- a/third_party/sqlite/patches/0009-Fix-Heap-use-after-free-in-releasePageNotNull.patch +++ b/third_party/sqlite/patches/0008-Fix-Heap-use-after-free-in-releasePageNotNull.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Tue, 12 Mar 2019 17:30:33 -0700 -Subject: [PATCH 09/12] Fix Heap-use-after-free in releasePageNotNull +Subject: [PATCH 08/11] Fix Heap-use-after-free in releasePageNotNull This backports https://www.sqlite.org/src/info/b0d5cf40bba34e45 @@ -28,6 +28,6 @@ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( pPager->tempFile ){ /* Do not discard pages from an in-memory database since we might --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0010-Fix-dangling-pointer-dereference.patch b/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch similarity index 95% rename from third_party/sqlite/patches/0010-Fix-dangling-pointer-dereference.patch rename to third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch index 728c884..d9b34b88 100644 --- a/third_party/sqlite/patches/0010-Fix-dangling-pointer-dereference.patch +++ b/third_party/sqlite/patches/0009-Fix-dangling-pointer-dereference.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Thu, 21 Mar 2019 13:19:11 -0700 -Subject: [PATCH 10/12] Fix dangling pointer dereference +Subject: [PATCH 09/11] Fix dangling pointer dereference This backports https://www.sqlite.org/src/info/b9e2393cf201e3fc @@ -32,7 +32,7 @@ @@ -79,7 +79,31 @@ do_execsql_test 3.2 { SELECT sql FROM sqlite_master WHERE name = 'v1' } {{CREATE VIEW v1 AS SELECT * FROM t1 WHERE a=1 OR (bbb IN ())}} - + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 5.0 { @@ -44,7 +44,7 @@ +do_execsql_test 5.1 { + ALTER TABLE t1 RENAME c1 TO c3; +} - + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 6.0 { @@ -58,9 +58,9 @@ +do_execsql_test 6.1 { + ALTER TABLE Table0 RENAME Col0 TO Col0; +} - + finish_test - --- -2.21.0.392.gf8f6787159e-goog + +-- +2.20.1
diff --git a/third_party/sqlite/patches/0011-Fix-faulty-assert-statement.patch b/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch similarity index 92% rename from third_party/sqlite/patches/0011-Fix-faulty-assert-statement.patch rename to third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch index 3f53c85..a0c2235 100644 --- a/third_party/sqlite/patches/0011-Fix-faulty-assert-statement.patch +++ b/third_party/sqlite/patches/0010-Fix-faulty-assert-statement.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Wed, 27 Mar 2019 12:05:31 -0700 -Subject: [PATCH 11/12] Fix faulty assert statement +Subject: [PATCH 10/11] Fix faulty assert statement This backports https://www.sqlite.org/src/info/bcbe7d96df3c9515 @@ -24,6 +24,6 @@ || rc!=SQLITE_OK ); copyNodeContent(apNew[0], pParent, &rc); --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/third_party/sqlite/patches/0012-Add-dbfuzz2-progress-handler-patch.patch b/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch similarity index 96% rename from third_party/sqlite/patches/0012-Add-dbfuzz2-progress-handler-patch.patch rename to third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch index f141ac5..32fb274 100644 --- a/third_party/sqlite/patches/0012-Add-dbfuzz2-progress-handler-patch.patch +++ b/third_party/sqlite/patches/0011-Add-dbfuzz2-progress-handler-patch.patch
@@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darwin Huang <huangdarwin@chromium.org> Date: Wed, 27 Mar 2019 12:10:17 -0700 -Subject: [PATCH 12/12] Add dbfuzz2 progress handler patch +Subject: [PATCH 11/11] Add dbfuzz2 progress handler patch This backports https://www.sqlite.org/src/info/b99f8512c06b9d47 @@ -17,7 +17,7 @@ @@ -74,6 +74,10 @@ static int bVdbeDebug = 0; /* Maximum size of the in-memory database file */ static sqlite3_int64 szMax = 104857600; - + +/* Progress handler callback data */ +static int nCb = 0; /* Number of callbacks seen so far */ +static int mxCb = 250000; /* Maximum allowed callbacks */ @@ -28,7 +28,7 @@ @@ -157,6 +161,21 @@ int sqlite3MemTraceDeactivate(void){ } /***** End copy/paste from ext/misc/memtrace.c ***************************/ - + +/* +** Progress handler callback +** @@ -79,6 +79,6 @@ if( strcmp(z,"memtrace")==0 ){ sqlite3MemTraceActivate(stdout); continue; --- -2.21.0.392.gf8f6787159e-goog +-- +2.20.1
diff --git a/tools/chrome_proxy/webdriver/lite_page.py b/tools/chrome_proxy/webdriver/lite_page.py index c247792b..5afe2da 100644 --- a/tools/chrome_proxy/webdriver/lite_page.py +++ b/tools/chrome_proxy/webdriver/lite_page.py
@@ -30,7 +30,9 @@ test_driver.AddChromeArg('--enable-spdy-proxy-auth') test_driver.AddChromeArg('--enable-features=NetworkQualityEstimator' '<NetworkQualityEstimator,' - 'Previews,DataReductionProxyDecidesTransform') + 'Previews,DataReductionProxyDecidesTransform,' + 'NetworkService,' + 'DataReductionProxyEnabledWithNetworkService') test_driver.AddChromeArg( '--force-fieldtrial-params=NetworkQualityEstimator.Enabled:' 'force_effective_connection_type/2G,' @@ -43,11 +45,14 @@ test_driver.LoadURL('http://check.googlezip.net/test.html') lite_page_responses = 0 + checked_chrome_proxy_header = False for response in test_driver.GetHTTPResponses(): - # Verify client sends ignore directive on every request for session. - self.assertIn('exp=ignore_preview_blacklist', - response.request_headers['chrome-proxy']) - self.assertEqual('2G', response.request_headers['chrome-proxy-ect']) + if response.request_headers: + # Verify client sends ignore directive on main frame request. + self.assertIn('exp=ignore_preview_blacklist', + response.request_headers['chrome-proxy']) + self.assertEqual('2G', response.request_headers['chrome-proxy-ect']) + checked_chrome_proxy_header = True if response.url.endswith('html'): self.assertTrue(self.checkLitePageResponse(response)) lite_page_responses = lite_page_responses + 1 @@ -59,6 +64,7 @@ # No subresources should accept transforms. self.assertNotIn('chrome-proxy-accept-transform', response.request_headers) + self.assertTrue(checked_chrome_proxy_header) # Verify that a Lite Page response for the main frame was seen. self.assertEqual(1, lite_page_responses) @@ -286,7 +292,9 @@ test_driver.AddChromeArg('--enable-spdy-proxy-auth') test_driver.AddChromeArg('--enable-features=NetworkQualityEstimator' '<NetworkQualityEstimator,' - 'Previews,DataReductionProxyDecidesTransform') + 'Previews,DataReductionProxyDecidesTransform,' + 'NetworkService,' + 'DataReductionProxyEnabledWithNetworkService') test_driver.AddChromeArg('--force-fieldtrial-params=' 'NetworkQualityEstimator.Enabled:' 'force_effective_connection_type/Slow2G') @@ -405,7 +413,9 @@ with TestDriver() as test_driver: test_driver.AddChromeArg('--enable-spdy-proxy-auth') test_driver.AddChromeArg('--enable-features=NetworkQualityEstimator' - '<NetworkQualityEstimator') + '<NetworkQualityEstimator,' + 'NetworkService,' + 'DataReductionProxyEnabledWithNetworkService') test_driver.AddChromeArg('--force-fieldtrial-params=' 'NetworkQualityEstimator.Enabled:' 'force_effective_connection_type/2G') @@ -417,6 +427,8 @@ test_driver.LoadURL('http://check.googlezip.net/test.html') for response in test_driver.GetHTTPResponses(): + if not response.request_headers: + continue self.assertEqual('2G', response.request_headers['chrome-proxy-ect']) if response.url.endswith('html'): if ParseFlags().android:
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index 10b061f..4e9b284 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@ # Do NOT CHANGE this if you don't know what you're doing -- see # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md # Reverting problematic clang rolls is safe, though. -CLANG_REVISION = '356356' +CLANG_REVISION = '357316' use_head_revision = bool(os.environ.get('LLVM_FORCE_HEAD_REVISION', '0') in ('1', 'YES')) @@ -43,7 +43,7 @@ CLANG_REVISION = 'HEAD' # This is incremented when pushing a new build of Clang at the same revision. -CLANG_SUB_REVISION=3 +CLANG_SUB_REVISION=1 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
diff --git a/tools/metrics/common/pretty_print_xml.py b/tools/metrics/common/pretty_print_xml.py index 712bbd9..ae97ff7 100644 --- a/tools/metrics/common/pretty_print_xml.py +++ b/tools/metrics/common/pretty_print_xml.py
@@ -78,6 +78,11 @@ self.tags_that_allow_single_line = tags_that_allow_single_line self.tags_alphabetization_rules = tags_alphabetization_rules + self.wrapper = textwrap.TextWrapper() + self.wrapper.break_on_hyphens = False + self.wrapper.break_long_words = False + self.wrapper.width = WRAP_COLUMN + def PrettyPrintXml(self, tree): tree = self._TransformByAlphabetizing(tree) tree = self.PrettyPrintNode(tree) @@ -158,6 +163,127 @@ self._TransformByAlphabetizing(c) return node + def _PrettyPrintText(self, node, indent): + # Wrap each paragraph in the text to fit in the 80 column limit. + self.wrapper.initial_indent = ' ' * indent + self.wrapper.subsequent_indent = ' ' * indent + text = XmlEscape(node.data) + paragraphs = SplitParagraphs(text) + # Wrap each paragraph and separate with two newlines. + return '\n\n'.join(self.wrapper.fill(p) for p in paragraphs) + + def _PrettyPrintElement(self, node, indent): + # Check if tag name is valid. + if node.tagName not in self.attribute_order: + logging.error('Unrecognized tag "%s"', node.tagName) + raise Error('Unrecognized tag "%s"', node.tagName) + + # Newlines. + newlines_after_open, newlines_before_close, newlines_after_close = ( + self.tags_that_have_extra_newline.get(node.tagName, (1, 1, 0))) + # Open the tag. + s = ' ' * indent + '<' + node.tagName + + # Calculate how much space to allow for the '>' or '/>'. + closing_chars = 1 + if not node.childNodes: + closing_chars = 2 + + attributes = node.attributes.keys() + required_attributes = [attribute for attribute in self.required_attributes + if attribute in self.attribute_order[node.tagName]] + missing_attributes = [attribute for attribute in required_attributes + if attribute not in attributes] + + for attribute in missing_attributes: + logging.error( + 'Missing attribute "%s" in tag "%s"', attribute, node.tagName) + if missing_attributes: + missing_attributes_str = ( + ', '.join('"%s"' % attribute for attribute in missing_attributes)) + present_attributes = [ + ' {0}="{1}"'.format(name, value) + for name, value in node.attributes.items()] + node_str = '<{0}{1}>'.format(node.tagName, ''.join(present_attributes)) + raise Error( + 'Missing attributes {0} in tag "{1}"'.format( + missing_attributes_str, node_str)) + + # Pretty-print the attributes. + if attributes: + # Reorder the attributes. + unrecognized_attributes = ( + [a for a in attributes + if a not in self.attribute_order[node.tagName]]) + attributes = [a for a in self.attribute_order[node.tagName] + if a in attributes] + + for a in unrecognized_attributes: + logging.error( + 'Unrecognized attribute "%s" in tag "%s"', a, node.tagName) + if unrecognized_attributes: + raise Error( + 'Unrecognized attributes {0} in tag "{1}"'.format( + ', '.join('"{0}"'.format(a) for a in unrecognized_attributes), + node.tagName)) + + for a in attributes: + value = XmlEscape(node.attributes[a].value) + # Replace sequences of whitespace with single spaces. + words = value.split() + a_str = ' %s="%s"' % (a, ' '.join(words)) + # Start a new line if the attribute will make this line too long. + if LastLineLength(s) + len(a_str) + closing_chars > WRAP_COLUMN: + s += '\n' + ' ' * (indent + 3) + # Output everything up to the first quote. + s += ' %s="' % (a) + value_indent_level = LastLineLength(s) + # Output one word at a time, splitting to the next line where + # necessary. + column = value_indent_level + for i, word in enumerate(words): + # This is slightly too conservative since not every word will be + # followed by the closing characters... + if i > 0 and (column + len(word) + 1 + closing_chars > WRAP_COLUMN): + s = s.rstrip() # remove any trailing whitespace + s += '\n' + ' ' * value_indent_level + column = value_indent_level + s += word + ' ' + column += len(word) + 1 + s = s.rstrip() # remove any trailing whitespace + s += '"' + s = s.rstrip() # remove any trailing whitespace + + # Pretty-print the child nodes. + if node.childNodes: + s += '>' + # Calculate the new indent level for child nodes. + new_indent = indent + if node.tagName not in self.tags_that_dont_indent: + new_indent += 2 + child_nodes = node.childNodes + + # Recursively pretty-print the child nodes. + child_nodes = [self.PrettyPrintNode(n, indent=new_indent) + for n in child_nodes] + child_nodes = [c for c in child_nodes if c.strip()] + + # Determine whether we can fit the entire node on a single line. + close_tag = '</%s>' % node.tagName + space_left = WRAP_COLUMN - LastLineLength(s) - len(close_tag) + if (node.tagName in self.tags_that_allow_single_line and + len(child_nodes) == 1 and + len(child_nodes[0].strip()) <= space_left): + s += child_nodes[0].strip() + else: + s += '\n' * newlines_after_open + '\n'.join(child_nodes) + s += '\n' * newlines_before_close + ' ' * indent + s += close_tag + else: + s += '/>' + s += '\n' * newlines_after_close + return s + def PrettyPrintNode(self, node, indent=0): """Pretty-prints the given XML node at the given indent level. @@ -177,130 +303,11 @@ # Handle text nodes. if node.nodeType == xml.dom.minidom.Node.TEXT_NODE: - # Wrap each paragraph in the text to fit in the 80 column limit. - wrapper = textwrap.TextWrapper() - wrapper.initial_indent = ' ' * indent - wrapper.subsequent_indent = ' ' * indent - wrapper.break_on_hyphens = False - wrapper.break_long_words = False - wrapper.width = WRAP_COLUMN - text = XmlEscape(node.data) - paragraphs = SplitParagraphs(text) - # Wrap each paragraph and separate with two newlines. - return '\n\n'.join(wrapper.fill(p) for p in paragraphs) + return self._PrettyPrintText(node, indent) # Handle element nodes. if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: - # Check if tag name is valid. - if node.tagName not in self.attribute_order: - logging.error('Unrecognized tag "%s"', node.tagName) - raise Error('Unrecognized tag "%s"', node.tagName) - - # Newlines. - newlines_after_open, newlines_before_close, newlines_after_close = ( - self.tags_that_have_extra_newline.get(node.tagName, (1, 1, 0))) - # Open the tag. - s = ' ' * indent + '<' + node.tagName - - # Calculate how much space to allow for the '>' or '/>'. - closing_chars = 1 - if not node.childNodes: - closing_chars = 2 - - attributes = node.attributes.keys() - required_attributes = [attribute for attribute in self.required_attributes - if attribute in self.attribute_order[node.tagName]] - missing_attributes = [attribute for attribute in required_attributes - if attribute not in attributes] - - for attribute in missing_attributes: - logging.error( - 'Missing attribute "%s" in tag "%s"', attribute, node.tagName) - if missing_attributes: - missing_attributes_str = ( - ', '.join('"%s"' % attribute for attribute in missing_attributes)) - present_attributes = [ - ' {0}="{1}"'.format(name, value) - for name, value in node.attributes.items()] - node_str = '<{0}{1}>'.format(node.tagName, ''.join(present_attributes)) - raise Error( - 'Missing attributes {0} in tag "{1}"'.format( - missing_attributes_str, node_str)) - - # Pretty-print the attributes. - if attributes: - # Reorder the attributes. - unrecognized_attributes = ( - [a for a in attributes - if a not in self.attribute_order[node.tagName]]) - attributes = [a for a in self.attribute_order[node.tagName] - if a in attributes] - - for a in unrecognized_attributes: - logging.error( - 'Unrecognized attribute "%s" in tag "%s"', a, node.tagName) - if unrecognized_attributes: - raise Error( - 'Unrecognized attributes {0} in tag "{1}"'.format( - ', '.join('"{0}"'.format(a) for a in unrecognized_attributes), - node.tagName)) - - for a in attributes: - value = XmlEscape(node.attributes[a].value) - # Replace sequences of whitespace with single spaces. - words = value.split() - a_str = ' %s="%s"' % (a, ' '.join(words)) - # Start a new line if the attribute will make this line too long. - if LastLineLength(s) + len(a_str) + closing_chars > WRAP_COLUMN: - s += '\n' + ' ' * (indent + 3) - # Output everything up to the first quote. - s += ' %s="' % (a) - value_indent_level = LastLineLength(s) - # Output one word at a time, splitting to the next line where - # necessary. - column = value_indent_level - for i, word in enumerate(words): - # This is slightly too conservative since not every word will be - # followed by the closing characters... - if i > 0 and (column + len(word) + 1 + closing_chars > WRAP_COLUMN): - s = s.rstrip() # remove any trailing whitespace - s += '\n' + ' ' * value_indent_level - column = value_indent_level - s += word + ' ' - column += len(word) + 1 - s = s.rstrip() # remove any trailing whitespace - s += '"' - s = s.rstrip() # remove any trailing whitespace - - # Pretty-print the child nodes. - if node.childNodes: - s += '>' - # Calculate the new indent level for child nodes. - new_indent = indent - if node.tagName not in self.tags_that_dont_indent: - new_indent += 2 - child_nodes = node.childNodes - - # Recursively pretty-print the child nodes. - child_nodes = [self.PrettyPrintNode(n, indent=new_indent) - for n in child_nodes] - child_nodes = [c for c in child_nodes if c.strip()] - - # Determine whether we can fit the entire node on a single line. - close_tag = '</%s>' % node.tagName - space_left = WRAP_COLUMN - LastLineLength(s) - len(close_tag) - if (node.tagName in self.tags_that_allow_single_line and - len(child_nodes) == 1 and - len(child_nodes[0].strip()) <= space_left): - s += child_nodes[0].strip() - else: - s += '\n' * newlines_after_open + '\n'.join(child_nodes) - s += '\n' * newlines_before_close + ' ' * indent - s += close_tag - else: - s += '/>' - s += '\n' * newlines_after_close - return s + return self._PrettyPrintElement(node, indent) # Handle comment nodes. if node.nodeType == xml.dom.minidom.Node.COMMENT_NODE:
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 2e9e178..bb8a743 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -8510,10 +8510,7 @@ <enum name="CompositorFrameSinkSubmitResult"> <int value="0" label="Accepted"/> <int value="1" label="CopyOutputResults not allowed"/> - <int value="2" label="Surface Invariants Violation (deprecated)"/> - <int value="3" label="Size mismatch"/> - <int value="4" label="SurfaceId sequence numbers decreased"/> - <int value="5" label="Surface owned by another client"/> + <int value="2" label="Surface Invariants Violation"/> </enum> <enum name="CompositorScrollResult"> @@ -22732,6 +22729,7 @@ <int value="43" label="Hid"/> <int value="44" label="IdleDetection"/> <int value="45" label="UnoptimizedLossyImages"/> + <int value="46" label="UnoptimizedLosslessImages"/> </enum> <enum name="FeedbackSource"> @@ -32718,6 +32716,7 @@ <int value="-966290456" label="WebAuthenticationCtap2:enabled"/> <int value="-965842218" label="MultiDeviceApi:disabled"/> <int value="-964676765" label="enable-accelerated-mjpeg-decode"/> + <int value="-957200826" label="enable-spdy-proxy-auth"/> <int value="-956696029" label="scheduler-configuration"/> <int value="-951394314" label="top-chrome-md"/> <int value="-950793721" label="TranslateUI2016Q2:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 622fc06d..9b1b5f8 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -119031,7 +119031,7 @@ <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnNewPassphrase" - enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M75"> + enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M77"> <owner>vitaliii@chromium.org</owner> <owner>treib@chromium.org</owner> <summary> @@ -119044,7 +119044,7 @@ <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnSuccessfulDecryption" - enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M75"> + enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M77"> <owner>vitaliii@chromium.org</owner> <owner>treib@chromium.org</owner> <summary> @@ -119057,7 +119057,7 @@ </histogram> <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup" - enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M75"> + enum="SyncCustomPassphraseKeyDerivationMethodState" expires_after="M77"> <owner>vitaliii@chromium.org</owner> <owner>treib@chromium.org</owner> <summary>
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json index 496be5c..3509983 100644 --- a/tools/perf/page_sets/data/system_health_mobile.json +++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -27,15 +27,15 @@ "browse:media:flickr_infinite_scroll": { "DEFAULT": "system_health_mobile_062.wprgo" }, + "browse:media:googleplaystore:2019": { + "DEFAULT": "system_health_mobile_4413a28712.wprgo" + }, "browse:media:imgur": { "DEFAULT": "system_health_mobile_035.wprgo" }, "browse:media:youtube": { "DEFAULT": "system_health_mobile_037.wprgo" }, - "browse:media:googleplaystore:2019": { - "DEFAULT": "system_health_mobile_4413a28712.wprgo" - }, "browse:news:cnn": { "DEFAULT": "system_health_mobile_014.wprgo" }, @@ -72,6 +72,9 @@ "browse:search:amp:2018": { "DEFAULT": "system_health_mobile_0483ae239d.wprgo" }, + "browse:search:amp:sxg:2019": { + "DEFAULT": "system_health_mobile_d7a7409e72.wprgo" + }, "browse:shopping:amazon": { "DEFAULT": "system_health_mobile_053.wprgo" }, @@ -294,4 +297,4 @@ }, "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.", "platform_specific": true -} +} \ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_d7a7409e72.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_d7a7409e72.wprgo.sha1 new file mode 100644 index 0000000..c09f3aa6 --- /dev/null +++ b/tools/perf/page_sets/data/system_health_mobile_d7a7409e72.wprgo.sha1
@@ -0,0 +1 @@ +d7a7409e72e323d0b2c3b01e60b412c970702e6f \ No newline at end of file
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py index c91e49a..21eaf48 100644 --- a/tools/perf/page_sets/system_health/browsing_stories.py +++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -397,6 +397,32 @@ action_runner.ClickElement(element_function=element_function) action_runner.Wait(2) +class GoogleAmpSXGStory2019(_ArticleBrowsingStory): + """ Story for Google's Signed Exchange (SXG) Accelerated Mobile Pages (AMP). + """ + NAME = 'browse:search:amp:sxg:2019' + # Specific URL for site that supports SXG, travel.yahoo.co.jp + # pylint: disable=line-too-long + URL='https://www.google.com/search?q=%E5%85%AD%E6%9C%AC%E6%9C%A8%E3%80%80%E3%83%A4%E3%83%95%E3%83%BC%E3%80%80%E3%83%9B%E3%83%86%E3%83%AB&esrch=SignedExchange::Demo' + # Need to find the SXG AMPlink in the results + ITEM_SELECTOR = 'a > div > span[aria-label="AMP logo"]' + SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY + TAGS = [story_tags.YEAR_2019] + + def _DidLoadDocument(self, action_runner): + # Waiting manually for the search results to load here and below. + # Telemetry's action_runner.WaitForNavigate has some difficulty with amp + # pages as it waits for a frameId without a parent id. + action_runner.Wait(2) + # Click on the yahoo amp link and then just wait for it to load. + element_function = js_template.Render( + 'document.querySelectorAll({{ selector }})[{{ index }}]', + selector=self.ITEM_SELECTOR, index=0) + action_runner.WaitForElement(element_function=element_function) + action_runner.ClickElement(element_function=element_function) + # Waiting for the document to fully render + action_runner.Wait(2) + class GoogleDesktopStory2018(_ArticleBrowsingStory): """
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index 5eaf229..128568358 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -244,10 +244,8 @@ "java/src/org/chromium/ui/base/ActivityWindowAndroid.java", "java/src/org/chromium/ui/base/AndroidPermissionDelegate.java", "java/src/org/chromium/ui/base/Clipboard.java", - "java/src/org/chromium/ui/base/CursorObserver.java", "java/src/org/chromium/ui/base/DeviceFormFactor.java", "java/src/org/chromium/ui/base/EventForwarder.java", - "java/src/org/chromium/ui/base/TouchlessEventHandler.java", "java/src/org/chromium/ui/base/IdleDetector.java", "java/src/org/chromium/ui/base/LocalizationUtils.java", "java/src/org/chromium/ui/base/PermissionCallback.java", @@ -312,6 +310,8 @@ "java/src/org/chromium/ui/resources/system/SystemResourceLoader.java", "java/src/org/chromium/ui/text/NoUnderlineClickableSpan.java", "java/src/org/chromium/ui/text/SpanApplier.java", + "java/src/org/chromium/ui/touchless/CursorObserver.java", + "java/src/org/chromium/ui/touchless/TouchlessEventHandler.java", "java/src/org/chromium/ui/widget/AnchoredPopupWindow.java", "java/src/org/chromium/ui/widget/ButtonCompat.java", "java/src/org/chromium/ui/widget/CheckableImageView.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java index 1599613..d383c50 100644 --- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java +++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
@@ -22,6 +22,7 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.compat.ApiHelperForN; import org.chromium.blink_public.web.WebCursorInfoType; +import org.chromium.ui.touchless.TouchlessEventHandler; /** * Class to acquire, position, and remove anchor views from the implementing View.
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java index 74cb1b6..f558a4f8 100644 --- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -44,6 +44,8 @@ import org.chromium.ui.VSyncMonitor; import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.display.DisplayAndroid.DisplayAndroidObserver; +import org.chromium.ui.touchless.CursorObserver; +import org.chromium.ui.touchless.TouchlessEventHandler; import org.chromium.ui.widget.Toast; import java.lang.ref.WeakReference;
diff --git a/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java index 2e4d8af..12a51d2 100644 --- a/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java +++ b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogManager.java
@@ -10,6 +10,7 @@ import android.util.SparseArray; import org.chromium.base.Callback; +import org.chromium.base.ObserverList; import org.chromium.base.VisibleForTesting; import org.chromium.ui.modelutil.PropertyModel; @@ -25,6 +26,24 @@ */ public class ModalDialogManager { /** + * An observer of the ModalDialogManager intended to broadcast notifications about any dialog + * being shown. Observers will know if something is overlaying the screen. + */ + public interface ModalDialogManagerObserver { + /** + * A notification that the manager started showing a modal dialog. + * @param model The model that describes the dialog that was shown. + */ + void onDialogShown(PropertyModel model); + + /** + * A notification that the manager hid a modal dialog. + * @param model The model that describes the dialog that was hidden. + */ + void onDialogHidden(PropertyModel model); + } + + /** * Present a {@link PropertyModel} in a container. */ public static abstract class Presenter { @@ -135,6 +154,9 @@ */ private boolean mDismissingCurrentDialog; + /** Observers of this manager. */ + private final ObserverList<ModalDialogManagerObserver> mObserverList = new ObserverList<>(); + /** * Constructor for initializing default {@link Presenter}. * @param defaultPresenter The default presenter to be used when no presenter specified. @@ -149,6 +171,23 @@ /** Clears any dependencies on the showing or pending dialogs. */ public void destroy() { dismissAllDialogs(DialogDismissalCause.ACTIVITY_DESTROYED); + mObserverList.clear(); + } + + /** + * Add an observer to this manager. + * @param observer The observer to add. + */ + public void addObserver(ModalDialogManagerObserver observer) { + mObserverList.addObserver(observer); + } + + /** + * Remove an observer of this manager. + * @param observer The observer to remove. + */ + public void removeObserver(ModalDialogManagerObserver observer) { + mObserverList.removeObserver(observer); } /** @@ -207,6 +246,7 @@ mCurrentPresenter = mPresenters.get(dialogType, mDefaultPresenter); mCurrentPresenter.setDialogModel( model, (dismissalCause) -> dismissDialog(model, dismissalCause)); + for (ModalDialogManagerObserver o : mObserverList) o.onDialogShown(model); } /** @@ -240,6 +280,7 @@ if (mDismissingCurrentDialog) return; mDismissingCurrentDialog = true; model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause); + for (ModalDialogManagerObserver o : mObserverList) o.onDialogHidden(model); mCurrentPresenter.setDialogModel(null, null); mCurrentPresenter = null; mDismissingCurrentDialog = false;
diff --git a/ui/android/java/src/org/chromium/ui/base/CursorObserver.java b/ui/android/java/src/org/chromium/ui/touchless/CursorObserver.java similarity index 90% rename from ui/android/java/src/org/chromium/ui/base/CursorObserver.java rename to ui/android/java/src/org/chromium/ui/touchless/CursorObserver.java index ac8d58e..f94424e 100644 --- a/ui/android/java/src/org/chromium/ui/base/CursorObserver.java +++ b/ui/android/java/src/org/chromium/ui/touchless/CursorObserver.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.ui.base; +package org.chromium.ui.touchless; /** * Observer Android cursor state.
diff --git a/ui/android/java/src/org/chromium/ui/touchless/OWNERS b/ui/android/java/src/org/chromium/ui/touchless/OWNERS new file mode 100644 index 0000000..faf17b0 --- /dev/null +++ b/ui/android/java/src/org/chromium/ui/touchless/OWNERS
@@ -0,0 +1,3 @@ +mdjones@chromium.org +mthiesse@chromium.org +yfriedman@chromium.org
diff --git a/ui/android/java/src/org/chromium/ui/base/TouchlessEventHandler.java b/ui/android/java/src/org/chromium/ui/touchless/TouchlessEventHandler.java similarity index 76% rename from ui/android/java/src/org/chromium/ui/base/TouchlessEventHandler.java rename to ui/android/java/src/org/chromium/ui/touchless/TouchlessEventHandler.java index b3983e1..5720dad1 100644 --- a/ui/android/java/src/org/chromium/ui/base/TouchlessEventHandler.java +++ b/ui/android/java/src/org/chromium/ui/touchless/TouchlessEventHandler.java
@@ -2,14 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.ui.base; +package org.chromium.ui.touchless; /** - * org.chromium.ui.base.TouchlessEventHandler + * org.chromium.ui.touchless.TouchlessEventHandler */ public class TouchlessEventHandler { + /** + * Provides an interface for handling zoom in and zoom out requests for touchless devices. + */ + public interface TouchlessZoomCallback { + void onZoomInRequested(); + void onZoomOutRequested(); + } + private static final String EVENT_HANDLER_INTERNAL = - "org.chromium.ui.base.TouchlessEventHandlerInternal"; + "org.chromium.ui.touchless.TouchlessEventHandlerInternal"; private static TouchlessEventHandler sInstance; static { @@ -43,6 +51,14 @@ } } + public static void setZoomCallback(TouchlessZoomCallback callback) { + if (sInstance != null) sInstance.setZoomCallbackInternal(callback); + } + + public static void removeZoomCallback(TouchlessZoomCallback callback) { + if (sInstance != null) sInstance.removeZoomCallbackInternal(callback); + } + public static void onDidFinishNavigation() { if (sInstance != null) { sInstance.onDidFinishNavigationInternal(); @@ -74,6 +90,10 @@ protected void removeCursorObserverInternal(CursorObserver observer) {} + protected void setZoomCallbackInternal(TouchlessZoomCallback callback) {} + + protected void removeZoomCallbackInternal(TouchlessZoomCallback callback) {} + protected void onDidFinishNavigationInternal() {} protected void onActivityHiddenInternal() {}
diff --git a/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java b/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java index 241bc14a..3980d5b 100644 --- a/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java +++ b/ui/android/junit/src/org/chromium/ui/modaldialog/ModalDialogManagerTest.java
@@ -26,6 +26,7 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Feature; +import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogManagerObserver; import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType; import org.chromium.ui.modelutil.PropertyModel; @@ -48,11 +49,15 @@ private ModalDialogManager mModalDialogManager; private List<PropertyModel> mDialogModels = new ArrayList<>(); + @Mock + private ModalDialogManagerObserver mObserver; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mModalDialogManager = new ModalDialogManager(mAppModalPresenter, ModalDialogType.APP); mModalDialogManager.registerPresenter(mTabModalPresenter, ModalDialogType.TAB); + mModalDialogManager.addObserver(mObserver); for (int i = 0; i < MAX_DIALOGS; ++i) { ModalDialogProperties.Controller controller = new ModalDialogProperties.Controller() { @@ -69,6 +74,23 @@ } } + /** Tests that the events on the {@link ModalDialogManagerObserver} are called correctly. */ + @Test + @Feature({"ModalDialogManagerObserver"}) + public void testModalDialogObserver() { + // Show two dialogs and make sure show is only called on one until it is hidden. + verify(mObserver, times(0)).onDialogShown(mDialogModels.get(0)); + mModalDialogManager.showDialog(mDialogModels.get(0), ModalDialogType.APP); + mModalDialogManager.showDialog(mDialogModels.get(1), ModalDialogType.APP); + verify(mObserver, times(1)).onDialogShown(mDialogModels.get(0)); + verify(mObserver, times(0)).onDialogShown(mDialogModels.get(1)); + + verify(mObserver, times(0)).onDialogHidden(mDialogModels.get(0)); + mModalDialogManager.dismissDialog(mDialogModels.get(0), ModalDialogType.APP); + verify(mObserver, times(1)).onDialogHidden(mDialogModels.get(0)); + verify(mObserver, times(1)).onDialogShown(mDialogModels.get(1)); + } + /** Tests showing a dialog when no dialog is currently showing. */ @Test @Feature({"ModalDialog"})
diff --git a/ui/android/junit/src/org/chromium/ui/modaldialog/OWNERS b/ui/android/junit/src/org/chromium/ui/modaldialog/OWNERS new file mode 100644 index 0000000..ac64cd03 --- /dev/null +++ b/ui/android/junit/src/org/chromium/ui/modaldialog/OWNERS
@@ -0,0 +1,4 @@ +file://ui/android/java/src/org/chromium/ui/modaldialog/OWNERS + +# COMPONENT: UI>Browser>Mobile +# OS: Android
diff --git a/ui/file_manager/base/js/filtered_volume_manager.js b/ui/file_manager/base/js/filtered_volume_manager.js index 8d1f466..0fa54f01 100644 --- a/ui/file_manager/base/js/filtered_volume_manager.js +++ b/ui/file_manager/base/js/filtered_volume_manager.js
@@ -51,425 +51,422 @@ * for example, Drive volumes are dropped if Drive is disabled, and read-only * volumes are dropped in save-as dialogs. * - * @constructor - * @extends {cr.EventTarget} * @implements {VolumeManager} - * - * @param {!AllowedPaths} allowedPaths Which paths are supported in the Files - * app dialog. - * @param {boolean} writableOnly If true, only writable volumes are returned. - * @param {Window=} opt_backgroundPage Window object of the background - * page. If this is specified, the class skips to get background page. - * TODO(hirono): Let all clients of the class pass the background page and - * make the argument not optional. */ -function FilteredVolumeManager(allowedPaths, writableOnly, opt_backgroundPage) { - cr.EventTarget.call(this); +class FilteredVolumeManager extends cr.EventTarget { + /** + * + * @param {!AllowedPaths} allowedPaths Which paths are supported in the Files + * app dialog. + * @param {boolean} writableOnly If true, only writable volumes are returned. + * @param {Window=} opt_backgroundPage Window object of the background + * page. If this is specified, the class skips to get background page. + * TODO(hirono): Let all clients of the class pass the background page and + * make the argument not optional. + */ + constructor(allowedPaths, writableOnly, opt_backgroundPage) { + super(); - this.allowedPaths_ = allowedPaths; - this.writableOnly_ = writableOnly; - // Internal list holds filtered VolumeInfo instances. - /** @private */ - this.list_ = new cr.ui.ArrayDataModel([]); - // Public VolumeManager.volumeInfoList property accessed by callers. - this.volumeInfoList = new FilteredVolumeInfoList(this.list_); + this.allowedPaths_ = allowedPaths; + this.writableOnly_ = writableOnly; + // Internal list holds filtered VolumeInfo instances. + /** @private */ + this.list_ = new cr.ui.ArrayDataModel([]); + // Public VolumeManager.volumeInfoList property accessed by callers. + this.volumeInfoList = new FilteredVolumeInfoList(this.list_); - this.volumeManager_ = null; - this.pendingTasks_ = []; - this.onEventBound_ = this.onEvent_.bind(this); - this.onVolumeInfoListUpdatedBound_ = this.onVolumeInfoListUpdated_.bind(this); + this.volumeManager_ = null; + this.pendingTasks_ = []; + this.onEventBound_ = this.onEvent_.bind(this); + this.onVolumeInfoListUpdatedBound_ = + this.onVolumeInfoListUpdated_.bind(this); - this.disposed_ = false; + this.disposed_ = false; - // Start initialize the VolumeManager. - const queue = new AsyncUtil.Queue(); + // Start initialize the VolumeManager. + const queue = new AsyncUtil.Queue(); - if (opt_backgroundPage) { - this.backgroundPage_ = opt_backgroundPage; - } else { - queue.run(callNextStep => { - chrome.runtime.getBackgroundPage( - /** @type {function(Window=)} */ (opt_backgroundPage => { - this.backgroundPage_ = opt_backgroundPage; - callNextStep(); - })); - }); - } - - queue.run(callNextStep => { - this.backgroundPage_.volumeManagerFactory.getInstance(volumeManager => { - this.onReady_(volumeManager); - callNextStep(); - }); - }); -} - -/** - * Extends cr.EventTarget. - */ -FilteredVolumeManager.prototype.__proto__ = cr.EventTarget.prototype; - -/** - * Checks if a volume type is allowed. - * - * Note that even if a volume type is allowed, a volume of that type might be - * disallowed for other restrictions. To check if a specific volume is allowed - * or not, use isAllowedVolume_() instead. - * - * @param {VolumeManagerCommon.VolumeType} volumeType - * @return {boolean} - */ -FilteredVolumeManager.prototype.isAllowedVolumeType_ = function(volumeType) { - switch (this.allowedPaths_) { - case AllowedPaths.ANY_PATH: - case AllowedPaths.ANY_PATH_OR_URL: - return true; - case AllowedPaths.NATIVE_OR_DRIVE_PATH: - return ( - VolumeManagerCommon.VolumeType.isNative(volumeType) || - volumeType == VolumeManagerCommon.VolumeType.DRIVE); - case AllowedPaths.NATIVE_PATH: - return VolumeManagerCommon.VolumeType.isNative(volumeType); - } - return false; -}; - -/** - * Checks if a volume is allowed. - * - * @param {!VolumeInfo} volumeInfo - * @return {boolean} - */ -FilteredVolumeManager.prototype.isAllowedVolume_ = function(volumeInfo) { - if (!this.isAllowedVolumeType_(volumeInfo.volumeType)) { - return false; - } - if (this.writableOnly_ && volumeInfo.isReadOnly) { - return false; - } - return true; -}; - -/** - * Called when the VolumeManager gets ready for post initialization. - * @param {VolumeManager} volumeManager The initialized VolumeManager instance. - * @private - */ -FilteredVolumeManager.prototype.onReady_ = function(volumeManager) { - if (this.disposed_) { - return; - } - - this.volumeManager_ = volumeManager; - - // Subscribe to VolumeManager. - this.volumeManager_.addEventListener( - 'drive-connection-changed', this.onEventBound_); - this.volumeManager_.addEventListener( - 'externally-unmounted', this.onEventBound_); - this.volumeManager_.addEventListener( - VolumeManagerCommon.ARCHIVE_OPENED_EVENT_TYPE, this.onEventBound_); - - // Dispatch 'drive-connection-changed' to listeners, since the return value of - // FilteredVolumeManager.getDriveConnectionState() can be changed by setting - // this.volumeManager_. - cr.dispatchSimpleEvent(this, 'drive-connection-changed'); - - // Cache volumeInfoList. - const volumeInfoList = []; - for (var i = 0; i < this.volumeManager_.volumeInfoList.length; i++) { - const volumeInfo = this.volumeManager_.volumeInfoList.item(i); - // TODO(hidehiko): Filter mounted volumes located on Drive File System. - if (!this.isAllowedVolume_(volumeInfo)) { - continue; - } - volumeInfoList.push(volumeInfo); - } - this.list_.splice.apply( - this.list_, [0, this.volumeInfoList.length].concat(volumeInfoList)); - - // Subscribe to VolumeInfoList. - // In VolumeInfoList, we only use 'splice' event. - this.volumeManager_.volumeInfoList.addEventListener( - 'splice', this.onVolumeInfoListUpdatedBound_); - - // Run pending tasks. - const pendingTasks = this.pendingTasks_; - this.pendingTasks_ = null; - for (var i = 0; i < pendingTasks.length; i++) { - pendingTasks[i](); - } -}; - -/** - * Disposes the instance. After the invocation of this method, any other - * method should not be called. - */ -FilteredVolumeManager.prototype.dispose = function() { - this.disposed_ = true; - - if (!this.volumeManager_) { - return; - } - this.volumeManager_.removeEventListener( - 'drive-connection-changed', this.onEventBound_); - this.volumeManager_.removeEventListener( - 'externally-unmounted', this.onEventBound_); - this.volumeManager_.volumeInfoList.removeEventListener( - 'splice', this.onVolumeInfoListUpdatedBound_); -}; - -/** - * Called on events sent from VolumeManager. This has responsibility to - * re-dispatch the event to the listeners. - * @param {!Event} event Event object sent from VolumeManager. - * @private - */ -FilteredVolumeManager.prototype.onEvent_ = function(event) { - switch (event.type) { - case 'drive-connection-changed': - if (this.isAllowedVolumeType_(VolumeManagerCommon.VolumeType.DRIVE)) { - this.dispatchEvent(event); - } - break; - case 'externally-unmounted': - event = /** @type {!ExternallyUnmountedEvent} */ (event); - if (this.isAllowedVolume_(event.volumeInfo)) { - this.dispatchEvent(event); - } - break; - case VolumeManagerCommon.ARCHIVE_OPENED_EVENT_TYPE: - this.dispatchEvent(event); - break; - } -}; - -/** - * Called on events of modifying VolumeInfoList. - * @param {Event} event Event object sent from VolumeInfoList. - * @private - */ -FilteredVolumeManager.prototype.onVolumeInfoListUpdated_ = function(event) { - // Filters some volumes. - let index = event.index; - for (var i = 0; i < event.index; i++) { - var volumeInfo = this.volumeManager_.volumeInfoList.item(i); - if (!this.isAllowedVolume_(volumeInfo)) { - index--; - } - } - - let numRemovedVolumes = 0; - for (var i = 0; i < event.removed.length; i++) { - var volumeInfo = event.removed[i]; - if (this.isAllowedVolume_(volumeInfo)) { - numRemovedVolumes++; - } - } - - const addedVolumes = []; - for (var i = 0; i < event.added.length; i++) { - var volumeInfo = event.added[i]; - if (this.isAllowedVolume_(volumeInfo)) { - addedVolumes.push(volumeInfo); - } - } - - this.list_.splice.apply( - this.list_, [index, numRemovedVolumes].concat(addedVolumes)); -}; - -/** - * Returns whether the VolumeManager is initialized or not. - * @return {boolean} True if the VolumeManager is initialized. - */ -FilteredVolumeManager.prototype.isInitialized = function() { - return this.pendingTasks_ === null; -}; - -/** - * Ensures the VolumeManager is initialized, and then invokes callback. - * If the VolumeManager is already initialized, callback will be called - * immediately. - * @param {function()} callback Called on initialization completion. - */ -FilteredVolumeManager.prototype.ensureInitialized = function(callback) { - if (!this.isInitialized()) { - this.pendingTasks_.push(this.ensureInitialized.bind(this, callback)); - return; - } - - callback(); -}; - -/** - * @return {VolumeManagerCommon.DriveConnectionState} Current drive connection - * state. - */ -FilteredVolumeManager.prototype.getDriveConnectionState = function() { - if (!this.isAllowedVolumeType_(VolumeManagerCommon.VolumeType.DRIVE) || - !this.volumeManager_) { - return { - type: VolumeManagerCommon.DriveConnectionType.OFFLINE, - reason: VolumeManagerCommon.DriveConnectionReason.NO_SERVICE - }; - } - - return this.volumeManager_.getDriveConnectionState(); -}; - -/** @override */ -FilteredVolumeManager.prototype.getVolumeInfo = function(entry) { - return this.filterDisallowedVolume_( - this.volumeManager_ && this.volumeManager_.getVolumeInfo(entry)); -}; - -/** - * Obtains a volume information of the current profile. - * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. - * @return {VolumeInfo} Found volume info. - */ -FilteredVolumeManager.prototype.getCurrentProfileVolumeInfo = function( - volumeType) { - return this.filterDisallowedVolume_( - this.volumeManager_ && - this.volumeManager_.getCurrentProfileVolumeInfo(volumeType)); -}; - -/** @override */ -FilteredVolumeManager.prototype.getDefaultDisplayRoot = function(callback) { - this.ensureInitialized(() => { - const defaultVolume = this.getCurrentProfileVolumeInfo( - VolumeManagerCommon.VolumeType.DOWNLOADS); - if (defaultVolume) { - defaultVolume.resolveDisplayRoot(callback, () => { - // defaultVolume is DOWNLOADS and resolveDisplayRoot should succeed. - throw new Error( - 'Unexpectedly failed to obtain the default display root.'); - }); + if (opt_backgroundPage) { + this.backgroundPage_ = opt_backgroundPage; } else { - console.warn('Unexpectedly failed to obtain the default display root.'); - callback(null); + queue.run(callNextStep => { + chrome.runtime.getBackgroundPage( + /** @type {function(Window=)} */ (opt_backgroundPage => { + this.backgroundPage_ = opt_backgroundPage; + callNextStep(); + })); + }); } - }); -}; -/** - * Obtains location information from an entry. - * - * @param {(!Entry|!FilesAppEntry)} entry File or directory entry. - * @return {EntryLocation} Location information. - */ -FilteredVolumeManager.prototype.getLocationInfo = function(entry) { - const locationInfo = - this.volumeManager_ && this.volumeManager_.getLocationInfo(entry); - if (!locationInfo) { - return null; - } - if (locationInfo.volumeInfo && - !this.filterDisallowedVolume_(locationInfo.volumeInfo)) { - return null; - } - return locationInfo; -}; - -/** @override */ -FilteredVolumeManager.prototype.findByDevicePath = function(devicePath) { - for (let i = 0; i < this.volumeInfoList.length; i++) { - const volumeInfo = this.volumeInfoList.item(i); - if (volumeInfo.devicePath && volumeInfo.devicePath === devicePath) { - return this.filterDisallowedVolume_(volumeInfo); - } - } - return null; -}; - -/** - * Returns a promise that will be resolved when volume info, identified - * by {@code volumeId} is created. - * - * @param {string} volumeId - * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve - * if the volume is never mounted. - */ -FilteredVolumeManager.prototype.whenVolumeInfoReady = function(volumeId) { - return new Promise(resolve => { - this.volumeManager_.whenVolumeInfoReady(volumeId).then((volumeInfo) => { - volumeInfo = this.filterDisallowedVolume_(volumeInfo); - if (volumeInfo) { - resolve(volumeInfo); - } - }); - }); -}; - -/** - * Requests to mount the archive file. - * @param {string} fileUrl The path to the archive file to be mounted. - * @param {function(VolumeInfo)} successCallback Called with the VolumeInfo - * instance. - * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called when - * an error occurs. - */ -FilteredVolumeManager.prototype.mountArchive = function( - fileUrl, successCallback, errorCallback) { - if (this.pendingTasks_) { - this.pendingTasks_.push( - this.mountArchive.bind(this, fileUrl, successCallback, errorCallback)); - return; - } - - this.volumeManager_.mountArchive(fileUrl, successCallback, errorCallback); -}; - -/** - * Requests unmount the specified volume. - * @param {!VolumeInfo} volumeInfo Volume to be unmounted. - * @param {function()} successCallback Called on success. - * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called when - * an error occurs. - */ -FilteredVolumeManager.prototype.unmount = function( - volumeInfo, successCallback, errorCallback) { - if (this.pendingTasks_) { - this.pendingTasks_.push( - this.unmount.bind(this, volumeInfo, successCallback, errorCallback)); - return; - } - - this.volumeManager_.unmount(volumeInfo, successCallback, errorCallback); -}; - -/** - * Requests configuring of the specified volume. - * @param {!VolumeInfo} volumeInfo Volume to be configured. - * @return {!Promise} Fulfilled on success, otherwise rejected with an error - * message. - */ -FilteredVolumeManager.prototype.configure = function(volumeInfo) { - if (this.pendingTasks_) { - return new Promise((fulfill, reject) => { - this.pendingTasks_.push(() => { - return this.volumeManager_.configure(volumeInfo).then(fulfill, reject); + queue.run(callNextStep => { + this.backgroundPage_.volumeManagerFactory.getInstance(volumeManager => { + this.onReady_(volumeManager); + callNextStep(); }); }); } - return this.volumeManager_.configure(volumeInfo); -}; + /** + * Checks if a volume type is allowed. + * + * Note that even if a volume type is allowed, a volume of that type might be + * disallowed for other restrictions. To check if a specific volume is allowed + * or not, use isAllowedVolume_() instead. + * + * @param {VolumeManagerCommon.VolumeType} volumeType + * @return {boolean} + */ + isAllowedVolumeType_(volumeType) { + switch (this.allowedPaths_) { + case AllowedPaths.ANY_PATH: + case AllowedPaths.ANY_PATH_OR_URL: + return true; + case AllowedPaths.NATIVE_OR_DRIVE_PATH: + return ( + VolumeManagerCommon.VolumeType.isNative(volumeType) || + volumeType == VolumeManagerCommon.VolumeType.DRIVE); + case AllowedPaths.NATIVE_PATH: + return VolumeManagerCommon.VolumeType.isNative(volumeType); + } + return false; + } -/** - * Filters volume info by isAllowedVolume_(). - * - * @param {VolumeInfo} volumeInfo Volume info. - * @return {VolumeInfo} Null if the volume is disallowed. Otherwise just returns - * the volume. - * @private - */ -FilteredVolumeManager.prototype.filterDisallowedVolume_ = function(volumeInfo) { - if (volumeInfo && this.isAllowedVolume_(volumeInfo)) { - return volumeInfo; - } else { + /** + * Checks if a volume is allowed. + * + * @param {!VolumeInfo} volumeInfo + * @return {boolean} + */ + isAllowedVolume_(volumeInfo) { + if (!this.isAllowedVolumeType_(volumeInfo.volumeType)) { + return false; + } + if (this.writableOnly_ && volumeInfo.isReadOnly) { + return false; + } + return true; + } + + /** + * Called when the VolumeManager gets ready for post initialization. + * @param {VolumeManager} volumeManager The initialized VolumeManager + * instance. + * @private + */ + onReady_(volumeManager) { + if (this.disposed_) { + return; + } + + this.volumeManager_ = volumeManager; + + // Subscribe to VolumeManager. + this.volumeManager_.addEventListener( + 'drive-connection-changed', this.onEventBound_); + this.volumeManager_.addEventListener( + 'externally-unmounted', this.onEventBound_); + this.volumeManager_.addEventListener( + VolumeManagerCommon.ARCHIVE_OPENED_EVENT_TYPE, this.onEventBound_); + + // Dispatch 'drive-connection-changed' to listeners, since the return value + // of FilteredVolumeManager.getDriveConnectionState() can be changed by + // setting this.volumeManager_. + cr.dispatchSimpleEvent(this, 'drive-connection-changed'); + + // Cache volumeInfoList. + const volumeInfoList = []; + for (var i = 0; i < this.volumeManager_.volumeInfoList.length; i++) { + const volumeInfo = this.volumeManager_.volumeInfoList.item(i); + // TODO(hidehiko): Filter mounted volumes located on Drive File System. + if (!this.isAllowedVolume_(volumeInfo)) { + continue; + } + volumeInfoList.push(volumeInfo); + } + this.list_.splice.apply( + this.list_, [0, this.volumeInfoList.length].concat(volumeInfoList)); + + // Subscribe to VolumeInfoList. + // In VolumeInfoList, we only use 'splice' event. + this.volumeManager_.volumeInfoList.addEventListener( + 'splice', this.onVolumeInfoListUpdatedBound_); + + // Run pending tasks. + const pendingTasks = this.pendingTasks_; + this.pendingTasks_ = null; + for (var i = 0; i < pendingTasks.length; i++) { + pendingTasks[i](); + } + } + + /** + * Disposes the instance. After the invocation of this method, any other + * method should not be called. + */ + dispose() { + this.disposed_ = true; + + if (!this.volumeManager_) { + return; + } + this.volumeManager_.removeEventListener( + 'drive-connection-changed', this.onEventBound_); + this.volumeManager_.removeEventListener( + 'externally-unmounted', this.onEventBound_); + this.volumeManager_.volumeInfoList.removeEventListener( + 'splice', this.onVolumeInfoListUpdatedBound_); + } + + /** + * Called on events sent from VolumeManager. This has responsibility to + * re-dispatch the event to the listeners. + * @param {!Event} event Event object sent from VolumeManager. + * @private + */ + onEvent_(event) { + switch (event.type) { + case 'drive-connection-changed': + if (this.isAllowedVolumeType_(VolumeManagerCommon.VolumeType.DRIVE)) { + this.dispatchEvent(event); + } + break; + case 'externally-unmounted': + event = /** @type {!ExternallyUnmountedEvent} */ (event); + if (this.isAllowedVolume_(event.volumeInfo)) { + this.dispatchEvent(event); + } + break; + case VolumeManagerCommon.ARCHIVE_OPENED_EVENT_TYPE: + this.dispatchEvent(event); + break; + } + } + + /** + * Called on events of modifying VolumeInfoList. + * @param {Event} event Event object sent from VolumeInfoList. + * @private + */ + onVolumeInfoListUpdated_(event) { + // Filters some volumes. + let index = event.index; + for (var i = 0; i < event.index; i++) { + var volumeInfo = this.volumeManager_.volumeInfoList.item(i); + if (!this.isAllowedVolume_(volumeInfo)) { + index--; + } + } + + let numRemovedVolumes = 0; + for (var i = 0; i < event.removed.length; i++) { + var volumeInfo = event.removed[i]; + if (this.isAllowedVolume_(volumeInfo)) { + numRemovedVolumes++; + } + } + + const addedVolumes = []; + for (var i = 0; i < event.added.length; i++) { + var volumeInfo = event.added[i]; + if (this.isAllowedVolume_(volumeInfo)) { + addedVolumes.push(volumeInfo); + } + } + + this.list_.splice.apply( + this.list_, [index, numRemovedVolumes].concat(addedVolumes)); + } + + /** + * Returns whether the VolumeManager is initialized or not. + * @return {boolean} True if the VolumeManager is initialized. + */ + isInitialized() { + return this.pendingTasks_ === null; + } + + /** + * Ensures the VolumeManager is initialized, and then invokes callback. + * If the VolumeManager is already initialized, callback will be called + * immediately. + * @param {function()} callback Called on initialization completion. + */ + ensureInitialized(callback) { + if (!this.isInitialized()) { + this.pendingTasks_.push(this.ensureInitialized.bind(this, callback)); + return; + } + + callback(); + } + + /** + * @return {VolumeManagerCommon.DriveConnectionState} Current drive connection + * state. + */ + getDriveConnectionState() { + if (!this.isAllowedVolumeType_(VolumeManagerCommon.VolumeType.DRIVE) || + !this.volumeManager_) { + return { + type: VolumeManagerCommon.DriveConnectionType.OFFLINE, + reason: VolumeManagerCommon.DriveConnectionReason.NO_SERVICE + }; + } + + return this.volumeManager_.getDriveConnectionState(); + } + + /** @override */ + getVolumeInfo(entry) { + return this.filterDisallowedVolume_( + this.volumeManager_ && this.volumeManager_.getVolumeInfo(entry)); + } + + /** + * Obtains a volume information of the current profile. + * @param {VolumeManagerCommon.VolumeType} volumeType Volume type. + * @return {VolumeInfo} Found volume info. + */ + getCurrentProfileVolumeInfo(volumeType) { + return this.filterDisallowedVolume_( + this.volumeManager_ && + this.volumeManager_.getCurrentProfileVolumeInfo(volumeType)); + } + + /** @override */ + getDefaultDisplayRoot(callback) { + this.ensureInitialized(() => { + const defaultVolume = this.getCurrentProfileVolumeInfo( + VolumeManagerCommon.VolumeType.DOWNLOADS); + if (defaultVolume) { + defaultVolume.resolveDisplayRoot(callback, () => { + // defaultVolume is DOWNLOADS and resolveDisplayRoot should succeed. + throw new Error( + 'Unexpectedly failed to obtain the default display root.'); + }); + } else { + console.warn('Unexpectedly failed to obtain the default display root.'); + callback(null); + } + }); + } + + /** + * Obtains location information from an entry. + * + * @param {(!Entry|!FilesAppEntry)} entry File or directory entry. + * @return {EntryLocation} Location information. + */ + getLocationInfo(entry) { + const locationInfo = + this.volumeManager_ && this.volumeManager_.getLocationInfo(entry); + if (!locationInfo) { + return null; + } + if (locationInfo.volumeInfo && + !this.filterDisallowedVolume_(locationInfo.volumeInfo)) { + return null; + } + return locationInfo; + } + + /** @override */ + findByDevicePath(devicePath) { + for (let i = 0; i < this.volumeInfoList.length; i++) { + const volumeInfo = this.volumeInfoList.item(i); + if (volumeInfo.devicePath && volumeInfo.devicePath === devicePath) { + return this.filterDisallowedVolume_(volumeInfo); + } + } return null; } -}; + + /** + * Returns a promise that will be resolved when volume info, identified + * by {@code volumeId} is created. + * + * @param {string} volumeId + * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve + * if the volume is never mounted. + */ + whenVolumeInfoReady(volumeId) { + return new Promise(resolve => { + this.volumeManager_.whenVolumeInfoReady(volumeId).then((volumeInfo) => { + volumeInfo = this.filterDisallowedVolume_(volumeInfo); + if (volumeInfo) { + resolve(volumeInfo); + } + }); + }); + } + + /** + * Requests to mount the archive file. + * @param {string} fileUrl The path to the archive file to be mounted. + * @param {function(VolumeInfo)} successCallback Called with the VolumeInfo + * instance. + * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called + * when an error occurs. + */ + mountArchive(fileUrl, successCallback, errorCallback) { + if (this.pendingTasks_) { + this.pendingTasks_.push(this.mountArchive.bind( + this, fileUrl, successCallback, errorCallback)); + return; + } + + this.volumeManager_.mountArchive(fileUrl, successCallback, errorCallback); + } + + /** + * Requests unmount the specified volume. + * @param {!VolumeInfo} volumeInfo Volume to be unmounted. + * @param {function()} successCallback Called on success. + * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called + * when an error occurs. + */ + unmount(volumeInfo, successCallback, errorCallback) { + if (this.pendingTasks_) { + this.pendingTasks_.push( + this.unmount.bind(this, volumeInfo, successCallback, errorCallback)); + return; + } + + this.volumeManager_.unmount(volumeInfo, successCallback, errorCallback); + } + + /** + * Requests configuring of the specified volume. + * @param {!VolumeInfo} volumeInfo Volume to be configured. + * @return {!Promise} Fulfilled on success, otherwise rejected with an error + * message. + */ + configure(volumeInfo) { + if (this.pendingTasks_) { + return new Promise((fulfill, reject) => { + this.pendingTasks_.push(() => { + return this.volumeManager_.configure(volumeInfo) + .then(fulfill, reject); + }); + }); + } + + return this.volumeManager_.configure(volumeInfo); + } + + /** + * Filters volume info by isAllowedVolume_(). + * + * @param {VolumeInfo} volumeInfo Volume info. + * @return {VolumeInfo} Null if the volume is disallowed. Otherwise just + * returns the volume. + * @private + */ + filterDisallowedVolume_(volumeInfo) { + if (volumeInfo && this.isAllowedVolume_(volumeInfo)) { + return volumeInfo; + } else { + return null; + } + } +}
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js index 767e989..5ab1553b 100644 --- a/ui/file_manager/file_manager/foreground/js/directory_model.js +++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -10,1442 +10,1450 @@ /** * Data model of the file manager. - * - * @constructor - * @extends {cr.EventTarget} - * - * @param {boolean} singleSelection True if only one file could be selected - * at the time. - * @param {FileFilter} fileFilter Instance of FileFilter. - * @param {!MetadataModel} metadataModel Metadata model. - * service. - * @param {!VolumeManager} volumeManager The volume manager. - * @param {!FileOperationManager} fileOperationManager File operation manager. */ -function DirectoryModel( - singleSelection, fileFilter, metadataModel, volumeManager, - fileOperationManager) { - this.fileListSelection_ = singleSelection ? - new FileListSingleSelectionModel() : - new FileListSelectionModel(); - - this.runningScan_ = null; - this.pendingScan_ = null; - this.rescanTime_ = null; - this.scanFailures_ = 0; - this.changeDirectorySequence_ = 0; - +class DirectoryModel extends cr.EventTarget { /** - * @private {boolean} + * @param {boolean} singleSelection True if only one file could be selected + * at the time. + * @param {FileFilter} fileFilter Instance of FileFilter. + * @param {!MetadataModel} metadataModel Metadata model. + * service. + * @param {!VolumeManager} volumeManager The volume manager. + * @param {!FileOperationManager} fileOperationManager File operation manager. */ - this.ignoreCurrentDirectoryDeletion_ = false; + constructor( + singleSelection, fileFilter, metadataModel, volumeManager, + fileOperationManager) { + super(); - this.directoryChangeQueue_ = new AsyncUtil.Queue(); - this.rescanAggregator_ = - new AsyncUtil.Aggregator(this.rescanSoon.bind(this, true), 500); - - this.fileFilter_ = fileFilter; - this.fileFilter_.addEventListener( - 'changed', this.onFilterChanged_.bind(this)); - - this.currentFileListContext_ = - new FileListContext(fileFilter, metadataModel, volumeManager); - this.currentDirContents_ = - DirectoryContents.createForDirectory(this.currentFileListContext_, null); - /** - * Empty file list which is used as a dummy for inactive view of file list. - * @private {!FileListModel} - */ - this.emptyFileList_ = new FileListModel(metadataModel); - - this.metadataModel_ = metadataModel; - - this.volumeManager_ = volumeManager; - this.volumeManager_.volumeInfoList.addEventListener( - 'splice', this.onVolumeInfoListUpdated_.bind(this)); - - /** - * File watcher. - * @private {!FileWatcher} - * @const - */ - this.fileWatcher_ = new FileWatcher(); - this.fileWatcher_.addEventListener( - 'watcher-directory-changed', this.onWatcherDirectoryChanged_.bind(this)); - util.addEventListenerToBackgroundComponent( - fileOperationManager, 'entries-changed', - this.onEntriesChanged_.bind(this)); - - /** @private {string} */ - this.lastSearchQuery_ = ''; - - /** @private {FilesAppDirEntry} */ - this.myFilesEntry_ = null; -} - -/** - * DirectoryModel extends cr.EventTarget. - */ -DirectoryModel.prototype.__proto__ = cr.EventTarget.prototype; - -/** - * Disposes the directory model by removing file watchers. - */ -DirectoryModel.prototype.dispose = function() { - this.fileWatcher_.dispose(); -}; - -/** - * @return {FileListModel} Files in the current directory. - */ -DirectoryModel.prototype.getFileList = function() { - return this.currentFileListContext_.fileList; -}; - -/** - * @return {!FileListModel} File list which is always empty. - */ -DirectoryModel.prototype.getEmptyFileList = function() { - return this.emptyFileList_; -}; - -/** - * @return {!FileListSelectionModel|!FileListSingleSelectionModel} Selection - * in the fileList. - */ -DirectoryModel.prototype.getFileListSelection = function() { - return this.fileListSelection_; -}; - -/** - * Obtains current volume information. - * @return {VolumeInfo} - */ -DirectoryModel.prototype.getCurrentVolumeInfo = function() { - const entry = this.getCurrentDirEntry(); - if (!entry) { - return null; - } - return this.volumeManager_.getVolumeInfo(entry); -}; - -/** - * @return {?VolumeManagerCommon.RootType} Root type of current root, or null if - * not found. - */ -DirectoryModel.prototype.getCurrentRootType = function() { - const entry = this.currentDirContents_.getDirectoryEntry(); - if (!entry) { - return null; - } - - const locationInfo = this.volumeManager_.getLocationInfo(entry); - if (!locationInfo) { - return null; - } - - return locationInfo.rootType; -}; - -/** - * Metadata property names that are expected to be Prefetched. - * @return {!Array<string>} - */ -DirectoryModel.prototype.getPrefetchPropertyNames = function() { - return this.currentFileListContext_.prefetchPropertyNames; -}; - -/** - * @return {boolean} True if the current directory is read only. If there is - * no entry set, then returns true. - */ -DirectoryModel.prototype.isReadOnly = function() { - const currentDirEntry = this.getCurrentDirEntry(); - if (currentDirEntry) { - const locationInfo = this.volumeManager_.getLocationInfo(currentDirEntry); - if (locationInfo) { - return locationInfo.isReadOnly; - } - } - return true; -}; - -/** - * @return {boolean} True if the a scan is active. - */ -DirectoryModel.prototype.isScanning = function() { - return this.currentDirContents_.isScanning(); -}; - -/** - * @return {boolean} True if search is in progress. - */ -DirectoryModel.prototype.isSearching = function() { - return this.currentDirContents_.isSearch(); -}; - -/** - * @return {boolean} True if it's on Drive. - */ -DirectoryModel.prototype.isOnDrive = function() { - return this.isCurrentRootVolumeType_(VolumeManagerCommon.VolumeType.DRIVE); -}; - -/** - * @return {boolean} True if it's on MTP volume. - */ -DirectoryModel.prototype.isOnMTP = function() { - return this.isCurrentRootVolumeType_(VolumeManagerCommon.VolumeType.MTP); -}; - -/** - * @param {VolumeManagerCommon.VolumeType} volumeType Volume Type - * @return {boolean} True if current root volume type is equal to specified - * volume type. - * @private - */ -DirectoryModel.prototype.isCurrentRootVolumeType_ = function(volumeType) { - const rootType = this.getCurrentRootType(); - return rootType != null && rootType != VolumeManagerCommon.RootType.RECENT && - VolumeManagerCommon.getVolumeTypeFromRootType(rootType) === volumeType; -}; - -/** - * Updates the selection by using the updateFunc and publish the change event. - * If updateFunc returns true, it force to dispatch the change event even if the - * selection index is not changed. - * - * @param {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} selection - * Selection to be updated. - * @param {function(): boolean} updateFunc Function updating the selection. - * @private - */ -DirectoryModel.prototype.updateSelectionAndPublishEvent_ = - (selection, updateFunc) => { - // Begin change. - selection.beginChange(); - - // If dispatchNeeded is true, we should ensure the change event is - // dispatched. - let dispatchNeeded = updateFunc(); - - // Check if the change event is dispatched in the endChange function - // or not. - const eventDispatched = () => { - dispatchNeeded = false; - }; - selection.addEventListener('change', eventDispatched); - selection.endChange(); - selection.removeEventListener('change', eventDispatched); - - // If the change event have been already dispatched, dispatchNeeded is - // false. - if (dispatchNeeded) { - const event = new Event('change'); - // The selection status (selected or not) is not changed because - // this event is caused by the change of selected item. - event.changes = []; - selection.dispatchEvent(event); - } - }; - -/** - * Sets to ignore current directory deletion. This method is used to prevent - * going up to the volume root with the deletion of current directory by rename - * operation in directory tree. - * @param {boolean} value True to ignore current directory deletion. - */ -DirectoryModel.prototype.setIgnoringCurrentDirectoryDeletion = function(value) { - this.ignoreCurrentDirectoryDeletion_ = value; -}; - -/** - * Invoked when a change in the directory is detected by the watcher. - * @param {Event} event Event object. - * @private - */ -DirectoryModel.prototype.onWatcherDirectoryChanged_ = function(event) { - const directoryEntry = this.getCurrentDirEntry(); - - if (!this.ignoreCurrentDirectoryDeletion_) { - // If the change is deletion of currentDir, move up to its parent directory. - directoryEntry.getDirectory( - directoryEntry.fullPath, {create: false}, () => {}, () => { - const volumeInfo = - this.volumeManager_.getVolumeInfo(assert(directoryEntry)); - if (volumeInfo) { - volumeInfo.resolveDisplayRoot().then(displayRoot => { - this.changeDirectoryEntry(displayRoot); - }); - } - }); - } - - if (event.changedFiles) { - const addedOrUpdatedFileUrls = []; - let deletedFileUrls = []; - event.changedFiles.forEach(change => { - if (change.changes.length === 1 && change.changes[0] === 'delete') { - deletedFileUrls.push(change.url); - } else { - addedOrUpdatedFileUrls.push(change.url); - } - }); - - util.URLsToEntries(addedOrUpdatedFileUrls) - .then(result => { - deletedFileUrls = deletedFileUrls.concat(result.failureUrls); - - // Passing the resolved entries and failed URLs as the removed files. - // The URLs are removed files and they chan't be resolved. - this.partialUpdate_(result.entries, deletedFileUrls); - }) - .catch(error => { - console.error( - 'Error in proceeding the changed event.', error, - 'Fallback to force-refresh'); - this.rescanAggregator_.run(); - }); - } else { - // Invokes force refresh if the detailed information isn't provided. - // This can occur very frequently (e.g. when copying files into Downlaods) - // and rescan is heavy operation, so we keep some interval for each rescan. - this.rescanAggregator_.run(); - } -}; - -/** - * Invoked when filters are changed. - * @private - */ -DirectoryModel.prototype.onFilterChanged_ = function() { - const currentDirectory = this.getCurrentDirEntry(); - if (currentDirectory && util.isNativeEntry(currentDirectory) && - !this.fileFilter_.filter( - /** @type {!DirectoryEntry} */ (currentDirectory))) { - // If the current directory should be hidden in the new filter setting, - // change the current directory to the current volume's root. - const volumeInfo = this.volumeManager_.getVolumeInfo(currentDirectory); - if (volumeInfo) { - volumeInfo.resolveDisplayRoot().then(displayRoot => { - this.changeDirectoryEntry(displayRoot); - }); - } - } else { - this.rescanSoon(false); - } -}; - -/** - * Returns the filter. - * @return {FileFilter} The file filter. - */ -DirectoryModel.prototype.getFileFilter = function() { - return this.fileFilter_; -}; - -/** - * @return {DirectoryEntry|FakeEntry|FilesAppDirEntry} Current directory. - */ -DirectoryModel.prototype.getCurrentDirEntry = function() { - return this.currentDirContents_.getDirectoryEntry(); -}; - -/** - * @return {Array<Entry>} Array of selected entries. - * @private - */ -DirectoryModel.prototype.getSelectedEntries_ = function() { - const indexes = this.fileListSelection_.selectedIndexes; - const fileList = this.getFileList(); - if (fileList) { - return indexes.map(i => { - return fileList.item(i); - }); - } - return []; -}; - -/** - * @param {Array<Entry>} value List of selected entries. - * @private - */ -DirectoryModel.prototype.setSelectedEntries_ = function(value) { - const indexes = []; - const fileList = this.getFileList(); - const urls = util.entriesToURLs(value); - - for (let i = 0; i < fileList.length; i++) { - if (urls.indexOf(fileList.item(i).toURL()) !== -1) { - indexes.push(i); - } - } - this.fileListSelection_.selectedIndexes = indexes; -}; - -/** - * @return {Entry} Lead entry. - * @private - */ -DirectoryModel.prototype.getLeadEntry_ = function() { - const index = this.fileListSelection_.leadIndex; - return index >= 0 ? - /** @type {Entry} */ (this.getFileList().item(index)) : - null; -}; - -/** - * @param {Entry} value The new lead entry. - * @private - */ -DirectoryModel.prototype.setLeadEntry_ = function(value) { - const fileList = this.getFileList(); - for (let i = 0; i < fileList.length; i++) { - if (util.isSameEntry(/** @type {Entry} */ (fileList.item(i)), value)) { - this.fileListSelection_.leadIndex = i; - return; - } - } -}; - -/** - * Schedule rescan with short delay. - * @param {boolean} refresh True to refresh metadata, or false to use cached - * one. - */ -DirectoryModel.prototype.rescanSoon = function(refresh) { - this.scheduleRescan(SHORT_RESCAN_INTERVAL, refresh); -}; - -/** - * Schedule rescan with delay. Designed to handle directory change - * notification. - * @param {boolean} refresh True to refresh metadata, or false to use cached - * one. - */ -DirectoryModel.prototype.rescanLater = function(refresh) { - this.scheduleRescan(SIMULTANEOUS_RESCAN_INTERVAL, refresh); -}; - -/** - * Schedule rescan with delay. If another rescan has been scheduled does - * nothing. File operation may cause a few notifications what should cause - * a single refresh. - * @param {number} delay Delay in ms after which the rescan will be performed. - * @param {boolean} refresh True to refresh metadata, or false to use cached - * one. - */ -DirectoryModel.prototype.scheduleRescan = function(delay, refresh) { - if (this.rescanTime_) { - if (this.rescanTime_ <= Date.now() + delay) { - return; - } - clearTimeout(this.rescanTimeoutId_); - } - - const sequence = this.changeDirectorySequence_; - - this.rescanTime_ = Date.now() + delay; - this.rescanTimeoutId_ = setTimeout(() => { - this.rescanTimeoutId_ = null; - if (sequence === this.changeDirectorySequence_) { - this.rescan(refresh); - } - }, delay); -}; - -/** - * Cancel a rescan on timeout if it is scheduled. - * @private - */ -DirectoryModel.prototype.clearRescanTimeout_ = function() { - this.rescanTime_ = null; - if (this.rescanTimeoutId_) { - clearTimeout(this.rescanTimeoutId_); - this.rescanTimeoutId_ = null; - } -}; - -/** - * Rescan current directory. May be called indirectly through rescanLater or - * directly in order to reflect user action. Will first cache all the directory - * contents in an array, then seamlessly substitute the fileList contents, - * preserving the select element etc. - * - * This should be to scan the contents of current directory (or search). - * - * @param {boolean} refresh True to refresh metadata, or false to use cached - * one. - */ -DirectoryModel.prototype.rescan = function(refresh) { - this.clearRescanTimeout_(); - if (this.runningScan_) { - this.pendingRescan_ = true; - return; - } - - const dirContents = this.currentDirContents_.clone(); - dirContents.setFileList(new FileListModel(this.metadataModel_)); - dirContents.setMetadataSnapshot( - this.currentDirContents_.createMetadataSnapshot()); - - const sequence = this.changeDirectorySequence_; - - const successCallback = () => { - if (sequence === this.changeDirectorySequence_) { - this.replaceDirectoryContents_(dirContents); - cr.dispatchSimpleEvent(this, 'rescan-completed'); - } - }; - - this.scan_( - dirContents, refresh, successCallback, () => {}, () => {}, () => {}); -}; - -/** - * Run scan on the current DirectoryContents. The active fileList is cleared and - * the entries are added directly. - * - * This should be used when changing directory or initiating a new search. - * - * @param {DirectoryContents} newDirContents New DirectoryContents instance to - * replace currentDirContents_. - * @param {function(boolean)} callback Callback with result. True if the scan - * is completed successfully, false if the scan is failed. - * @private - */ -DirectoryModel.prototype.clearAndScan_ = function(newDirContents, callback) { - if (this.currentDirContents_.isScanning()) { - this.currentDirContents_.cancelScan(); - } - this.currentDirContents_ = newDirContents; - this.clearRescanTimeout_(); - - if (this.pendingScan_) { - this.pendingScan_ = false; - } - - if (this.runningScan_) { - if (this.runningScan_.isScanning()) { - this.runningScan_.cancelScan(); - } - this.runningScan_ = null; - } - - const sequence = this.changeDirectorySequence_; - let cancelled = false; - - const onDone = () => { - if (cancelled) { - return; - } - - cr.dispatchSimpleEvent(this, 'scan-completed'); - callback(true); - }; - - /** @param {DOMError} error error. */ - const onFailed = error => { - if (cancelled) { - return; - } - - const event = new Event('scan-failed'); - event.error = error; - this.dispatchEvent(event); - callback(false); - }; - - const onUpdated = () => { - if (cancelled) { - return; - } - - if (this.changeDirectorySequence_ !== sequence) { - cancelled = true; - cr.dispatchSimpleEvent(this, 'scan-cancelled'); - callback(false); - return; - } - - cr.dispatchSimpleEvent(this, 'scan-updated'); - }; - - const onCancelled = () => { - if (cancelled) { - return; - } - - cancelled = true; - cr.dispatchSimpleEvent(this, 'scan-cancelled'); - callback(false); - }; - - // Clear metadata information for the old (no longer visible) items in the - // file list. - const fileList = this.getFileList(); - let removedUrls = []; - for (let i = 0; i < fileList.length; i++) { - removedUrls.push(fileList.item(i).toURL()); - } - this.metadataModel_.notifyEntriesRemoved(removedUrls); - - // Retrieve metadata information for the newly selected directory. - const currentEntry = this.currentDirContents_.getDirectoryEntry(); - if (currentEntry && !util.isFakeEntry(assert(currentEntry))) { - this.metadataModel_.get( - [currentEntry], - constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES); - } - - // Clear the table, and start scanning. - cr.dispatchSimpleEvent(this, 'scan-started'); - fileList.splice(0, fileList.length); - this.scan_( - this.currentDirContents_, false, onDone, onFailed, onUpdated, - onCancelled); -}; - -/** - * Adds/removes/updates items of file list. - * @param {Array<Entry>} changedEntries Entries of updated/added files. - * @param {Array<string>} removedUrls URLs of removed files. - * @private - */ -DirectoryModel.prototype.partialUpdate_ = function( - changedEntries, removedUrls) { - // This update should be included in the current running update. - if (this.pendingScan_) { - return; - } - - if (this.runningScan_) { - // Do update after the current scan is finished. - const previousScan = this.runningScan_; - const onPreviousScanCompleted = () => { - previousScan.removeEventListener( - 'scan-completed', onPreviousScanCompleted); - // Run the update asynchronously. - Promise.resolve().then(() => { - this.partialUpdate_(changedEntries, removedUrls); - }); - }; - previousScan.addEventListener('scan-completed', onPreviousScanCompleted); - return; - } - - const onFinish = () => { - this.runningScan_ = null; - - this.currentDirContents_.removeEventListener('scan-completed', onCompleted); - this.currentDirContents_.removeEventListener('scan-failed', onFailure); - this.currentDirContents_.removeEventListener('scan-cancelled', onCancelled); - }; - - const onCompleted = () => { - onFinish(); - cr.dispatchSimpleEvent(this, 'rescan-completed'); - }; - - const onFailure = () => { - onFinish(); - }; - - const onCancelled = () => { - onFinish(); - }; - - this.runningScan_ = this.currentDirContents_; - this.currentDirContents_.addEventListener('scan-completed', onCompleted); - this.currentDirContents_.addEventListener('scan-failed', onFailure); - this.currentDirContents_.addEventListener('scan-cancelled', onCancelled); - this.currentDirContents_.update(changedEntries, removedUrls); -}; - -/** - * Perform a directory contents scan. Should be called only from rescan() and - * clearAndScan_(). - * - * @param {DirectoryContents} dirContents DirectoryContents instance on which - * the scan will be run. - * @param {boolean} refresh True to refresh metadata, or false to use cached - * one. - * @param {function()} successCallback Callback on success. - * @param {function(DOMError)} failureCallback Callback on failure. - * @param {function()} updatedCallback Callback on update. Only on the last - * update, {@code successCallback} is called instead of this. - * @param {function()} cancelledCallback Callback on cancel. - * @private - */ -DirectoryModel.prototype.scan_ = function( - dirContents, refresh, successCallback, failureCallback, updatedCallback, - cancelledCallback) { - const self = this; - - /** - * Runs pending scan if there is one. - * - * @return {boolean} Did pending scan exist. - */ - const maybeRunPendingRescan = () => { - if (this.pendingRescan_) { - this.rescanSoon(refresh); - this.pendingRescan_ = false; - return true; - } - return false; - }; - - const onFinished = () => { - dirContents.removeEventListener('scan-completed', onSuccess); - dirContents.removeEventListener('scan-updated', updatedCallback); - dirContents.removeEventListener('scan-failed', onFailure); - dirContents.removeEventListener('scan-cancelled', cancelledCallback); - }; - - const onSuccess = () => { - onFinished(); - - // Record metric for Downloads directory. - if (!dirContents.isSearch()) { - const locationInfo = this.volumeManager_.getLocationInfo( - assert(dirContents.getDirectoryEntry())); - const volumeInfo = locationInfo && locationInfo.volumeInfo; - if (volumeInfo && - volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS && - locationInfo.isRootEntry) { - metrics.recordMediumCount( - 'DownloadsCount', dirContents.getFileListLength()); - } - } + this.fileListSelection_ = singleSelection ? + new FileListSingleSelectionModel() : + new FileListSelectionModel(); this.runningScan_ = null; - successCallback(); + this.pendingScan_ = null; + this.pendingRescan_ = null; + this.rescanTime_ = null; this.scanFailures_ = 0; - maybeRunPendingRescan(); - }; + this.changeDirectorySequence_ = 0; - const onFailure = event => { - onFinished(); + /** @private {?function(Event): void} */ + this.onSearchCompleted_ = null; + /** @private {?Function} */ + this.onClearSearch_ = null; - this.runningScan_ = null; - this.scanFailures_++; - failureCallback(event.error); + /** + * @private {boolean} + */ + this.ignoreCurrentDirectoryDeletion_ = false; - if (maybeRunPendingRescan()) { - return; - } + this.directoryChangeQueue_ = new AsyncUtil.Queue(); + this.rescanAggregator_ = + new AsyncUtil.Aggregator(this.rescanSoon.bind(this, true), 500); - // Do not rescan for crostini errors. - if (event.error.name === DirectoryModel.CROSTINI_CONNECT_ERR) { - return; - } + this.fileFilter_ = fileFilter; + this.fileFilter_.addEventListener( + 'changed', this.onFilterChanged_.bind(this)); - if (this.scanFailures_ <= 1) { - this.rescanLater(refresh); - } - }; + this.currentFileListContext_ = + new FileListContext(fileFilter, metadataModel, volumeManager); + this.currentDirContents_ = DirectoryContents.createForDirectory( + this.currentFileListContext_, null); + /** + * Empty file list which is used as a dummy for inactive view of file list. + * @private {!FileListModel} + */ + this.emptyFileList_ = new FileListModel(metadataModel); - const onCancelled = () => { - onFinished(); - cancelledCallback(); - }; + this.metadataModel_ = metadataModel; - this.runningScan_ = dirContents; + this.volumeManager_ = volumeManager; + this.volumeManager_.volumeInfoList.addEventListener( + 'splice', this.onVolumeInfoListUpdated_.bind(this)); - dirContents.addEventListener('scan-completed', onSuccess); - dirContents.addEventListener('scan-updated', updatedCallback); - dirContents.addEventListener('scan-failed', onFailure); - dirContents.addEventListener('scan-cancelled', onCancelled); - dirContents.scan(refresh); -}; + /** + * File watcher. + * @private {!FileWatcher} + * @const + */ + this.fileWatcher_ = new FileWatcher(); + this.fileWatcher_.addEventListener( + 'watcher-directory-changed', + this.onWatcherDirectoryChanged_.bind(this)); + util.addEventListenerToBackgroundComponent( + fileOperationManager, 'entries-changed', + this.onEntriesChanged_.bind(this)); -/** - * @param {DirectoryContents} dirContents DirectoryContents instance. This must - * be a different instance from this.currentDirContents_. - * @private - */ -DirectoryModel.prototype.replaceDirectoryContents_ = function(dirContents) { - console.assert( - this.currentDirContents_ !== dirContents, - 'Give directory contents instance must be different from current one.'); - cr.dispatchSimpleEvent(this, 'begin-update-files'); - this.updateSelectionAndPublishEvent_(this.fileListSelection_, () => { - const selectedEntries = this.getSelectedEntries_(); - const selectedIndices = this.fileListSelection_.selectedIndexes; + /** @private {string} */ + this.lastSearchQuery_ = ''; - // Restore leadIndex in case leadName no longer exists. - const leadIndex = this.fileListSelection_.leadIndex; - const leadEntry = this.getLeadEntry_(); - const isCheckSelectMode = this.fileListSelection_.getCheckSelectMode(); - - const previousDirContents = this.currentDirContents_; - this.currentDirContents_ = dirContents; - this.currentDirContents_.replaceContextFileList(); - - this.setSelectedEntries_(selectedEntries); - this.fileListSelection_.leadIndex = leadIndex; - this.setLeadEntry_(leadEntry); - - // If nothing is selected after update, then select file next to the - // latest selection - let forceChangeEvent = false; - if (this.fileListSelection_.selectedIndexes.length == 0 && - selectedIndices.length != 0) { - const maxIdx = Math.max.apply(null, selectedIndices); - this.selectIndex( - Math.min( - maxIdx - selectedIndices.length + 2, this.getFileList().length) - - 1); - forceChangeEvent = true; - } else if (isCheckSelectMode) { - // Otherwise, ensure check select mode is retained if it was previously - // active. - this.fileListSelection_.setCheckSelectMode(true); - } - return forceChangeEvent; - }); - - cr.dispatchSimpleEvent(this, 'end-update-files'); -}; - -/** - * Callback when an entry is changed. - * @param {EntriesChangedEvent} event Entry change event. - * @private - */ -DirectoryModel.prototype.onEntriesChanged_ = function(event) { - const kind = event.kind; - const entries = event.entries; - // TODO(hidehiko): We should update directory model even the search result - // is shown. - const rootType = this.getCurrentRootType(); - if ((rootType === VolumeManagerCommon.RootType.DRIVE || - rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME || - rootType === VolumeManagerCommon.RootType.DRIVE_RECENT || - rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE) && - this.isSearching()) { - return; + /** @private {FilesAppDirEntry} */ + this.myFilesEntry_ = null; } - switch (kind) { - case util.EntryChangedKind.CREATED: - const parentPromises = []; - for (let i = 0; i < entries.length; i++) { - parentPromises.push(new Promise((resolve, reject) => { - entries[i].getParent(resolve, reject); - })); + /** + * Disposes the directory model by removing file watchers. + */ + dispose() { + this.fileWatcher_.dispose(); + } + + /** + * @return {FileListModel} Files in the current directory. + */ + getFileList() { + return this.currentFileListContext_.fileList; + } + + /** + * @return {!FileListModel} File list which is always empty. + */ + getEmptyFileList() { + return this.emptyFileList_; + } + + /** + * @return {!FileListSelectionModel|!FileListSingleSelectionModel} Selection + * in the fileList. + */ + getFileListSelection() { + return this.fileListSelection_; + } + + /** + * Obtains current volume information. + * @return {VolumeInfo} + */ + getCurrentVolumeInfo() { + const entry = this.getCurrentDirEntry(); + if (!entry) { + return null; + } + return this.volumeManager_.getVolumeInfo(entry); + } + + /** + * @return {?VolumeManagerCommon.RootType} Root type of current root, or null + * if not found. + */ + getCurrentRootType() { + const entry = this.currentDirContents_.getDirectoryEntry(); + if (!entry) { + return null; + } + + const locationInfo = this.volumeManager_.getLocationInfo(entry); + if (!locationInfo) { + return null; + } + + return locationInfo.rootType; + } + + /** + * Metadata property names that are expected to be Prefetched. + * @return {!Array<string>} + */ + getPrefetchPropertyNames() { + return this.currentFileListContext_.prefetchPropertyNames; + } + + /** + * @return {boolean} True if the current directory is read only. If there is + * no entry set, then returns true. + */ + isReadOnly() { + const currentDirEntry = this.getCurrentDirEntry(); + if (currentDirEntry) { + const locationInfo = this.volumeManager_.getLocationInfo(currentDirEntry); + if (locationInfo) { + return locationInfo.isReadOnly; } - Promise.all(parentPromises) - .then(parents => { - const entriesToAdd = []; - for (let i = 0; i < parents.length; i++) { - if (!util.isSameEntry(parents[i], this.getCurrentDirEntry())) { - continue; - } - const index = this.findIndexByEntry_(entries[i]); - if (index >= 0) { - this.getFileList().replaceItem( - this.getFileList().item(index), entries[i]); - } else { - entriesToAdd.push(entries[i]); - } + } + return true; + } + + /** + * @return {boolean} True if the a scan is active. + */ + isScanning() { + return this.currentDirContents_.isScanning(); + } + + /** + * @return {boolean} True if search is in progress. + */ + isSearching() { + return this.currentDirContents_.isSearch(); + } + + /** + * @return {boolean} True if it's on Drive. + */ + isOnDrive() { + return this.isCurrentRootVolumeType_(VolumeManagerCommon.VolumeType.DRIVE); + } + + /** + * @return {boolean} True if it's on MTP volume. + */ + isOnMTP() { + return this.isCurrentRootVolumeType_(VolumeManagerCommon.VolumeType.MTP); + } + + /** + * @param {VolumeManagerCommon.VolumeType} volumeType Volume Type + * @return {boolean} True if current root volume type is equal to specified + * volume type. + * @private + */ + isCurrentRootVolumeType_(volumeType) { + const rootType = this.getCurrentRootType(); + return rootType != null && + rootType != VolumeManagerCommon.RootType.RECENT && + VolumeManagerCommon.getVolumeTypeFromRootType(rootType) === volumeType; + } + + /** + * Updates the selection by using the updateFunc and publish the change event. + * If updateFunc returns true, it force to dispatch the change event even if + * the selection index is not changed. + * + * @param {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} selection + * Selection to be updated. + * @param {function(): boolean} updateFunc Function updating the selection. + * @private + */ + updateSelectionAndPublishEvent_(selection, updateFunc) { + // Begin change. + selection.beginChange(); + + // If dispatchNeeded is true, we should ensure the change event is + // dispatched. + let dispatchNeeded = updateFunc(); + + // Check if the change event is dispatched in the endChange function + // or not. + const eventDispatched = () => { + dispatchNeeded = false; + }; + selection.addEventListener('change', eventDispatched); + selection.endChange(); + selection.removeEventListener('change', eventDispatched); + + // If the change event have been already dispatched, dispatchNeeded is + // false. + if (dispatchNeeded) { + const event = new Event('change'); + // The selection status (selected or not) is not changed because + // this event is caused by the change of selected item. + event.changes = []; + selection.dispatchEvent(event); + } + } + + /** + * Sets to ignore current directory deletion. This method is used to prevent + * going up to the volume root with the deletion of current directory by + * rename operation in directory tree. + * @param {boolean} value True to ignore current directory deletion. + */ + setIgnoringCurrentDirectoryDeletion(value) { + this.ignoreCurrentDirectoryDeletion_ = value; + } + + /** + * Invoked when a change in the directory is detected by the watcher. + * @param {Event} event Event object. + * @private + */ + onWatcherDirectoryChanged_(event) { + const directoryEntry = this.getCurrentDirEntry(); + + if (!this.ignoreCurrentDirectoryDeletion_) { + // If the change is deletion of currentDir, move up to its parent + // directory. + directoryEntry.getDirectory( + directoryEntry.fullPath, {create: false}, () => {}, () => { + const volumeInfo = + this.volumeManager_.getVolumeInfo(assert(directoryEntry)); + if (volumeInfo) { + volumeInfo.resolveDisplayRoot().then(displayRoot => { + this.changeDirectoryEntry(displayRoot); + }); } - this.partialUpdate_(entriesToAdd, []); + }); + } + + if (event.changedFiles) { + const addedOrUpdatedFileUrls = []; + let deletedFileUrls = []; + event.changedFiles.forEach(change => { + if (change.changes.length === 1 && change.changes[0] === 'delete') { + deletedFileUrls.push(change.url); + } else { + addedOrUpdatedFileUrls.push(change.url); + } + }); + + util.URLsToEntries(addedOrUpdatedFileUrls) + .then(result => { + deletedFileUrls = deletedFileUrls.concat(result.failureUrls); + + // Passing the resolved entries and failed URLs as the removed + // files. The URLs are removed files and they chan't be resolved. + this.partialUpdate_(result.entries, deletedFileUrls); }) .catch(error => { - console.error(error.stack || error); + console.error( + 'Error in proceeding the changed event.', error, + 'Fallback to force-refresh'); + this.rescanAggregator_.run(); }); - break; - - case util.EntryChangedKind.DELETED: - // This is the delete event. - this.partialUpdate_([], util.entriesToURLs(entries)); - break; - - default: - console.error('Invalid EntryChangedKind: ' + kind); - break; - } -}; - -/** - * @param {Entry} entry The entry to be searched. - * @return {number} The index in the fileList, or -1 if not found. - * @private - */ -DirectoryModel.prototype.findIndexByEntry_ = function(entry) { - const fileList = this.getFileList(); - for (let i = 0; i < fileList.length; i++) { - if (util.isSameEntry(/** @type {Entry} */ (fileList.item(i)), entry)) { - return i; - } - } - return -1; -}; - -/** - * Called when rename is done successfully. - * Note: conceptually, DirectoryModel should work without this, because entries - * can be renamed by other systems anytime and the Files app should reflect it - * correctly. - * TODO(hidehiko): investigate more background, and remove this if possible. - * - * @param {!Entry} oldEntry The old entry. - * @param {!Entry} newEntry The new entry. - * @param {function()=} opt_callback Called on completion. - */ -DirectoryModel.prototype.onRenameEntry = function( - oldEntry, newEntry, opt_callback) { - this.currentDirContents_.prefetchMetadata([newEntry], true, () => { - // If the current directory is the old entry, then quietly change to the - // new one. - if (util.isSameEntry(oldEntry, this.getCurrentDirEntry())) { - this.changeDirectoryEntry( - /** @type {!DirectoryEntry|!FilesAppDirEntry} */ (newEntry)); - } - - // Replace the old item with the new item. oldEntry instance itself may - // have been removed/replaced from the list during the async process, we - // find an entry which should be replaced by checking toURL(). - const list = this.getFileList(); - let oldEntryExist = false; - let newEntryExist = false; - const oldEntryUrl = oldEntry.toURL(); - const newEntryUrl = newEntry.toURL(); - - for (let i = 0; i < list.length; i++) { - const item = list.item(i); - const url = item.toURL(); - if (url === oldEntryUrl) { - list.replaceItem(item, newEntry); - oldEntryExist = true; - break; - } - - if (url === newEntryUrl) { - newEntryExist = true; - } - } - - // When both old and new entries don't exist, it may be in the middle of - // update process. In DirectoryContent.update deletion is executed at first - // and insertion is executed as a async call. There is a chance that this - // method is called in the middle of update process. - if (!oldEntryExist && !newEntryExist) { - list.push(newEntry); - } - - // Run callback, finally. - if (opt_callback) { - opt_callback(); - } - }); -}; - -/** - * Updates data model and selects new directory. - * @param {!DirectoryEntry} newDirectory Directory entry to be selected. - * @return {Promise} A promise which is resolved when new directory is selected. - * If current directory has changed during the operation, this will be - * rejected. - */ -DirectoryModel.prototype.updateAndSelectNewDirectory = function(newDirectory) { - // Refresh the cache. - this.metadataModel_.notifyEntriesCreated([newDirectory]); - const dirContents = this.currentDirContents_; - - return new Promise((onFulfilled, onRejected) => { - dirContents.prefetchMetadata([newDirectory], false, onFulfilled); - }) - .then((sequence => { - // If current directory has changed during the prefetch, do not - // try to select new directory. - if (sequence !== this.changeDirectorySequence_) { - return Promise.reject(); - } - - // If target directory is already in the list, just select it. - const existing = this.getFileList().slice().filter(e => { - return e.name === newDirectory.name; - }); - if (existing.length) { - this.selectEntry(newDirectory); - } else { - this.fileListSelection_.beginChange(); - this.getFileList().splice(0, 0, newDirectory); - this.selectEntry(newDirectory); - this.fileListSelection_.endChange(); - } - }).bind(null, this.changeDirectorySequence_)); -}; - -/** - * Sets the current MyFilesEntry. - * @param {FilesAppDirEntry} myFilesEntry - */ -DirectoryModel.prototype.setMyFiles = function(myFilesEntry) { - this.myFilesEntry_ = myFilesEntry; -}; - -/** - * Changes the current directory to the directory represented by - * a DirectoryEntry or a fake entry. - * - * Dispatches the 'directory-changed' event when the directory is successfully - * changed. - * - * Note : if this is called from UI, please consider to use DirectoryModel. - * activateDirectoryEntry instead of this, which is higher-level function and - * cares about the selection. - * - * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new - * directory to be opened. - * @param {function()=} opt_callback Executed if the directory loads - * successfully. - */ -DirectoryModel.prototype.changeDirectoryEntry = function( - dirEntry, opt_callback) { - // Increment the sequence value. - this.changeDirectorySequence_++; - this.clearSearch_(); - - // When switching to MyFiles volume, we should use a FilesAppEntry if - // available because it returns UI-only entries too, like Linux files and Play - // files. - const locationInfo = this.volumeManager_.getLocationInfo(dirEntry); - if (util.isMyFilesVolumeEnabled() && locationInfo && this.myFilesEntry_ && - locationInfo.rootType === VolumeManagerCommon.RootType.DOWNLOADS && - locationInfo.isRootEntry) { - dirEntry = this.myFilesEntry_; - } - - // If there is on-going scan, cancel it. - if (this.currentDirContents_.isScanning()) { - this.currentDirContents_.cancelScan(); - } - - this.directoryChangeQueue_.run( - ((sequence, queueTaskCallback) => { - this.fileWatcher_.changeWatchedDirectory(dirEntry).then(() => { - if (this.changeDirectorySequence_ !== sequence) { - queueTaskCallback(); - return; - } - - const newDirectoryContents = this.createDirectoryContents_( - this.currentFileListContext_, dirEntry, ''); - if (!newDirectoryContents) { - queueTaskCallback(); - return; - } - - const previousDirEntry = this.currentDirContents_.getDirectoryEntry(); - this.clearAndScan_(newDirectoryContents, result => { - // Calls the callback of the method when successful. - if (result && opt_callback) { - opt_callback(); - } - - // Notify that the current task of this.directoryChangeQueue_ - // is completed. - setTimeout(queueTaskCallback, 0); - }); - - // For tests that open the dialog to empty directories, everything - // is loaded at this point. - util.testSendMessage('directory-change-complete'); - const previousVolumeInfo = previousDirEntry ? - this.volumeManager_.getVolumeInfo(previousDirEntry) : - null; - // VolumeInfo for dirEntry. - const currentVolumeInfo = this.getCurrentVolumeInfo(); - const event = new Event('directory-changed'); - event.previousDirEntry = previousDirEntry; - event.newDirEntry = dirEntry; - event.volumeChanged = previousVolumeInfo !== currentVolumeInfo; - this.dispatchEvent(event); - }); - }).bind(null, this.changeDirectorySequence_)); -}; - -/** - * Activates the given directory. - * This method: - * - Changes the current directory, if the given directory is not the current - * directory. - * - Clears the selection, if the given directory is the current directory. - * - * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new - * directory to be opened. - * @param {function()=} opt_callback Executed if the directory loads - * successfully. - */ -DirectoryModel.prototype.activateDirectoryEntry = function( - dirEntry, opt_callback) { - const currentDirectoryEntry = this.getCurrentDirEntry(); - if (currentDirectoryEntry && - util.isSameEntry(dirEntry, currentDirectoryEntry)) { - // On activating the current directory, clear the selection on the filelist. - this.clearSelection(); - } else { - // Otherwise, changes the current directory. - this.changeDirectoryEntry(dirEntry, opt_callback); - } -}; - -/** - * Clears the selection in the file list. - */ -DirectoryModel.prototype.clearSelection = function() { - this.setSelectedEntries_([]); -}; - -/** - * Creates an object which could say whether directory has changed while it has - * been active or not. Designed for long operations that should be cancelled - * if the used change current directory. - * @return {Object} Created object. - */ -DirectoryModel.prototype.createDirectoryChangeTracker = function() { - const tracker = { - dm_: this, - active_: false, - hasChanged: false, - - start: function() { - if (!this.active_) { - this.dm_.addEventListener('directory-changed', this.onDirectoryChange_); - this.active_ = true; - this.hasChanged = false; - } - }, - - stop: function() { - if (this.active_) { - this.dm_.removeEventListener( - 'directory-changed', this.onDirectoryChange_); - this.active_ = false; - } - }, - - onDirectoryChange_: function(event) { - tracker.stop(); - tracker.hasChanged = true; - } - }; - return tracker; -}; - -/** - * @param {Entry} entry Entry to be selected. - */ -DirectoryModel.prototype.selectEntry = function(entry) { - const fileList = this.getFileList(); - for (let i = 0; i < fileList.length; i++) { - if (fileList.item(i).toURL() === entry.toURL()) { - this.selectIndex(i); - return; - } - } -}; - -/** - * @param {Array<Entry>} entries Array of entries. - */ -DirectoryModel.prototype.selectEntries = function(entries) { - // URLs are needed here, since we are comparing Entries by URLs. - const urls = util.entriesToURLs(entries); - const fileList = this.getFileList(); - this.fileListSelection_.beginChange(); - this.fileListSelection_.unselectAll(); - for (let i = 0; i < fileList.length; i++) { - if (urls.indexOf(fileList.item(i).toURL()) >= 0) { - this.fileListSelection_.setIndexSelected(i, true); - } - } - this.fileListSelection_.endChange(); -}; - -/** - * @param {number} index Index of file. - */ -DirectoryModel.prototype.selectIndex = function(index) { - // this.focusCurrentList_(); - if (index >= this.getFileList().length) { - return; - } - - // If a list bound with the model it will do scrollIndexIntoView(index). - this.fileListSelection_.selectedIndex = index; -}; - -/** - * Handles update of VolumeInfoList. - * @param {Event} event Event of VolumeInfoList's 'splice'. - * @private - */ -DirectoryModel.prototype.onVolumeInfoListUpdated_ = function(event) { - // Fallback to the default volume's root if the current volume is unmounted. - if (this.hasCurrentDirEntryBeenUnmounted_(event.removed)) { - this.volumeManager_.getDefaultDisplayRoot((displayRoot) => { - if (displayRoot) { - this.changeDirectoryEntry(displayRoot); - } - }); - } - - // If a volume within My files is mounted, rescan the contents. - // TODO(crbug.com/901690): Remove this special case. - if (this.getCurrentRootType() === VolumeManagerCommon.RootType.MY_FILES) { - for (let newVolume of event.added) { - if (newVolume.volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS || - newVolume.volumeType === - VolumeManagerCommon.VolumeType.ANDROID_FILES || - newVolume.volumeType === VolumeManagerCommon.VolumeType.CROSTINI) { - this.rescan(false); - break; - } + } else { + // Invokes force refresh if the detailed information isn't provided. + // This can occur very frequently (e.g. when copying files into Downlaods) + // and rescan is heavy operation, so we keep some interval for each + // rescan. + this.rescanAggregator_.run(); } } - // If the current directory is the Drive placeholder and the real Drive is - // mounted, switch to it. - if (this.getCurrentRootType() === - VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) { - for (let newVolume of event.added) { - if (newVolume.volumeType === VolumeManagerCommon.VolumeType.DRIVE) { - newVolume.resolveDisplayRoot().then((displayRoot) => { + /** + * Invoked when filters are changed. + * @private + */ + onFilterChanged_() { + const currentDirectory = this.getCurrentDirEntry(); + if (currentDirectory && util.isNativeEntry(currentDirectory) && + !this.fileFilter_.filter( + /** @type {!DirectoryEntry} */ (currentDirectory))) { + // If the current directory should be hidden in the new filter setting, + // change the current directory to the current volume's root. + const volumeInfo = this.volumeManager_.getVolumeInfo(currentDirectory); + if (volumeInfo) { + volumeInfo.resolveDisplayRoot().then(displayRoot => { this.changeDirectoryEntry(displayRoot); }); } + } else { + this.rescanSoon(false); } } - // If a new file backed provided volume is mounted, - // then redirect to it in the focused window. - // Note, that this is a temporary solution for https://crbug.com/427776. - // If crostini is mounted, redirect if it is the currently selected dir. - if (event.added.length !== 1) { - return; + + /** + * Returns the filter. + * @return {FileFilter} The file filter. + */ + getFileFilter() { + return this.fileFilter_; } - if ((window.isFocused() && - event.added[0].volumeType === VolumeManagerCommon.VolumeType.PROVIDED && - event.added[0].source === VolumeManagerCommon.Source.FILE) || - (event.added[0].volumeType === VolumeManagerCommon.VolumeType.CROSTINI && - this.getCurrentRootType() === VolumeManagerCommon.RootType.CROSTINI)) { - event.added[0].resolveDisplayRoot().then((displayRoot) => { - // Resolving a display root on FSP volumes is instant, despite the - // asynchronous call. - this.changeDirectoryEntry(event.added[0].displayRoot); + + /** + * @return {DirectoryEntry|FakeEntry|FilesAppDirEntry} Current directory. + */ + getCurrentDirEntry() { + return this.currentDirContents_.getDirectoryEntry(); + } + + /** + * @return {Array<Entry>} Array of selected entries. + * @private + */ + getSelectedEntries_() { + const indexes = this.fileListSelection_.selectedIndexes; + const fileList = this.getFileList(); + if (fileList) { + return indexes.map(i => { + return fileList.item(i); + }); + } + return []; + } + + /** + * @param {Array<Entry>} value List of selected entries. + * @private + */ + setSelectedEntries_(value) { + const indexes = []; + const fileList = this.getFileList(); + const urls = util.entriesToURLs(value); + + for (let i = 0; i < fileList.length; i++) { + if (urls.indexOf(fileList.item(i).toURL()) !== -1) { + indexes.push(i); + } + } + this.fileListSelection_.selectedIndexes = indexes; + } + + /** + * @return {Entry} Lead entry. + * @private + */ + getLeadEntry_() { + const index = this.fileListSelection_.leadIndex; + return index >= 0 ? + /** @type {Entry} */ (this.getFileList().item(index)) : + null; + } + + /** + * @param {Entry} value The new lead entry. + * @private + */ + setLeadEntry_(value) { + const fileList = this.getFileList(); + for (let i = 0; i < fileList.length; i++) { + if (util.isSameEntry(/** @type {Entry} */ (fileList.item(i)), value)) { + this.fileListSelection_.leadIndex = i; + return; + } + } + } + + /** + * Schedule rescan with short delay. + * @param {boolean} refresh True to refresh metadata, or false to use cached + * one. + */ + rescanSoon(refresh) { + this.scheduleRescan(SHORT_RESCAN_INTERVAL, refresh); + } + + /** + * Schedule rescan with delay. Designed to handle directory change + * notification. + * @param {boolean} refresh True to refresh metadata, or false to use cached + * one. + */ + rescanLater(refresh) { + this.scheduleRescan(SIMULTANEOUS_RESCAN_INTERVAL, refresh); + } + + /** + * Schedule rescan with delay. If another rescan has been scheduled does + * nothing. File operation may cause a few notifications what should cause + * a single refresh. + * @param {number} delay Delay in ms after which the rescan will be performed. + * @param {boolean} refresh True to refresh metadata, or false to use cached + * one. + */ + scheduleRescan(delay, refresh) { + if (this.rescanTime_) { + if (this.rescanTime_ <= Date.now() + delay) { + return; + } + clearTimeout(this.rescanTimeoutId_); + } + + const sequence = this.changeDirectorySequence_; + + this.rescanTime_ = Date.now() + delay; + this.rescanTimeoutId_ = setTimeout(() => { + this.rescanTimeoutId_ = null; + if (sequence === this.changeDirectorySequence_) { + this.rescan(refresh); + } + }, delay); + } + + /** + * Cancel a rescan on timeout if it is scheduled. + * @private + */ + clearRescanTimeout_() { + this.rescanTime_ = null; + if (this.rescanTimeoutId_) { + clearTimeout(this.rescanTimeoutId_); + this.rescanTimeoutId_ = null; + } + } + + /** + * Rescan current directory. May be called indirectly through rescanLater or + * directly in order to reflect user action. Will first cache all the + * directory contents in an array, then seamlessly substitute the fileList + * contents, preserving the select element etc. + * + * This should be to scan the contents of current directory (or search). + * + * @param {boolean} refresh True to refresh metadata, or false to use cached + * one. + */ + rescan(refresh) { + this.clearRescanTimeout_(); + if (this.runningScan_) { + this.pendingRescan_ = true; + return; + } + + const dirContents = this.currentDirContents_.clone(); + dirContents.setFileList(new FileListModel(this.metadataModel_)); + dirContents.setMetadataSnapshot( + this.currentDirContents_.createMetadataSnapshot()); + + const sequence = this.changeDirectorySequence_; + + const successCallback = () => { + if (sequence === this.changeDirectorySequence_) { + this.replaceDirectoryContents_(dirContents); + cr.dispatchSimpleEvent(this, 'rescan-completed'); + } + }; + + this.scan_( + dirContents, refresh, successCallback, () => {}, () => {}, () => {}); + } + + /** + * Run scan on the current DirectoryContents. The active fileList is cleared + * and the entries are added directly. + * + * This should be used when changing directory or initiating a new search. + * + * @param {DirectoryContents} newDirContents New DirectoryContents instance to + * replace currentDirContents_. + * @param {function(boolean)} callback Callback with result. True if the scan + * is completed successfully, false if the scan is failed. + * @private + */ + clearAndScan_(newDirContents, callback) { + if (this.currentDirContents_.isScanning()) { + this.currentDirContents_.cancelScan(); + } + this.currentDirContents_ = newDirContents; + this.clearRescanTimeout_(); + + if (this.pendingScan_) { + this.pendingScan_ = false; + } + + if (this.runningScan_) { + if (this.runningScan_.isScanning()) { + this.runningScan_.cancelScan(); + } + this.runningScan_ = null; + } + + const sequence = this.changeDirectorySequence_; + let cancelled = false; + + const onDone = () => { + if (cancelled) { + return; + } + + cr.dispatchSimpleEvent(this, 'scan-completed'); + callback(true); + }; + + /** @param {DOMError} error error. */ + const onFailed = error => { + if (cancelled) { + return; + } + + const event = new Event('scan-failed'); + event.error = error; + this.dispatchEvent(event); + callback(false); + }; + + const onUpdated = () => { + if (cancelled) { + return; + } + + if (this.changeDirectorySequence_ !== sequence) { + cancelled = true; + cr.dispatchSimpleEvent(this, 'scan-cancelled'); + callback(false); + return; + } + + cr.dispatchSimpleEvent(this, 'scan-updated'); + }; + + const onCancelled = () => { + if (cancelled) { + return; + } + + cancelled = true; + cr.dispatchSimpleEvent(this, 'scan-cancelled'); + callback(false); + }; + + // Clear metadata information for the old (no longer visible) items in the + // file list. + const fileList = this.getFileList(); + let removedUrls = []; + for (let i = 0; i < fileList.length; i++) { + removedUrls.push(fileList.item(i).toURL()); + } + this.metadataModel_.notifyEntriesRemoved(removedUrls); + + // Retrieve metadata information for the newly selected directory. + const currentEntry = this.currentDirContents_.getDirectoryEntry(); + if (currentEntry && !util.isFakeEntry(assert(currentEntry))) { + this.metadataModel_.get( + [currentEntry], + constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES); + } + + // Clear the table, and start scanning. + cr.dispatchSimpleEvent(this, 'scan-started'); + fileList.splice(0, fileList.length); + this.scan_( + this.currentDirContents_, false, onDone, onFailed, onUpdated, + onCancelled); + } + + /** + * Adds/removes/updates items of file list. + * @param {Array<Entry>} changedEntries Entries of updated/added files. + * @param {Array<string>} removedUrls URLs of removed files. + * @private + */ + partialUpdate_(changedEntries, removedUrls) { + // This update should be included in the current running update. + if (this.pendingScan_) { + return; + } + + if (this.runningScan_) { + // Do update after the current scan is finished. + const previousScan = this.runningScan_; + const onPreviousScanCompleted = () => { + previousScan.removeEventListener( + 'scan-completed', onPreviousScanCompleted); + // Run the update asynchronously. + Promise.resolve().then(() => { + this.partialUpdate_(changedEntries, removedUrls); + }); + }; + previousScan.addEventListener('scan-completed', onPreviousScanCompleted); + return; + } + + const onFinish = () => { + this.runningScan_ = null; + + this.currentDirContents_.removeEventListener( + 'scan-completed', onCompleted); + this.currentDirContents_.removeEventListener('scan-failed', onFailure); + this.currentDirContents_.removeEventListener( + 'scan-cancelled', onCancelled); + }; + + const onCompleted = () => { + onFinish(); + cr.dispatchSimpleEvent(this, 'rescan-completed'); + }; + + const onFailure = () => { + onFinish(); + }; + + const onCancelled = () => { + onFinish(); + }; + + this.runningScan_ = this.currentDirContents_; + this.currentDirContents_.addEventListener('scan-completed', onCompleted); + this.currentDirContents_.addEventListener('scan-failed', onFailure); + this.currentDirContents_.addEventListener('scan-cancelled', onCancelled); + this.currentDirContents_.update(changedEntries, removedUrls); + } + + /** + * Perform a directory contents scan. Should be called only from rescan() and + * clearAndScan_(). + * + * @param {DirectoryContents} dirContents DirectoryContents instance on which + * the scan will be run. + * @param {boolean} refresh True to refresh metadata, or false to use cached + * one. + * @param {function()} successCallback Callback on success. + * @param {function(DOMError)} failureCallback Callback on failure. + * @param {function()} updatedCallback Callback on update. Only on the last + * update, {@code successCallback} is called instead of this. + * @param {function()} cancelledCallback Callback on cancel. + * @private + */ + scan_( + dirContents, refresh, successCallback, failureCallback, updatedCallback, + cancelledCallback) { + const self = this; + + /** + * Runs pending scan if there is one. + * + * @return {boolean} Did pending scan exist. + */ + const maybeRunPendingRescan = () => { + if (this.pendingRescan_) { + this.rescanSoon(refresh); + this.pendingRescan_ = false; + return true; + } + return false; + }; + + const onFinished = () => { + dirContents.removeEventListener('scan-completed', onSuccess); + dirContents.removeEventListener('scan-updated', updatedCallback); + dirContents.removeEventListener('scan-failed', onFailure); + dirContents.removeEventListener('scan-cancelled', cancelledCallback); + }; + + const onSuccess = () => { + onFinished(); + + // Record metric for Downloads directory. + if (!dirContents.isSearch()) { + const locationInfo = this.volumeManager_.getLocationInfo( + assert(dirContents.getDirectoryEntry())); + const volumeInfo = locationInfo && locationInfo.volumeInfo; + if (volumeInfo && + volumeInfo.volumeType === + VolumeManagerCommon.VolumeType.DOWNLOADS && + locationInfo.isRootEntry) { + metrics.recordMediumCount( + 'DownloadsCount', dirContents.getFileListLength()); + } + } + + this.runningScan_ = null; + successCallback(); + this.scanFailures_ = 0; + maybeRunPendingRescan(); + }; + + const onFailure = event => { + onFinished(); + + this.runningScan_ = null; + this.scanFailures_++; + failureCallback(event.error); + + if (maybeRunPendingRescan()) { + return; + } + + // Do not rescan for crostini errors. + if (event.error.name === DirectoryModel.CROSTINI_CONNECT_ERR) { + return; + } + + if (this.scanFailures_ <= 1) { + this.rescanLater(refresh); + } + }; + + const onCancelled = () => { + onFinished(); + cancelledCallback(); + }; + + this.runningScan_ = dirContents; + + dirContents.addEventListener('scan-completed', onSuccess); + dirContents.addEventListener('scan-updated', updatedCallback); + dirContents.addEventListener('scan-failed', onFailure); + dirContents.addEventListener('scan-cancelled', onCancelled); + dirContents.scan(refresh); + } + + /** + * @param {DirectoryContents} dirContents DirectoryContents instance. This + * must be a different instance from this.currentDirContents_. + * @private + */ + replaceDirectoryContents_(dirContents) { + console.assert( + this.currentDirContents_ !== dirContents, + 'Give directory contents instance must be different from current one.'); + cr.dispatchSimpleEvent(this, 'begin-update-files'); + this.updateSelectionAndPublishEvent_(this.fileListSelection_, () => { + const selectedEntries = this.getSelectedEntries_(); + const selectedIndices = this.fileListSelection_.selectedIndexes; + + // Restore leadIndex in case leadName no longer exists. + const leadIndex = this.fileListSelection_.leadIndex; + const leadEntry = this.getLeadEntry_(); + const isCheckSelectMode = this.fileListSelection_.getCheckSelectMode(); + + const previousDirContents = this.currentDirContents_; + this.currentDirContents_ = dirContents; + this.currentDirContents_.replaceContextFileList(); + + this.setSelectedEntries_(selectedEntries); + this.fileListSelection_.leadIndex = leadIndex; + this.setLeadEntry_(leadEntry); + + // If nothing is selected after update, then select file next to the + // latest selection + let forceChangeEvent = false; + if (this.fileListSelection_.selectedIndexes.length == 0 && + selectedIndices.length != 0) { + const maxIdx = Math.max.apply(null, selectedIndices); + this.selectIndex( + Math.min( + maxIdx - selectedIndices.length + 2, + this.getFileList().length) - + 1); + forceChangeEvent = true; + } else if (isCheckSelectMode) { + // Otherwise, ensure check select mode is retained if it was previously + // active. + this.fileListSelection_.setCheckSelectMode(true); + } + return forceChangeEvent; + }); + + cr.dispatchSimpleEvent(this, 'end-update-files'); + } + + /** + * Callback when an entry is changed. + * @param {EntriesChangedEvent} event Entry change event. + * @private + */ + onEntriesChanged_(event) { + const kind = event.kind; + const entries = event.entries; + // TODO(hidehiko): We should update directory model even the search result + // is shown. + const rootType = this.getCurrentRootType(); + if ((rootType === VolumeManagerCommon.RootType.DRIVE || + rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME || + rootType === VolumeManagerCommon.RootType.DRIVE_RECENT || + rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE) && + this.isSearching()) { + return; + } + + switch (kind) { + case util.EntryChangedKind.CREATED: + const parentPromises = []; + for (let i = 0; i < entries.length; i++) { + parentPromises.push(new Promise((resolve, reject) => { + entries[i].getParent(resolve, reject); + })); + } + Promise.all(parentPromises) + .then(parents => { + const entriesToAdd = []; + for (let i = 0; i < parents.length; i++) { + if (!util.isSameEntry(parents[i], this.getCurrentDirEntry())) { + continue; + } + const index = this.findIndexByEntry_(entries[i]); + if (index >= 0) { + this.getFileList().replaceItem( + this.getFileList().item(index), entries[i]); + } else { + entriesToAdd.push(entries[i]); + } + } + this.partialUpdate_(entriesToAdd, []); + }) + .catch(error => { + console.error(error.stack || error); + }); + break; + + case util.EntryChangedKind.DELETED: + // This is the delete event. + this.partialUpdate_([], util.entriesToURLs(entries)); + break; + + default: + console.error('Invalid EntryChangedKind: ' + kind); + break; + } + } + + /** + * @param {Entry} entry The entry to be searched. + * @return {number} The index in the fileList, or -1 if not found. + * @private + */ + findIndexByEntry_(entry) { + const fileList = this.getFileList(); + for (let i = 0; i < fileList.length; i++) { + if (util.isSameEntry(/** @type {Entry} */ (fileList.item(i)), entry)) { + return i; + } + } + return -1; + } + + /** + * Called when rename is done successfully. + * Note: conceptually, DirectoryModel should work without this, because + * entries can be renamed by other systems anytime and the Files app should + * reflect it correctly. + * TODO(hidehiko): investigate more background, and remove this if possible. + * + * @param {!Entry} oldEntry The old entry. + * @param {!Entry} newEntry The new entry. + * @param {function()=} opt_callback Called on completion. + */ + onRenameEntry(oldEntry, newEntry, opt_callback) { + this.currentDirContents_.prefetchMetadata([newEntry], true, () => { + // If the current directory is the old entry, then quietly change to the + // new one. + if (util.isSameEntry(oldEntry, this.getCurrentDirEntry())) { + this.changeDirectoryEntry( + /** @type {!DirectoryEntry|!FilesAppDirEntry} */ (newEntry)); + } + + // Replace the old item with the new item. oldEntry instance itself may + // have been removed/replaced from the list during the async process, we + // find an entry which should be replaced by checking toURL(). + const list = this.getFileList(); + let oldEntryExist = false; + let newEntryExist = false; + const oldEntryUrl = oldEntry.toURL(); + const newEntryUrl = newEntry.toURL(); + + for (let i = 0; i < list.length; i++) { + const item = list.item(i); + const url = item.toURL(); + if (url === oldEntryUrl) { + list.replaceItem(item, newEntry); + oldEntryExist = true; + break; + } + + if (url === newEntryUrl) { + newEntryExist = true; + } + } + + // When both old and new entries don't exist, it may be in the middle of + // update process. In DirectoryContent.update deletion is executed at + // first and insertion is executed as a async call. There is a chance that + // this method is called in the middle of update process. + if (!oldEntryExist && !newEntryExist) { + list.push(newEntry); + } + + // Run callback, finally. + if (opt_callback) { + opt_callback(); + } }); } -}; -/** - * Returns whether the current directory entry has been unmounted. - * - * @param {!Array<!VolumeInfo>} removedVolumes The removed volumes. - * @private - */ -DirectoryModel.prototype.hasCurrentDirEntryBeenUnmounted_ = function( - removedVolumes) { - const entry = this.getCurrentDirEntry(); - if (!entry) { + /** + * Updates data model and selects new directory. + * @param {!DirectoryEntry} newDirectory Directory entry to be selected. + * @return {Promise} A promise which is resolved when new directory is + * selected. If current directory has changed during the operation, this + * will be rejected. + */ + updateAndSelectNewDirectory(newDirectory) { + // Refresh the cache. + this.metadataModel_.notifyEntriesCreated([newDirectory]); + const dirContents = this.currentDirContents_; + + return new Promise((onFulfilled, onRejected) => { + dirContents.prefetchMetadata([newDirectory], false, onFulfilled); + }) + .then((sequence => { + // If current directory has changed during the prefetch, do not + // try to select new directory. + if (sequence !== this.changeDirectorySequence_) { + return Promise.reject(); + } + + // If target directory is already in the list, just select it. + const existing = this.getFileList().slice().filter(e => { + return e.name === newDirectory.name; + }); + if (existing.length) { + this.selectEntry(newDirectory); + } else { + this.fileListSelection_.beginChange(); + this.getFileList().splice(0, 0, newDirectory); + this.selectEntry(newDirectory); + this.fileListSelection_.endChange(); + } + }).bind(null, this.changeDirectorySequence_)); + } + + /** + * Sets the current MyFilesEntry. + * @param {FilesAppDirEntry} myFilesEntry + */ + setMyFiles(myFilesEntry) { + this.myFilesEntry_ = myFilesEntry; + } + + /** + * Changes the current directory to the directory represented by + * a DirectoryEntry or a fake entry. + * + * Dispatches the 'directory-changed' event when the directory is successfully + * changed. + * + * Note : if this is called from UI, please consider to use DirectoryModel. + * activateDirectoryEntry instead of this, which is higher-level function and + * cares about the selection. + * + * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new + * directory to be opened. + * @param {function()=} opt_callback Executed if the directory loads + * successfully. + */ + changeDirectoryEntry(dirEntry, opt_callback) { + // Increment the sequence value. + this.changeDirectorySequence_++; + this.clearSearch_(); + + // When switching to MyFiles volume, we should use a FilesAppEntry if + // available because it returns UI-only entries too, like Linux files and + // Play files. + const locationInfo = this.volumeManager_.getLocationInfo(dirEntry); + if (util.isMyFilesVolumeEnabled() && locationInfo && this.myFilesEntry_ && + locationInfo.rootType === VolumeManagerCommon.RootType.DOWNLOADS && + locationInfo.isRootEntry) { + dirEntry = this.myFilesEntry_; + } + + // If there is on-going scan, cancel it. + if (this.currentDirContents_.isScanning()) { + this.currentDirContents_.cancelScan(); + } + + this.directoryChangeQueue_.run( + ((sequence, queueTaskCallback) => { + this.fileWatcher_.changeWatchedDirectory(dirEntry).then(() => { + if (this.changeDirectorySequence_ !== sequence) { + queueTaskCallback(); + return; + } + + const newDirectoryContents = this.createDirectoryContents_( + this.currentFileListContext_, dirEntry, ''); + if (!newDirectoryContents) { + queueTaskCallback(); + return; + } + + const previousDirEntry = + this.currentDirContents_.getDirectoryEntry(); + this.clearAndScan_(newDirectoryContents, result => { + // Calls the callback of the method when successful. + if (result && opt_callback) { + opt_callback(); + } + + // Notify that the current task of this.directoryChangeQueue_ + // is completed. + setTimeout(queueTaskCallback, 0); + }); + + // For tests that open the dialog to empty directories, everything + // is loaded at this point. + util.testSendMessage('directory-change-complete'); + const previousVolumeInfo = previousDirEntry ? + this.volumeManager_.getVolumeInfo(previousDirEntry) : + null; + // VolumeInfo for dirEntry. + const currentVolumeInfo = this.getCurrentVolumeInfo(); + const event = new Event('directory-changed'); + event.previousDirEntry = previousDirEntry; + event.newDirEntry = dirEntry; + event.volumeChanged = previousVolumeInfo !== currentVolumeInfo; + this.dispatchEvent(event); + }); + }).bind(null, this.changeDirectorySequence_)); + } + + /** + * Activates the given directory. + * This method: + * - Changes the current directory, if the given directory is not the current + * directory. + * - Clears the selection, if the given directory is the current directory. + * + * @param {!DirectoryEntry|!FilesAppDirEntry} dirEntry The entry of the new + * directory to be opened. + * @param {function()=} opt_callback Executed if the directory loads + * successfully. + */ + activateDirectoryEntry(dirEntry, opt_callback) { + const currentDirectoryEntry = this.getCurrentDirEntry(); + if (currentDirectoryEntry && + util.isSameEntry(dirEntry, currentDirectoryEntry)) { + // On activating the current directory, clear the selection on the + // filelist. + this.clearSelection(); + } else { + // Otherwise, changes the current directory. + this.changeDirectoryEntry(dirEntry, opt_callback); + } + } + + /** + * Clears the selection in the file list. + */ + clearSelection() { + this.setSelectedEntries_([]); + } + + /** + * Creates an object which could say whether directory has changed while it + * has been active or not. Designed for long operations that should be + * cancelled if the used change current directory. + * @return {Object} Created object. + */ + createDirectoryChangeTracker() { + const tracker = { + dm_: this, + active_: false, + hasChanged: false, + + start: function() { + if (!this.active_) { + this.dm_.addEventListener( + 'directory-changed', this.onDirectoryChange_); + this.active_ = true; + this.hasChanged = false; + } + }, + + stop: function() { + if (this.active_) { + this.dm_.removeEventListener( + 'directory-changed', this.onDirectoryChange_); + this.active_ = false; + } + }, + + onDirectoryChange_: function(event) { + tracker.stop(); + tracker.hasChanged = true; + } + }; + return tracker; + } + + /** + * @param {Entry} entry Entry to be selected. + */ + selectEntry(entry) { + const fileList = this.getFileList(); + for (let i = 0; i < fileList.length; i++) { + if (fileList.item(i).toURL() === entry.toURL()) { + this.selectIndex(i); + return; + } + } + } + + /** + * @param {Array<Entry>} entries Array of entries. + */ + selectEntries(entries) { + // URLs are needed here, since we are comparing Entries by URLs. + const urls = util.entriesToURLs(entries); + const fileList = this.getFileList(); + this.fileListSelection_.beginChange(); + this.fileListSelection_.unselectAll(); + for (let i = 0; i < fileList.length; i++) { + if (urls.indexOf(fileList.item(i).toURL()) >= 0) { + this.fileListSelection_.setIndexSelected(i, true); + } + } + this.fileListSelection_.endChange(); + } + + /** + * @param {number} index Index of file. + */ + selectIndex(index) { + // this.focusCurrentList_(); + if (index >= this.getFileList().length) { + return; + } + + // If a list bound with the model it will do scrollIndexIntoView(index). + this.fileListSelection_.selectedIndex = index; + } + + /** + * Handles update of VolumeInfoList. + * @param {Event} event Event of VolumeInfoList's 'splice'. + * @private + */ + onVolumeInfoListUpdated_(event) { + // Fallback to the default volume's root if the current volume is unmounted. + if (this.hasCurrentDirEntryBeenUnmounted_(event.removed)) { + this.volumeManager_.getDefaultDisplayRoot((displayRoot) => { + if (displayRoot) { + this.changeDirectoryEntry(displayRoot); + } + }); + } + + // If a volume within My files is mounted, rescan the contents. + // TODO(crbug.com/901690): Remove this special case. + if (this.getCurrentRootType() === VolumeManagerCommon.RootType.MY_FILES) { + for (let newVolume of event.added) { + if (newVolume.volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS || + newVolume.volumeType === + VolumeManagerCommon.VolumeType.ANDROID_FILES || + newVolume.volumeType === VolumeManagerCommon.VolumeType.CROSTINI) { + this.rescan(false); + break; + } + } + } + + // If the current directory is the Drive placeholder and the real Drive is + // mounted, switch to it. + if (this.getCurrentRootType() === + VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) { + for (let newVolume of event.added) { + if (newVolume.volumeType === VolumeManagerCommon.VolumeType.DRIVE) { + newVolume.resolveDisplayRoot().then((displayRoot) => { + this.changeDirectoryEntry(displayRoot); + }); + } + } + } + // If a new file backed provided volume is mounted, + // then redirect to it in the focused window. + // Note, that this is a temporary solution for https://crbug.com/427776. + // If crostini is mounted, redirect if it is the currently selected dir. + if (event.added.length !== 1) { + return; + } + if ((window.isFocused() && + event.added[0].volumeType === + VolumeManagerCommon.VolumeType.PROVIDED && + event.added[0].source === VolumeManagerCommon.Source.FILE) || + (event.added[0].volumeType === + VolumeManagerCommon.VolumeType.CROSTINI && + this.getCurrentRootType() === VolumeManagerCommon.RootType.CROSTINI)) { + event.added[0].resolveDisplayRoot().then((displayRoot) => { + // Resolving a display root on FSP volumes is instant, despite the + // asynchronous call. + this.changeDirectoryEntry(event.added[0].displayRoot); + }); + } + } + + /** + * Returns whether the current directory entry has been unmounted. + * + * @param {!Array<!VolumeInfo>} removedVolumes The removed volumes. + * @private + */ + hasCurrentDirEntryBeenUnmounted_(removedVolumes) { + const entry = this.getCurrentDirEntry(); + if (!entry) { + return false; + } + + if (!util.isFakeEntry(entry)) { + return !this.volumeManager_.getVolumeInfo(entry); + } + + const rootType = this.getCurrentRootType(); + for (let volume of removedVolumes) { + if (volume.fakeEntries[rootType]) { + return true; + } + // The removable root is selected and one of its child partitions has been + // unmounted. + if (volume.prefixEntry === entry) { + return true; + } + } return false; } - if (!util.isFakeEntry(entry)) { - return !this.volumeManager_.getVolumeInfo(entry); - } + /** + * Creates directory contents for the entry and query. + * + * @param {FileListContext} context File list context. + * @param {!DirectoryEntry|!FilesAppEntry} entry Current directory. + * @param {string=} opt_query Search query string. + * @return {DirectoryContents} Directory contents. + * @private + */ + createDirectoryContents_(context, entry, opt_query) { + const query = (opt_query || '').trimLeft(); + const locationInfo = this.volumeManager_.getLocationInfo(entry); + const canUseDriveSearch = + this.volumeManager_.getDriveConnectionState().type !== + VolumeManagerCommon.DriveConnectionType.OFFLINE && + (locationInfo && locationInfo.isDriveBased); - const rootType = this.getCurrentRootType(); - for (let volume of removedVolumes) { - if (volume.fakeEntries[rootType]) { - return true; + if (entry.rootType == VolumeManagerCommon.RootType.RECENT) { + return DirectoryContents.createForRecent( + context, /** @type {!FakeEntry} */ (entry), query); } - // The removable root is selected and one of its child partitions has been - // unmounted. - if (volume.prefixEntry === entry) { - return true; + if (entry.rootType == VolumeManagerCommon.RootType.CROSTINI) { + return DirectoryContents.createForCrostiniMounter( + context, /** @type {!FakeEntry} */ (entry)); } - } - return false; -}; + if (entry.rootType == VolumeManagerCommon.RootType.MY_FILES) { + return DirectoryContents.createForDirectory( + context, /** @type {!FilesAppDirEntry} */ (entry)); + } + if (entry.rootType == VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) { + return DirectoryContents.createForFakeDrive( + context, /** @type {!FakeEntry} */ (entry)); + } + if (query && canUseDriveSearch) { + // Drive search. + return DirectoryContents.createForDriveSearch( + context, /** @type {!DirectoryEntry} */ (entry), query); + } + if (query) { + // Local search. + return DirectoryContents.createForLocalSearch( + context, /** @type {!DirectoryEntry} */ (entry), query); + } -/** - * Creates directory contents for the entry and query. - * - * @param {FileListContext} context File list context. - * @param {!DirectoryEntry|!FilesAppEntry} entry Current directory. - * @param {string=} opt_query Search query string. - * @return {DirectoryContents} Directory contents. - * @private - */ -DirectoryModel.prototype.createDirectoryContents_ = function( - context, entry, opt_query) { - const query = (opt_query || '').trimLeft(); - const locationInfo = this.volumeManager_.getLocationInfo(entry); - const canUseDriveSearch = - this.volumeManager_.getDriveConnectionState().type !== - VolumeManagerCommon.DriveConnectionType.OFFLINE && - (locationInfo && locationInfo.isDriveBased); + if (!locationInfo) { + return null; + } - if (entry.rootType == VolumeManagerCommon.RootType.RECENT) { - return DirectoryContents.createForRecent( - context, /** @type {!FakeEntry} */ (entry), query); - } - if (entry.rootType == VolumeManagerCommon.RootType.CROSTINI) { - return DirectoryContents.createForCrostiniMounter( - context, /** @type {!FakeEntry} */ (entry)); - } - if (entry.rootType == VolumeManagerCommon.RootType.MY_FILES) { + if (locationInfo.rootType == VolumeManagerCommon.RootType.MEDIA_VIEW) { + return DirectoryContents.createForMediaView( + context, /** @type {!DirectoryEntry} */ (entry)); + } + + if (locationInfo.isSpecialSearchRoot) { + // Drive special search. + let searchType; + switch (locationInfo.rootType) { + case VolumeManagerCommon.RootType.DRIVE_OFFLINE: + searchType = + DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; + break; + case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME: + searchType = DriveMetadataSearchContentScanner.SearchType + .SEARCH_SHARED_WITH_ME; + break; + case VolumeManagerCommon.RootType.DRIVE_RECENT: + searchType = + DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; + break; + default: + // Unknown special search entry. + throw new Error('Unknown special search type.'); + } + return DirectoryContents.createForDriveMetadataSearch( + context, + /** @type {!FakeEntry} */ (entry), searchType); + } + // Local fetch or search. return DirectoryContents.createForDirectory( - context, /** @type {!FilesAppDirEntry} */ (entry)); - } - if (entry.rootType == VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) { - return DirectoryContents.createForFakeDrive( - context, /** @type {!FakeEntry} */ (entry)); - } - if (query && canUseDriveSearch) { - // Drive search. - return DirectoryContents.createForDriveSearch( - context, /** @type {!DirectoryEntry} */ (entry), query); - } - if (query) { - // Local search. - return DirectoryContents.createForLocalSearch( - context, /** @type {!DirectoryEntry} */ (entry), query); - } - - if (!locationInfo) { - return null; - } - - if (locationInfo.rootType == VolumeManagerCommon.RootType.MEDIA_VIEW) { - return DirectoryContents.createForMediaView( context, /** @type {!DirectoryEntry} */ (entry)); } - if (locationInfo.isSpecialSearchRoot) { - // Drive special search. - let searchType; - switch (locationInfo.rootType) { - case VolumeManagerCommon.RootType.DRIVE_OFFLINE: - searchType = - DriveMetadataSearchContentScanner.SearchType.SEARCH_OFFLINE; - break; - case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME: - searchType = - DriveMetadataSearchContentScanner.SearchType.SEARCH_SHARED_WITH_ME; - break; - case VolumeManagerCommon.RootType.DRIVE_RECENT: - searchType = - DriveMetadataSearchContentScanner.SearchType.SEARCH_RECENT_FILES; - break; - default: - // Unknown special search entry. - throw new Error('Unknown special search type.'); + /** + * Gets the last search query. + * @return {string} the last search query. + */ + getLastSearchQuery() { + return this.lastSearchQuery_; + } + + /** + * Clears the last search query with the empty string. + */ + clearLastSearchQuery() { + this.lastSearchQuery_ = ''; + } + + /** + * Performs search and displays results. The search type is dependent on the + * current directory. If we are currently on drive, server side content search + * over drive mount point. If the current directory is not on the drive, file + * name search over current directory will be performed. + * + * @param {string} query Query that will be searched for. + * @param {function(Event)} onSearchRescan Function that will be called when + * the search directory is rescanned (i.e. search results are displayed). + * @param {function()} onClearSearch Function to be called when search state + * gets cleared. + * TODO(olege): Change callbacks to events. + */ + search(query, onSearchRescan, onClearSearch) { + this.lastSearchQuery_ = query; + this.clearSearch_(); + const currentDirEntry = this.getCurrentDirEntry(); + if (!currentDirEntry) { + // Not yet initialized. Do nothing. + return; } - return DirectoryContents.createForDriveMetadataSearch( - context, - /** @type {!FakeEntry} */ (entry), searchType); - } - // Local fetch or search. - return DirectoryContents.createForDirectory( - context, /** @type {!DirectoryEntry} */ (entry)); -}; -/** - * Gets the last search query. - * @return {string} the last search query. - */ -DirectoryModel.prototype.getLastSearchQuery = function() { - return this.lastSearchQuery_; -}; - -/** - * Clears the last search query with the empty string. - */ -DirectoryModel.prototype.clearLastSearchQuery = function() { - this.lastSearchQuery_ = ''; -}; - -/** - * Performs search and displays results. The search type is dependent on the - * current directory. If we are currently on drive, server side content search - * over drive mount point. If the current directory is not on the drive, file - * name search over current directory will be performed. - * - * @param {string} query Query that will be searched for. - * @param {function(Event)} onSearchRescan Function that will be called when the - * search directory is rescanned (i.e. search results are displayed). - * @param {function()} onClearSearch Function to be called when search state - * gets cleared. - * TODO(olege): Change callbacks to events. - */ -DirectoryModel.prototype.search = function( - query, onSearchRescan, onClearSearch) { - this.lastSearchQuery_ = query; - this.clearSearch_(); - const currentDirEntry = this.getCurrentDirEntry(); - if (!currentDirEntry) { - // Not yet initialized. Do nothing. - return; - } - - this.changeDirectorySequence_++; - this.directoryChangeQueue_.run( - ((sequence, callback) => { - if (this.changeDirectorySequence_ !== sequence) { - callback(); - return; - } - - if (!(query || '').trimLeft()) { - if (this.isSearching()) { - const newDirContents = this.createDirectoryContents_( - this.currentFileListContext_, assert(currentDirEntry)); - this.clearAndScan_(newDirContents, callback); - } else { + this.changeDirectorySequence_++; + this.directoryChangeQueue_.run( + ((sequence, callback) => { + if (this.changeDirectorySequence_ !== sequence) { callback(); + return; } - return; - } - const newDirContents = this.createDirectoryContents_( - this.currentFileListContext_, assert(currentDirEntry), query); - if (!newDirContents) { - callback(); - return; - } + if (!(query || '').trimLeft()) { + if (this.isSearching()) { + const newDirContents = this.createDirectoryContents_( + this.currentFileListContext_, assert(currentDirEntry)); + this.clearAndScan_(newDirContents, callback); + } else { + callback(); + } + return; + } - this.onSearchCompleted_ = onSearchRescan; - this.onClearSearch_ = onClearSearch; - this.addEventListener('scan-completed', this.onSearchCompleted_); - this.clearAndScan_(newDirContents, callback); - }).bind(null, this.changeDirectorySequence_)); -}; + const newDirContents = this.createDirectoryContents_( + this.currentFileListContext_, assert(currentDirEntry), query); + if (!newDirContents) { + callback(); + return; + } -/** - * In case the search was active, remove listeners and send notifications on - * its canceling. - * @private - */ -DirectoryModel.prototype.clearSearch_ = function() { - if (!this.isSearching()) { - return; + this.onSearchCompleted_ = onSearchRescan; + this.onClearSearch_ = onClearSearch; + this.addEventListener('scan-completed', this.onSearchCompleted_); + this.clearAndScan_(newDirContents, callback); + }).bind(null, this.changeDirectorySequence_)); } - if (this.onSearchCompleted_) { - this.removeEventListener('scan-completed', this.onSearchCompleted_); - this.onSearchCompleted_ = null; - } + /** + * In case the search was active, remove listeners and send notifications on + * its canceling. + * @private + */ + clearSearch_() { + if (!this.isSearching()) { + return; + } - if (this.onClearSearch_) { - this.onClearSearch_(); - this.onClearSearch_ = null; + if (this.onSearchCompleted_) { + this.removeEventListener('scan-completed', this.onSearchCompleted_); + this.onSearchCompleted_ = null; + } + + if (this.onClearSearch_) { + this.onClearSearch_(); + this.onClearSearch_ = null; + } } -}; +} /** * DOMError type for crostini connection failure.
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 a02d46d..0c59bfb 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
@@ -2115,6 +2115,7 @@ } // DirectoryTree is always expanded. + /** @return {boolean} */ get expanded() { return true; }
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc index 9c3f13f..3477f34c8 100644 --- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc +++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
@@ -10,6 +10,7 @@ #include "base/native_library.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_instance.h" +#include "gpu/vulkan/vulkan_posix_util.h" #include "gpu/vulkan/vulkan_surface.h" #include "ui/gfx/gpu_fence.h" @@ -82,11 +83,11 @@ std::vector<const char*> VulkanImplementationGbm::GetRequiredDeviceExtensions() { - return { - "VK_KHR_external_fence", "VK_KHR_external_fence_fd", - }; + return {VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, + VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME}; } - VkFence VulkanImplementationGbm::CreateVkFenceForGpuFence(VkDevice vk_device) { VkFenceCreateInfo fence_create_info = {}; fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; @@ -128,4 +129,17 @@ return std::make_unique<gfx::GpuFence>(gpu_fence_handle); } +VkSemaphore VulkanImplementationGbm::ImportSemaphoreHandle( + VkDevice vk_device, + gpu::SemaphoreHandle sync_handle) { + return gpu::ImportVkSemaphoreHandlePosix(vk_device, std::move(sync_handle)); +} + +gpu::SemaphoreHandle VulkanImplementationGbm::GetSemaphoreHandle( + VkDevice vk_device, + VkSemaphore vk_semaphore) { + return gpu::GetVkSemaphoreHandlePosix( + vk_device, vk_semaphore, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT); +} + } // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h index 1202d6a..0fe36cb 100644 --- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h +++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.h
@@ -31,6 +31,10 @@ std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( VkDevice vk_device, VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + gpu::SemaphoreHandle handle) override; + gpu::SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) override; private: gpu::VulkanInstance vulkan_instance_;
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc index a75a9ebd..54d52ac 100644 --- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc +++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -15,6 +15,7 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/macros.h" #include "base/native_library.h" +#include "gpu/vulkan/fuchsia/vulkan_fuchsia_ext.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_instance.h" #include "gpu/vulkan/vulkan_surface.h" @@ -108,7 +109,8 @@ std::vector<const char*> VulkanImplementationScenic::GetRequiredDeviceExtensions() { - return {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + return {VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME}; } VkFence VulkanImplementationScenic::CreateVkFenceForGpuFence( @@ -124,4 +126,64 @@ return nullptr; } +VkSemaphore VulkanImplementationScenic::ImportSemaphoreHandle( + VkDevice vk_device, + gpu::SemaphoreHandle handle) { + if (!handle.is_valid()) + return VK_NULL_HANDLE; + + if (handle.vk_handle_type() != + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA) { + return VK_NULL_HANDLE; + } + + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + VkResult result = vkCreateSemaphore(vk_device, &info, nullptr, &semaphore); + if (result != VK_SUCCESS) + return VK_NULL_HANDLE; + + zx::event event = handle.TakeHandle(); + VkImportSemaphoreZirconHandleInfoFUCHSIA import = { + VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA}; + import.semaphore = semaphore; + import.handleType = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + import.handle = event.get(); + + result = vkImportSemaphoreZirconHandleFUCHSIA(vk_device, &import); + if (result != VK_SUCCESS) { + vkDestroySemaphore(vk_device, semaphore, nullptr); + return VK_NULL_HANDLE; + } + + // Vulkan took ownership of the handle. + ignore_result(event.release()); + + return semaphore; +} + +gpu::SemaphoreHandle VulkanImplementationScenic::GetSemaphoreHandle( + VkDevice vk_device, + VkSemaphore vk_semaphore) { + // Create VkSemaphoreGetFdInfoKHR structure. + VkSemaphoreGetZirconHandleInfoFUCHSIA info = { + VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA}; + info.semaphore = vk_semaphore; + info.handleType = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + + zx_handle_t handle; + VkResult result = + vkGetSemaphoreZirconHandleFUCHSIA(vk_device, &info, &handle); + if (result != VK_SUCCESS) { + LOG(ERROR) << "vkGetSemaphoreFuchsiaHandleKHR failed : " << result; + return gpu::SemaphoreHandle(); + } + + return gpu::SemaphoreHandle( + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA, + zx::event(handle)); +} + } // namespace ui
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h index 8aba7af8..3e245998 100644 --- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h +++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
@@ -35,6 +35,10 @@ std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( VkDevice vk_device, VkFence vk_fence) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, + gpu::SemaphoreHandle handle) override; + gpu::SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) override; private: ScenicSurfaceFactory* const scenic_surface_factory_;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 4751133..d5ab87b 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -891,6 +891,7 @@ "test/widget_test_mac.mm", "test/x11_property_change_waiter.cc", "test/x11_property_change_waiter.h", + "view_test_api.h", "views_test_suite.cc", "views_test_suite.h", ]
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc index 57f4b69..3828c024 100644 --- a/ui/views/bubble/bubble_frame_view.cc +++ b/ui/views/bubble/bubble_frame_view.cc
@@ -262,7 +262,7 @@ !delegate->GetWindowTitle().empty()); default_title_->SetText(delegate->GetWindowTitle()); } // custom_title_'s updates are handled by its creator. - Layout(); + InvalidateLayout(); } void BubbleFrameView::SizeConstraintsChanged() {} @@ -533,8 +533,7 @@ GetOffScreenLength(available_bounds, window_bounds, vertical)) { bubble_border_->set_arrow(arrow); } else { - if (parent()) - parent()->Layout(); + InvalidateLayout(); SchedulePaint(); } }
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h index 9403ce40..69f1c98 100644 --- a/ui/views/cocoa/bridged_native_widget_host_impl.h +++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -362,6 +362,7 @@ void OnPaintLayer(const ui::PaintContext& context) override; void OnDeviceScaleFactorChanged(float old_device_scale_factor, float new_device_scale_factor) override; + void UpdateVisualState() override; // ui::AcceleratedWidgetMacNSView: void AcceleratedWidgetCALayerParamsUpdated() override;
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm index 4d7d94a..db8c010 100644 --- a/ui/views/cocoa/bridged_native_widget_host_impl.mm +++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -1407,6 +1407,10 @@ old_device_scale_factor, new_device_scale_factor); } +void BridgedNativeWidgetHostImpl::UpdateVisualState() { + native_widget_mac_->GetWidget()->LayoutRootViewIfNecessary(); +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidgetHostImpl, AcceleratedWidgetMac:
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc index 967342b..da6d3116c 100644 --- a/ui/views/controls/button/label_button.cc +++ b/ui/views/controls/button/label_button.cc
@@ -385,7 +385,7 @@ ResetLabelEnabledColor(); label_->SetEnabled(state() != STATE_DISABLED); if (image_->GetPreferredSize() != previous_image_size) - Layout(); + InvalidateLayout(); Button::StateChanged(old_state); } @@ -480,7 +480,6 @@ void LabelButton::ChildPreferredSizeChanged(View* child) { ResetCachedPreferredSize(); PreferredSizeChanged(); - Layout(); } ui::NativeTheme::Part LabelButton::GetThemePart() const {
diff --git a/ui/views/controls/button/label_button_unittest.cc b/ui/views/controls/button/label_button_unittest.cc index 375c6a5..be3f7fe9 100644 --- a/ui/views/controls/button/label_button_unittest.cc +++ b/ui/views/controls/button/label_button_unittest.cc
@@ -29,6 +29,7 @@ #include "ui/views/style/platform_style.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" +#include "ui/views/view_test_api.h" #include "ui/views/widget/widget_utils.h" using base::ASCIIToUTF16; @@ -459,12 +460,16 @@ // The button preferred size and the label size increase when the text size // is increased. button_->SetText(longer_text); + EXPECT_TRUE(ViewTestApi(button_).needs_layout()); + button_->Layout(); EXPECT_GT(button_->label()->bounds().width(), original_label_width * 2); EXPECT_GT(button_->GetPreferredSize().width(), original_width * 2); // The button and the label view return to its original size when the original // text is restored. button_->SetText(text); + EXPECT_TRUE(ViewTestApi(button_).needs_layout()); + button_->Layout(); EXPECT_EQ(original_label_width, button_->label()->bounds().width()); EXPECT_EQ(original_width, button_->GetPreferredSize().width()); }
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc index b1bd6f1c..2346789 100644 --- a/ui/views/controls/focus_ring.cc +++ b/ui/views/controls/focus_ring.cc
@@ -32,7 +32,7 @@ auto ring = base::WrapUnique<FocusRing>(new FocusRing()); ring->set_owned_by_client(); parent->AddChildView(ring.get()); - ring->Layout(); + ring->InvalidateLayout(); ring->SchedulePaint(); return ring; }
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc index 09c24be..bba4490 100644 --- a/ui/views/controls/label.cc +++ b/ui/views/controls/label.cc
@@ -831,7 +831,6 @@ } void Label::ResetLayout() { - InvalidateLayout(); PreferredSizeChanged(); SchedulePaint(); ClearDisplayText();
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc index 95049b9..5ca281f 100644 --- a/ui/views/controls/menu/menu_item_view.cc +++ b/ui/views/controls/menu/menu_item_view.cc
@@ -486,7 +486,7 @@ AddChildView(icon_view); icon_view_ = icon_view; } - Layout(); + InvalidateLayout(); SchedulePaint(); } @@ -607,9 +607,10 @@ controller->MenuChildrenChanged(this); if (submenu_) { - // Force a paint and layout. This handles the case of the top - // level window's size remaining the same, resulting in no - // change to the submenu's size and no layout. + // Force a paint and a synchronous layout. This needs a synchronous layout + // as UpdateSubmenuSelection() looks at bounds. This handles the case of + // the top level window's size remaining the same, resulting in no change + // to the submenu's size and no layout. submenu_->Layout(); submenu_->SchedulePaint(); // Update the menu selection after layout. @@ -1211,7 +1212,7 @@ dimensions.standard_width = menu_config.touchable_menu_width; if (icon_view_) { - dimensions.height = icon_view_->height() + + dimensions.height = icon_view_->GetPreferredSize().height() + 2 * menu_config.vertical_touchable_menu_item_padding; } return dimensions;
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h index db677e0ce..d182c277 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h
@@ -381,8 +381,8 @@ // MenuRunner owns MenuItemView and should be the only one deleting it. ~MenuItemView() override; + // View: void ChildPreferredSizeChanged(View* child) override; - const char* GetClassName() const override; // Returns the preferred size (and padding) of any children.
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index 85ae4ec9..f3ff29e3 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -282,7 +282,7 @@ gfx::Size content_pref = scroll_view_->GetContents()->GetPreferredSize(); scroll_up_button_->SetVisible(content_pref.height() > height()); scroll_down_button_->SetVisible(content_pref.height() > height()); - Layout(); + InvalidateLayout(); } void MenuScrollViewContainer::CreateBorder() {
diff --git a/ui/views/controls/native/native_view_host.cc b/ui/views/controls/native/native_view_host.cc index e64c679..3ec401e 100644 --- a/ui/views/controls/native/native_view_host.cc +++ b/ui/views/controls/native/native_view_host.cc
@@ -33,6 +33,12 @@ DCHECK(!native_view_); native_view_ = native_view; native_wrapper_->AttachNativeView(); + // This does not use InvalidateLayout() to ensure the visibility state of + // the NativeView is correctly set (if this View isn't visible, Layout() + // won't, be called, resulting in the NativeView potentially having the wrong + // visibility state). + // TODO(https://crbug.com/947051): inestigate removing updating visibility + // immediately and calling InvalidateLayout() to update bounds. Layout(); Widget* widget = Widget::GetWidgetForNativeView(native_view); @@ -150,6 +156,8 @@ } void NativeViewHost::VisibilityChanged(View* starting_from, bool is_visible) { + // This does not use InvalidateLayout() to ensure the visibility state is + // correctly set (if this View isn't visible, Layout() won't be called). Layout(); } @@ -161,7 +169,7 @@ } void NativeViewHost::OnVisibleBoundsChanged() { - Layout(); + InvalidateLayout(); } void NativeViewHost::ViewHierarchyChanged(
diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc index f11658d..c15db47 100644 --- a/ui/views/controls/native/native_view_host_aura.cc +++ b/ui/views/controls/native/native_view_host_aura.cc
@@ -144,7 +144,7 @@ host_->native_view()->Show(); else host_->native_view()->Hide(); - host_->Layout(); + host_->InvalidateLayout(); } void NativeViewHostAura::RemovedFromWidget() {
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc index 5518ed93..0cf1032 100644 --- a/ui/views/controls/scroll_view.cc +++ b/ui/views/controls/scroll_view.cc
@@ -135,6 +135,8 @@ scroll_view_->ScrollContentsRegionToBeVisible(scroll_rect); } + // TODO(https://crbug.com/947053): this override should not be necessary, but + // there are some assumptions that this calls Layout(). void ChildPreferredSizeChanged(View* child) override { if (parent()) parent()->Layout(); @@ -701,6 +703,9 @@ *member = parent->AddChildView(std::move(new_view)); else *member = nullptr; + // TODO(https://crbug.com/947053): this should call InvalidateLayout(), but + // there are some assumptions that it call Layout(). These assumptions should + // be updated. Layout(); }
diff --git a/ui/views/controls/scroll_view_unittest.cc b/ui/views/controls/scroll_view_unittest.cc index 6ca5557..a40ef51a 100644 --- a/ui/views/controls/scroll_view_unittest.cc +++ b/ui/views/controls/scroll_view_unittest.cc
@@ -27,6 +27,7 @@ #include "ui/views/test/test_views.h" #include "ui/views/test/views_test_base.h" #include "ui/views/test/widget_test.h" +#include "ui/views/view_test_api.h" #if defined(OS_MACOSX) #include "ui/base/test/scoped_preferred_scroller_style_mac.h" @@ -567,6 +568,8 @@ // Get the header a height of 20. header->SetPreferredSize(gfx::Size(10, 20)); + EXPECT_TRUE(ViewTestApi(scroll_view_.get()).needs_layout()); + scroll_view_->Layout(); EXPECT_EQ("0,0 100x20", header->parent()->bounds().ToString()); EXPECT_EQ("0,20 100x80", contents->parent()->bounds().ToString()); if (contents->layer()) { @@ -989,6 +992,8 @@ // Switch to the non-overlay style and check that the ViewPort is now sized // to be smaller, and ScrollbarWidth and ScrollbarHeight are non-zero. SetOverlayScrollersEnabled(false); + EXPECT_TRUE(ViewTestApi(scroll_view_.get()).needs_layout()); + scroll_view_->Layout(); EXPECT_EQ(100 - VerticalScrollBarWidth(), contents->parent()->width()); EXPECT_EQ(100 - HorizontalScrollBarHeight(), contents->parent()->height()); EXPECT_NE(0, VerticalScrollBarWidth());
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm index 99a0392..a942b40 100644 --- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm +++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -413,7 +413,7 @@ // Ensure that the ScrollView updates the scrollbar's layout. if (parent()) - parent()->Layout(); + parent()->InvalidateLayout(); if (scroller_style_ == NSScrollerStyleOverlay) { // Hide the scrollbar, but don't fade out.
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc index 4a83681..9b6f2567 100644 --- a/ui/views/controls/table/table_view.cc +++ b/ui/views/controls/table/table_view.cc
@@ -193,7 +193,7 @@ void TableView::SetGrouper(TableGrouper* grouper) { grouper_ = grouper; - SortItemsAndUpdateMapping(); + SortItemsAndUpdateMapping(/*schedule_paint=*/true); } int TableView::RowCount() const { @@ -267,7 +267,7 @@ void TableView::SetSortDescriptors(const SortDescriptors& sort_descriptors) { sort_descriptors_ = sort_descriptors; - SortItemsAndUpdateMapping(); + SortItemsAndUpdateMapping(/*schedule_paint=*/true); if (header_) header_->SchedulePaint(); } @@ -632,7 +632,7 @@ } void TableView::OnItemsChanged(int start, int length) { - SortItemsAndUpdateMapping(); + SortItemsAndUpdateMapping(/*schedule_paint=*/true); } void TableView::OnItemsAdded(int start, int length) { @@ -643,7 +643,7 @@ void TableView::OnItemsMoved(int old_start, int length, int new_start) { selection_model_.Move(old_start, new_start, length); - SortItemsAndUpdateMapping(); + SortItemsAndUpdateMapping(/*schedule_paint=*/true); } void TableView::OnItemsRemoved(int start, int length) { @@ -696,6 +696,9 @@ void TableView::OnPaint(gfx::Canvas* canvas) { // Don't invoke View::OnPaint so that we can render our own focus border. + if (sort_on_paint_) + SortItemsAndUpdateMapping(/*schedule_paint=*/false); + canvas->DrawColor(GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_TableBackground)); @@ -825,13 +828,11 @@ } void TableView::NumRowsChanged() { - SortItemsAndUpdateMapping(); + SortItemsAndUpdateMapping(/*schedule_paint=*/true); PreferredSizeChanged(); - SchedulePaint(); - UpdateVirtualAccessibilityChildren(); } -void TableView::SortItemsAndUpdateMapping() { +void TableView::SortItemsAndUpdateMapping(bool schedule_paint) { if (!is_sorted()) { view_to_model_.clear(); model_to_view_.clear(); @@ -855,8 +856,9 @@ model_to_view_[view_to_model_[i]] = i; model_->ClearCollator(); } - SchedulePaint(); UpdateVirtualAccessibilityChildren(); + if (schedule_paint) + SchedulePaint(); } int TableView::CompareRows(int model_row1, int model_row2) {
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h index 80bd11ec..eeee3de 100644 --- a/ui/views/controls/table/table_view.h +++ b/ui/views/controls/table/table_view.h
@@ -201,6 +201,15 @@ select_on_remove_ = select_on_remove; } + // WARNING: this function forces a sort on every paint, and is therefore + // expensive! It assumes you are calling SchedulePaint() at intervals for + // the whole table. If your model is properly notifying the table, this is + // not needed. This is only used in th extremely rare case, where between the + // time the SchedulePaint() is called and the paint is processed, the + // underlying data may change. Also, this only works if the number of rows + // remains the same. + void set_sort_on_paint(bool sort_on_paint) { sort_on_paint_ = sort_on_paint; } + // View overrides: void Layout() override; const char* GetClassName() const override; @@ -257,8 +266,10 @@ void NumRowsChanged(); // Does the actual sort and updates the mappings (|view_to_model_| and - // |model_to_view_|) appropriately. - void SortItemsAndUpdateMapping(); + // |model_to_view_|) appropriately. If |schedule_paint| is true, + // schedules a paint. This should be true, unless called from + // OnPaint. + void SortItemsAndUpdateMapping(bool schedule_paint); // Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating // whether the row2 comes before row1, row2 is the same as row1 or row1 comes @@ -376,6 +387,8 @@ bool select_on_remove_ = true; TableViewObserver* observer_ = nullptr; + // If |sort_on_paint_| is true, table will sort before painting. + bool sort_on_paint_ = false; // The selection, in terms of the model. ui::ListSelectionModel selection_model_;
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc index 206f8bc..e50e979 100644 --- a/ui/views/controls/textfield/textfield_model.cc +++ b/ui/views/controls/textfield/textfield_model.cc
@@ -63,7 +63,7 @@ } // Commits the edit and marks as un-mergeable. - void Commit() { merge_type_ = DO_NOT_MERGE; } + void Commit() { merge_type_ = MergeType::kDoNotMerge; } private: friend class InsertEdit; @@ -95,10 +95,10 @@ Type type() const { return type_; } // Can this edit be merged? - bool mergeable() const { return merge_type_ == MERGEABLE; } + bool mergeable() const { return merge_type_ == MergeType::kMergeable; } // Should this edit be forcibly merged with the previous edit? - bool force_merge() const { return merge_type_ == FORCE_MERGE; } + bool force_merge() const { return merge_type_ == MergeType::kForceMerge; } // Returns the end index of the |old_text_|. size_t old_text_end() const { return old_text_start_ + old_text_.length(); } @@ -123,7 +123,7 @@ new_text_ = edit->new_text_; new_text_start_ = edit->new_text_start_; - merge_type_ = DO_NOT_MERGE; + merge_type_ = MergeType::kDoNotMerge; } Type type_; @@ -152,7 +152,7 @@ public: InsertEdit(bool mergeable, const base::string16& new_text, size_t at) : Edit(INSERT_EDIT, - mergeable ? MERGEABLE : DO_NOT_MERGE, + mergeable ? MergeType::kMergeable : MergeType::kDoNotMerge, base::string16(), at, gfx::Range(at, at), @@ -215,7 +215,7 @@ bool backward, gfx::Range old_selection) : Edit(DELETE_EDIT, - mergeable ? MERGEABLE : DO_NOT_MERGE, + mergeable ? MergeType::kMergeable : MergeType::kDoNotMerge, text, text_start, old_selection, @@ -303,10 +303,6 @@ using internal::DeleteEdit; using internal::InsertEdit; using internal::ReplaceEdit; -using internal::MergeType; -using internal::DO_NOT_MERGE; -using internal::FORCE_MERGE; -using internal::MERGEABLE; ///////////////////////////////////////////////////////////////// // TextfieldModel: public @@ -324,6 +320,7 @@ } bool TextfieldModel::SetText(const base::string16& new_text) { + using MergeType = internal::MergeType; bool changed = false; if (HasCompositionText()) { ConfirmCompositionText(); @@ -336,9 +333,9 @@ size_t new_cursor = new_text.length(); // If there is a composition text, don't merge with previous edit. // Otherwise, force merge the edits. - ExecuteAndRecordReplace(changed ? DO_NOT_MERGE : FORCE_MERGE, - gfx::Range(0, text().length()), new_cursor, - new_text, 0U); + ExecuteAndRecordReplace( + changed ? MergeType::kDoNotMerge : MergeType::kForceMerge, + gfx::Range(0, text().length()), new_cursor, new_text, 0U); render_text_->SetCursorPosition(new_cursor); } ClearSelection(); @@ -622,9 +619,10 @@ void TextfieldModel::DeleteSelectionAndInsertTextAt( const base::string16& new_text, size_t position) { + using MergeType = internal::MergeType; if (HasCompositionText()) CancelCompositionText(); - ExecuteAndRecordReplace(DO_NOT_MERGE, render_text_->selection(), + ExecuteAndRecordReplace(MergeType::kDoNotMerge, render_text_->selection(), position + new_text.length(), new_text, position); } @@ -723,12 +721,13 @@ void TextfieldModel::InsertTextInternal(const base::string16& new_text, bool mergeable) { + using MergeType = internal::MergeType; if (HasCompositionText()) { CancelCompositionText(); ExecuteAndRecordInsert(new_text, mergeable); } else if (HasSelection()) { - ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, - new_text); + ExecuteAndRecordReplaceSelection( + mergeable ? MergeType::kMergeable : MergeType::kDoNotMerge, new_text); } else { ExecuteAndRecordInsert(new_text, mergeable); } @@ -778,7 +777,7 @@ } void TextfieldModel::ExecuteAndRecordReplaceSelection( - MergeType merge_type, + internal::MergeType merge_type, const base::string16& new_text) { size_t new_text_start = render_text_->selection().GetMin(); size_t new_cursor_pos = new_text_start + new_text.length(); @@ -786,7 +785,7 @@ new_text, new_text_start); } -void TextfieldModel::ExecuteAndRecordReplace(MergeType merge_type, +void TextfieldModel::ExecuteAndRecordReplace(internal::MergeType merge_type, gfx::Range replacement_range, size_t new_cursor_pos, const base::string16& new_text,
diff --git a/ui/views/controls/textfield/textfield_model.h b/ui/views/controls/textfield/textfield_model.h index 771d7a9..19d434d5 100644 --- a/ui/views/controls/textfield/textfield_model.h +++ b/ui/views/controls/textfield/textfield_model.h
@@ -26,13 +26,13 @@ class Edit; // The types of merge behavior implemented by Edit operations. -enum MergeType { +enum class MergeType { // The edit should not usually be merged with next edit. - DO_NOT_MERGE, + kDoNotMerge, // The edit should be merged with next edit when possible. - MERGEABLE, - // The edit should be merged with the prior edit, even if marked DO_NOT_MERGE. - FORCE_MERGE, + kMergeable, + // The edit should be merged with the prior edit, even if marked kDoNotMerge. + kForceMerge, }; } // namespace internal
diff --git a/ui/views/examples/animated_image_view_example.cc b/ui/views/examples/animated_image_view_example.cc index 8e9ac20..09bf0eea 100644 --- a/ui/views/examples/animated_image_view_example.cc +++ b/ui/views/examples/animated_image_view_example.cc
@@ -113,7 +113,7 @@ animated_image_view_->SetImageSize(gfx::Size(size_, size_)); else animated_image_view_->ResetImageSize(); - Layout(); + InvalidateLayout(); } AnimatedImageView* animated_image_view_;
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc index 53cfe77..e70e1f4c 100644 --- a/ui/views/examples/examples_window.cc +++ b/ui/views/examples/examples_window.cc
@@ -208,7 +208,7 @@ combobox_model_->GetItemViewAt(combobox->selected_index())); example_shown_->RequestFocus(); SetStatus(std::string()); - Layout(); + InvalidateLayout(); } static ExamplesWindowContents* instance_;
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc index ba39175..0f80eb2 100644 --- a/ui/views/examples/label_example.cc +++ b/ui/views/examples/label_example.cc
@@ -133,7 +133,7 @@ } else if (button == selectable_) { custom_label_->SetSelectable(selectable_->checked()); } - custom_label_->parent()->parent()->Layout(); + custom_label_->parent()->parent()->InvalidateLayout(); custom_label_->SchedulePaint(); } @@ -150,7 +150,7 @@ void LabelExample::ContentsChanged(Textfield* sender, const base::string16& new_contents) { custom_label_->SetText(new_contents); - custom_label_->parent()->parent()->Layout(); + custom_label_->parent()->parent()->InvalidateLayout(); } void LabelExample::AddCustomLabel(View* container) {
diff --git a/ui/views/examples/layout_example_base.cc b/ui/views/examples/layout_example_base.cc index 67d85d0..df727979 100644 --- a/ui/views/examples/layout_example_base.cc +++ b/ui/views/examples/layout_example_base.cc
@@ -310,7 +310,7 @@ void LayoutExampleBase::RefreshLayoutPanel(bool update_layout) { if (update_layout) UpdateLayoutManager(); - layout_panel_->Layout(); + layout_panel_->InvalidateLayout(); layout_panel_->SchedulePaint(); }
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc index 3517a36..bbff7cd 100644 --- a/ui/views/examples/multiline_example.cc +++ b/ui/views/examples/multiline_example.cc
@@ -180,7 +180,7 @@ render_text_view_->SetText(new_contents); if (label_checkbox_->checked()) label_->SetText(new_contents); - container()->Layout(); + container()->InvalidateLayout(); container()->SchedulePaint(); } @@ -191,7 +191,7 @@ } else if (sender == elision_checkbox_) { render_text_view_->SetMaxLines(elision_checkbox_->checked() ? 3 : 0); } - container()->Layout(); + container()->InvalidateLayout(); container()->SchedulePaint(); }
diff --git a/ui/views/examples/scroll_view_example.cc b/ui/views/examples/scroll_view_example.cc index 59a3662..f254ca6 100644 --- a/ui/views/examples/scroll_view_example.cc +++ b/ui/views/examples/scroll_view_example.cc
@@ -126,7 +126,7 @@ scroll_view_->contents()->ScrollRectToVisible( gfx::Rect(20, 500, 1000, 500)); } - scroll_view_->Layout(); + scroll_view_->InvalidateLayout(); } } // namespace examples
diff --git a/ui/views/examples/vector_example.cc b/ui/views/examples/vector_example.cc index e05e8f6..56b77985 100644 --- a/ui/views/examples/vector_example.cc +++ b/ui/views/examples/vector_example.cc
@@ -124,7 +124,7 @@ image_view_->SetImage( gfx::CreateVectorIconFromSource(contents_, size_, color_)); } - Layout(); + InvalidateLayout(); } // 36dp is one of the natural sizes for MD icons, and corresponds roughly to a
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc index fa32589..bfbeadb 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -134,10 +134,8 @@ void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) override { - if (key == aura::client::kTopViewInset) { + if (key == aura::client::kTopViewInset) InvalidateLayout(); - widget_->GetRootView()->Layout(); - } } aura::Window* window() const { @@ -968,7 +966,7 @@ NonClientView* non_client_view = native_widget_delegate_->AsWidget()->non_client_view(); if (non_client_view) { - non_client_view->Layout(); + non_client_view->InvalidateLayout(); non_client_view->SchedulePaint(); }
diff --git a/ui/views/test/views_test_base.cc b/ui/views/test/views_test_base.cc index d24485b..464992a 100644 --- a/ui/views/test/views_test_base.cc +++ b/ui/views/test/views_test_base.cc
@@ -246,6 +246,11 @@ #endif } +void ViewsTestBaseWithNativeWidgetType::SetUp() { + set_native_widget_type(GetParam()); + ViewsTestBase::SetUp(); +} + void ViewsTestWithDesktopNativeWidget::SetUp() { set_native_widget_type(NativeWidgetType::kDesktop); ViewsTestBase::SetUp();
diff --git a/ui/views/test/views_test_base.h b/ui/views/test/views_test_base.h index 6a169ce..e2e3d8e 100644 --- a/ui/views/test/views_test_base.h +++ b/ui/views/test/views_test_base.h
@@ -140,6 +140,20 @@ DISALLOW_COPY_AND_ASSIGN(ViewsTestBase); }; +class ViewsTestBaseWithNativeWidgetType + : public ViewsTestBase, + public testing::WithParamInterface<ViewsTestBase::NativeWidgetType> { + public: + ViewsTestBaseWithNativeWidgetType() = default; + ~ViewsTestBaseWithNativeWidgetType() override = default; + + // ViewsTestBase: + void SetUp() override; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewsTestBaseWithNativeWidgetType); +}; + // A helper that makes it easier to declare basic views tests that want to test // desktop native widgets. See |ViewsTestBase::native_wiget_type_| and // |ViewsTestBase::CreateNativeWidgetForTest|. In short, for Aura, this will
diff --git a/ui/views/touchui/touch_selection_menu_views.cc b/ui/views/touchui/touch_selection_menu_views.cc index bbde208..d5caea6 100644 --- a/ui/views/touchui/touch_selection_menu_views.cc +++ b/ui/views/touchui/touch_selection_menu_views.cc
@@ -129,7 +129,7 @@ // Finally, add ellipses button. AddChildView( CreateButton(base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag)); - Layout(); + InvalidateLayout(); } LabelButton* TouchSelectionMenuViews::CreateButton(const base::string16& title,
diff --git a/ui/views/view.cc b/ui/views/view.cc index f6f61c6..eebe9cf 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc
@@ -605,8 +605,13 @@ if (layout_manager_) layout_manager_->InvalidateLayout(); - if (parent_) + if (parent_) { parent_->InvalidateLayout(); + } else { + Widget* widget = GetWidget(); + if (widget) + widget->ScheduleLayout(); + } } LayoutManager* View::GetLayoutManager() const {
diff --git a/ui/views/view.h b/ui/views/view.h index d95d572..125ac49 100644 --- a/ui/views/view.h +++ b/ui/views/view.h
@@ -1432,6 +1432,7 @@ friend class FocusManager; friend class ViewLayerTest; friend class ViewLayerPixelCanvasTest; + friend class ViewTestApi; friend class Widget; FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCache); FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCacheInRTL);
diff --git a/ui/views/view_test_api.h b/ui/views/view_test_api.h new file mode 100644 index 0000000..9c0f7acc --- /dev/null +++ b/ui/views/view_test_api.h
@@ -0,0 +1,27 @@ +// 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 UI_VIEWS_VIEW_TEST_API_H_ +#define UI_VIEWS_VIEW_TEST_API_H_ + +#include "ui/views/view.h" + +namespace views { + +class VIEWS_EXPORT ViewTestApi { + public: + explicit ViewTestApi(View* view) : view_(view) {} + ~ViewTestApi() = default; + + bool needs_layout() { return view_->needs_layout(); } + + private: + View* view_; + + DISALLOW_COPY_AND_ASSIGN(ViewTestApi); +}; + +} // namespace views + +#endif // UI_VIEWS_VIEW_TEST_API_H_
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 5b0508b..6c19974 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -911,6 +911,13 @@ content_window_->SchedulePaintInRect(rect); } +void DesktopNativeWidgetAura::ScheduleLayout() { + // ScheduleDraw() triggers a callback to + // WindowDelegate::UpdateVisualState(). + if (content_window_) + content_window_->ScheduleDraw(); +} + void DesktopNativeWidgetAura::SetCursor(gfx::NativeCursor cursor) { cursor_ = cursor; aura::client::CursorClient* cursor_client = @@ -1084,6 +1091,10 @@ native_widget_delegate_->GetHitTestMask(mask); } +void DesktopNativeWidgetAura::UpdateVisualState() { + native_widget_delegate_->LayoutRootViewIfNecessary(); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopNativeWidgetAura, ui::EventHandler implementation:
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 45319529..bed6fa5 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -173,6 +173,7 @@ int operation, ui::DragDropTypes::DragEventSource source) override; void SchedulePaintInRect(const gfx::Rect& rect) override; + void ScheduleLayout() override; void SetCursor(gfx::NativeCursor cursor) override; bool IsMouseEventsEnabled() const override; bool IsMouseButtonDown() const override; @@ -213,6 +214,7 @@ void OnWindowTargetVisibilityChanged(bool visible) override; bool HasHitTestMask() const override; void GetHitTestMask(SkPath* mask) const override; + void UpdateVisualState() override; // Overridden from ui::EventHandler: void OnKeyEvent(ui::KeyEvent* event) override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index 1afcdd74..ac7c92d 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -549,7 +549,6 @@ non_client_view->client_view()->InvalidateLayout(); non_client_view->InvalidateLayout(); } - widget->GetRootView()->Layout(); } void DesktopWindowTreeHostPlatform::RemoveNonClientEventFilter() {
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 867867b53..26290c4 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -2009,7 +2009,6 @@ non_client_view->client_view()->InvalidateLayout(); non_client_view->InvalidateLayout(); } - widget->GetRootView()->Layout(); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index 6de855c..1659e91 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc
@@ -700,6 +700,12 @@ window_->SchedulePaintInRect(rect); } +void NativeWidgetAura::ScheduleLayout() { + // ScheduleDraw() triggers a callback to WindowDelegate::UpdateVisualState(). + if (window_) + window_->ScheduleDraw(); +} + void NativeWidgetAura::SetCursor(gfx::NativeCursor cursor) { cursor_ = cursor; aura::client::CursorClient* cursor_client = @@ -916,6 +922,10 @@ delegate_->GetHitTestMask(mask); } +void NativeWidgetAura::UpdateVisualState() { + delegate_->LayoutRootViewIfNecessary(); +} + //////////////////////////////////////////////////////////////////////////////// // NativeWidgetAura, aura::WindowObserver implementation:
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h index c71289dc..9f50fed 100644 --- a/ui/views/widget/native_widget_aura.h +++ b/ui/views/widget/native_widget_aura.h
@@ -134,6 +134,7 @@ int operation, ui::DragDropTypes::DragEventSource source) override; void SchedulePaintInRect(const gfx::Rect& rect) override; + void ScheduleLayout() override; void SetCursor(gfx::NativeCursor cursor) override; bool IsMouseEventsEnabled() const override; bool IsMouseButtonDown() const override; @@ -173,6 +174,7 @@ void OnWindowTargetVisibilityChanged(bool visible) override; bool HasHitTestMask() const override; void GetHitTestMask(SkPath* mask) const override; + void UpdateVisualState() override; // Overridden from aura::WindowObserver: void OnWindowPropertyChanged(aura::Window* window,
diff --git a/ui/views/widget/native_widget_delegate.h b/ui/views/widget/native_widget_delegate.h index 8965822..57fdde7 100644 --- a/ui/views/widget/native_widget_delegate.h +++ b/ui/views/widget/native_widget_delegate.h
@@ -155,6 +155,9 @@ gfx::NativeView child, ui::Layer* child_layer, const gfx::Point& location) = 0; + + // Called to process a previous call to ScheduleLayout(). + virtual void LayoutRootViewIfNecessary() = 0; }; } // namespace internal
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h index da7a220..dba6b57 100644 --- a/ui/views/widget/native_widget_mac.h +++ b/ui/views/widget/native_widget_mac.h
@@ -80,6 +80,11 @@ WindowOpenDisposition window_open_disposition, bool is_before_first_responder); + ui::Compositor* GetCompositor() { + return const_cast<ui::Compositor*>( + const_cast<const NativeWidgetMac*>(this)->GetCompositor()); + } + // internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override; void OnWidgetInitDone() override; @@ -151,6 +156,7 @@ int operation, ui::DragDropTypes::DragEventSource source) override; void SchedulePaintInRect(const gfx::Rect& rect) override; + void ScheduleLayout() override; void SetCursor(gfx::NativeCursor cursor) override; void ShowEmojiPanel() override; bool IsMouseEventsEnabled() const override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index be935af..c464e86 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -576,6 +576,12 @@ bridge_host_->layer()->SchedulePaint(rect); } +void NativeWidgetMac::ScheduleLayout() { + ui::Compositor* compositor = GetCompositor(); + if (compositor) + compositor->ScheduleDraw(); +} + void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) { if (bridge_impl()) bridge_impl()->SetCursor(cursor);
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h index e594c69..c02942b 100644 --- a/ui/views/widget/native_widget_private.h +++ b/ui/views/widget/native_widget_private.h
@@ -213,6 +213,7 @@ int operation, ui::DragDropTypes::DragEventSource source) = 0; virtual void SchedulePaintInRect(const gfx::Rect& rect) = 0; + virtual void ScheduleLayout() = 0; virtual void SetCursor(gfx::NativeCursor cursor) = 0; virtual void ShowEmojiPanel(); virtual bool IsMouseEventsEnabled() const = 0;
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 1bf59a1..4e00aae2 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -700,7 +700,7 @@ native_widget_->SetFullscreen(fullscreen); if (non_client_view_) - non_client_view_->Layout(); + non_client_view_->InvalidateLayout(); } bool Widget::IsFullscreen() const { @@ -815,6 +815,10 @@ native_widget_->SchedulePaintInRect(rect); } +void Widget::ScheduleLayout() { + native_widget_->ScheduleLayout(); +} + void Widget::SetCursor(gfx::NativeCursor cursor) { native_widget_->SetCursor(cursor); } @@ -1413,6 +1417,11 @@ return true; } +void Widget::LayoutRootViewIfNecessary() { + if (root_view_ && root_view_->needs_layout()) + root_view_->Layout(); +} + //////////////////////////////////////////////////////////////////////////////// // Widget, ui::EventSource implementation: ui::EventSink* Widget::GetEventSink() {
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index 6f1f45f..06119b2 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h
@@ -632,6 +632,10 @@ // redrawn. virtual void SchedulePaintInRect(const gfx::Rect& rect); + // Schedule a layout to occur. This is called by RootView, client code should + // not need to call this. + void ScheduleLayout(); + // Sets the currently visible cursor. If |cursor| is NULL, the cursor used // before the current is restored. void SetCursor(gfx::NativeCursor cursor); @@ -853,6 +857,7 @@ gfx::NativeView child, ui::Layer* child_layer, const gfx::Point& location) override; + void LayoutRootViewIfNecessary() override; // Overridden from ui::EventSource: ui::EventSink* GetEventSink() override; @@ -883,8 +888,8 @@ virtual void OnDragComplete(); private: - friend class ComboboxTest; friend class ButtonTest; + friend class ComboboxTest; friend class TextfieldTest; friend class ViewAuraTest; friend void DisableActivationChangeHandlingForTests();
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc index 2f05546..e90e907 100644 --- a/ui/views/widget/widget_interactive_uitest.cc +++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -634,12 +634,15 @@ // Tests mouse move outside of the window into the "resize controller" and back // will still generate an OnMouseEntered and OnMouseExited event.. TEST_F(WidgetTestInteractive, CheckResizeControllerEvents) { - Widget* toplevel = CreateTopLevelPlatformWidget(); + Widget* toplevel = CreateTopLevelFramelessPlatformWidget(); toplevel->SetBounds(gfx::Rect(0, 0, 100, 100)); MouseView* view = new MouseView(); view->SetBounds(90, 90, 10, 10); + // |view| needs to be a particular size. Reset the LayoutManager so that + // it doesn't get resized. + toplevel->GetRootView()->SetLayoutManager(nullptr); toplevel->GetRootView()->AddChildView(view); toplevel->Show();
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc index fea8ac0c..ee89998 100644 --- a/ui/views/widget/widget_unittest.cc +++ b/ui/views/widget/widget_unittest.cc
@@ -17,6 +17,7 @@ #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/compositor/test/draw_waiter_for_test.h" #include "ui/events/event_observer.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" @@ -25,10 +26,12 @@ #include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/event_monitor.h" +#include "ui/views/layout/fill_layout.h" #include "ui/views/test/native_widget_factory.h" #include "ui/views/test/test_views.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" +#include "ui/views/view_test_api.h" #include "ui/views/widget/native_widget_delegate.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/root_view.h" @@ -557,68 +560,7 @@ // Test to verify using various Widget methods doesn't crash when the underlying // NativeView is destroyed. // -class WidgetWithDestroyedNativeViewTest - : public ViewsTestBase, - public testing::WithParamInterface<ViewsTestBase::NativeWidgetType> { - public: - WidgetWithDestroyedNativeViewTest() = default; - ~WidgetWithDestroyedNativeViewTest() override = default; - - // ViewsTestBase: - void SetUp() override { - set_native_widget_type(GetParam()); - ViewsTestBase::SetUp(); - } - - void InvokeWidgetMethods(Widget* widget) { - widget->GetNativeView(); - widget->GetNativeWindow(); - ui::Accelerator accelerator; - widget->GetAccelerator(0, &accelerator); - widget->GetTopLevelWidget(); - widget->GetWindowBoundsInScreen(); - widget->GetClientAreaBoundsInScreen(); - widget->SetBounds(gfx::Rect(0, 0, 100, 80)); - widget->SetSize(gfx::Size(10, 11)); - widget->SetBoundsConstrained(gfx::Rect(0, 0, 120, 140)); - widget->SetVisibilityChangedAnimationsEnabled(false); - widget->StackAtTop(); - widget->IsClosed(); - widget->Close(); - widget->Hide(); - widget->Activate(); - widget->Deactivate(); - widget->IsActive(); - widget->SetAlwaysOnTop(true); - widget->IsAlwaysOnTop(); - widget->Maximize(); - widget->Minimize(); - widget->Restore(); - widget->IsMaximized(); - widget->IsFullscreen(); - widget->SetOpacity(0.f); - widget->FlashFrame(true); - widget->IsVisible(); - widget->GetThemeProvider(); - widget->GetNativeTheme(); - widget->GetFocusManager(); - widget->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2)); - widget->IsMouseEventsEnabled(); - widget->SetNativeWindowProperty("xx", widget); - widget->GetNativeWindowProperty("xx"); - widget->GetFocusTraversable(); - widget->GetLayer(); - widget->ReorderNativeViews(); - widget->SetCapture(widget->GetRootView()); - widget->ReleaseCapture(); - widget->HasCapture(); - widget->GetWorkAreaBoundsInScreen(); - widget->IsTranslucentWindowOpacitySupported(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(WidgetWithDestroyedNativeViewTest); -}; +using WidgetWithDestroyedNativeViewTest = ViewsTestBaseWithNativeWidgetType; TEST_P(WidgetWithDestroyedNativeViewTest, Test) { Widget widget; @@ -628,7 +570,49 @@ widget.Show(); widget.native_widget_private()->CloseNow(); - InvokeWidgetMethods(&widget); + widget.GetNativeView(); + widget.GetNativeWindow(); + ui::Accelerator accelerator; + widget.GetAccelerator(0, &accelerator); + widget.GetTopLevelWidget(); + widget.GetWindowBoundsInScreen(); + widget.GetClientAreaBoundsInScreen(); + widget.SetBounds(gfx::Rect(0, 0, 100, 80)); + widget.SetSize(gfx::Size(10, 11)); + widget.SetBoundsConstrained(gfx::Rect(0, 0, 120, 140)); + widget.SetVisibilityChangedAnimationsEnabled(false); + widget.StackAtTop(); + widget.IsClosed(); + widget.Close(); + widget.Hide(); + widget.Activate(); + widget.Deactivate(); + widget.IsActive(); + widget.SetAlwaysOnTop(true); + widget.IsAlwaysOnTop(); + widget.Maximize(); + widget.Minimize(); + widget.Restore(); + widget.IsMaximized(); + widget.IsFullscreen(); + widget.SetOpacity(0.f); + widget.FlashFrame(true); + widget.IsVisible(); + widget.GetThemeProvider(); + widget.GetNativeTheme(); + widget.GetFocusManager(); + widget.SchedulePaintInRect(gfx::Rect(0, 0, 1, 2)); + widget.IsMouseEventsEnabled(); + widget.SetNativeWindowProperty("xx", &widget); + widget.GetNativeWindowProperty("xx"); + widget.GetFocusTraversable(); + widget.GetLayer(); + widget.ReorderNativeViews(); + widget.SetCapture(widget.GetRootView()); + widget.ReleaseCapture(); + widget.HasCapture(); + widget.GetWorkAreaBoundsInScreen(); + widget.IsTranslucentWindowOpacitySupported(); } INSTANTIATE_TEST_SUITE_P( @@ -3340,6 +3324,8 @@ EXPECT_FALSE(frame->fullscreen_layout_called()); widget->SetFullscreen(true); widget->Show(); + EXPECT_TRUE(ViewTestApi(frame).needs_layout()); + widget->LayoutRootViewIfNecessary(); RunPendingMessages(); EXPECT_TRUE(frame->fullscreen_layout_called()); @@ -3811,6 +3797,84 @@ EXPECT_EQ(1, event_count_view->GetEventCount(ui::ET_MOUSEWHEEL)); } +class LayoutCountingView : public View { + public: + LayoutCountingView() = default; + ~LayoutCountingView() override = default; + + void set_layout_closure(base::OnceClosure layout_closure) { + layout_closure_ = std::move(layout_closure); + } + + size_t GetAndClearLayoutCount() { + const size_t count = layout_count_; + layout_count_ = 0u; + return count; + } + + // View: + void Layout() override { + ++layout_count_; + View::Layout(); + if (layout_closure_) + std::move(layout_closure_).Run(); + } + + private: + size_t layout_count_ = 0u; + + // If valid, this is run when Layout() is called. + base::OnceClosure layout_closure_; + + DISALLOW_COPY_AND_ASSIGN(LayoutCountingView); +}; + +using WidgetInvalidateLayoutTest = ViewsTestBaseWithNativeWidgetType; + +TEST_P(WidgetInvalidateLayoutTest, InvalidateLayout) { + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget.Init(params); + widget.SetBounds(gfx::Rect(0, 0, 600, 600)); + LayoutCountingView* view = + widget.widget_delegate()->GetContentsView()->AddChildView( + std::make_unique<LayoutCountingView>()); + view->parent()->SetLayoutManager(std::make_unique<FillLayout>()); + // Force an initial Layout(). + // TODO(sky): this shouldn't be necessary, adding a child view should trigger + // ScheduleLayout(). + view->Layout(); + widget.Show(); + + ui::Compositor* compositor = widget.GetCompositor(); + ASSERT_TRUE(compositor); + compositor->ScheduleDraw(); + ui::DrawWaiterForTest::WaitForCompositingEnded(compositor); + + base::RunLoop run_loop; + view->GetAndClearLayoutCount(); + // Don't use WaitForCompositingEnded() here as it's entirely possible nothing + // will be drawn (which means WaitForCompositingEnded() isn't run). Instead + // wait for Layout() to be called. + view->set_layout_closure(run_loop.QuitClosure()); + EXPECT_FALSE(ViewTestApi(view).needs_layout()); + EXPECT_FALSE(ViewTestApi(widget.GetRootView()).needs_layout()); + view->InvalidateLayout(); + EXPECT_TRUE(ViewTestApi(view).needs_layout()); + EXPECT_TRUE(ViewTestApi(widget.GetRootView()).needs_layout()); + run_loop.Run(); + EXPECT_EQ(1u, view->GetAndClearLayoutCount()); + EXPECT_FALSE(ViewTestApi(view).needs_layout()); + EXPECT_FALSE(ViewTestApi(widget.GetRootView()).needs_layout()); +} + +INSTANTIATE_TEST_SUITE_P( + WidgetInvalidateLayoutTest, + WidgetInvalidateLayoutTest, + ::testing::Values(ViewsTestBase::NativeWidgetType::kDefault, + ViewsTestBase::NativeWidgetType::kDesktop)); + class WidgetShadowTest : public WidgetTest { public: WidgetShadowTest() = default;
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc index 068aac6..bf21e227 100644 --- a/ui/views/window/dialog_client_view.cc +++ b/ui/views/window/dialog_client_view.cc
@@ -264,16 +264,11 @@ return GetWidget()->widget_delegate()->AsDialogDelegate(); } -void DialogClientView::ChildPreferredSizeChanged(View* child) { - if (!adding_or_removing_views_ && child == extra_view_) - Layout(); -} - void DialogClientView::ChildVisibilityChanged(View* child) { // Showing or hiding |extra_view_| can alter which columns have linked sizes. if (child == extra_view_) UpdateDialogButtons(); - ChildPreferredSizeChanged(child); + InvalidateLayout(); } void DialogClientView::OnDialogChanged() {
diff --git a/ui/views/window/dialog_client_view.h b/ui/views/window/dialog_client_view.h index 47c80e6e6..6a894a0 100644 --- a/ui/views/window/dialog_client_view.h +++ b/ui/views/window/dialog_client_view.h
@@ -84,7 +84,6 @@ DialogDelegate* GetDialogDelegate() const; // View implementation. - void ChildPreferredSizeChanged(View* child) override; void ChildVisibilityChanged(View* child) override; // DialogObserver:
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc index 61f2f236..b8e92a1 100644 --- a/ui/views/window/dialog_delegate_unittest.cc +++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -263,6 +263,7 @@ const NonClientView* view = dialog()->GetWidget()->non_client_view(); dialog()->set_title(base::ASCIIToUTF16("Title")); dialog()->GetWidget()->UpdateWindowTitle(); + dialog()->GetWidget()->LayoutRootViewIfNecessary(); BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view()); struct {
diff --git a/ui/views/window/non_client_view.cc b/ui/views/window/non_client_view.cc index 8786480..c8cb1ba 100644 --- a/ui/views/window/non_client_view.cc +++ b/ui/views/window/non_client_view.cc
@@ -87,7 +87,7 @@ Widget* widget = GetWidget(); SetFrameView(widget->CreateNonClientFrameView()); widget->ThemeChanged(); - Layout(); + InvalidateLayout(); SchedulePaint(); }