diff --git a/DEPS b/DEPS index 1ff8db1..12cfd6b 100644 --- a/DEPS +++ b/DEPS
@@ -1392,7 +1392,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6b936a2133cd1ba23c2a27657c7d438dc2004e5a', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4f841753fcf62c678635a459e60642d2cb697c56', 'condition': 'checkout_src_internal', },
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index 39a8e6f..e06ba8e 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -579,7 +579,6 @@ "/DELAYLOAD:dxgi.dll", "/DELAYLOAD:dxva2.dll", "/DELAYLOAD:esent.dll", - "/DELAYLOAD:gdi32.dll", "/DELAYLOAD:imm32.dll", "/DELAYLOAD:ole32.dll", "/DELAYLOAD:oleacc.dll",
diff --git a/chrome/VERSION b/chrome/VERSION index f7057d3c..783f304 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=76 MINOR=0 BUILD=3809 -PATCH=73 +PATCH=74
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java index 8b7e727..6a95bd41 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
@@ -252,8 +252,11 @@ SharedPreferences prefs = getSharedPreferences(); prefs.edit().remove(LAST_TAB_URL).apply(); }); - mHistoryBridge.markItemForRemoval(mHistoryItem); - mHistoryBridge.removeItems(); + // Don't actually remove the element from history db. This is fine since + // we're tracking it in the pref, the open last tab button will not show it. + // If this history item is backed by 2+ rows a history deletion could + // trigger a clear and suppression of suggested articles, see + // https://crbug.com/982973. mHistoryItem = null; mLastTabUrl = null; updateModel();
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 5842809..939c02e 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -2010,6 +2010,8 @@ "tpm_firmware_update.h", "tpm_firmware_update_notification.cc", "tpm_firmware_update_notification.h", + "u2f_notification.cc", + "u2f_notification.h", "ui/echo_dialog_listener.h", "ui/echo_dialog_view.cc", "ui/echo_dialog_view.h",
diff --git a/chrome/browser/chromeos/extensions/default_web_app_ids.h b/chrome/browser/chromeos/extensions/default_web_app_ids.h index 196ba26..d403c77 100644 --- a/chrome/browser/chromeos/extensions/default_web_app_ids.h +++ b/chrome/browser/chromeos/extensions/default_web_app_ids.h
@@ -16,8 +16,8 @@ constexpr char kShowtimeAppId[] = "eoccpgmpiempcflglfokeengliildkag"; // Generated as -// web_app::GenerateAppIdFromURL(GURL("https://canvas.apps.chrome/index.html)). -constexpr char kCanvasAppId[] = "memejfanofdmelnjmboefinndljpifdm"; +// web_app::GenerateAppIdFromURL(GURL("https://canvas.apps.chrome/")). +constexpr char kCanvasAppId[] = "ieailfmhaghpphfffooibmlghaeopach"; } // namespace default_web_apps } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.cc b/chrome/browser/chromeos/login/session/chrome_session_manager.cc index fd0fb56..d818255 100644 --- a/chrome/browser/chromeos/login/session/chrome_session_manager.cc +++ b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
@@ -35,6 +35,7 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/tether/tether_service.h" #include "chrome/browser/chromeos/tpm_firmware_update_notification.h" +#include "chrome/browser/chromeos/u2f_notification.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/app_list/app_list_client_impl.h" @@ -195,6 +196,7 @@ UserSessionManager::GetInstance()->CheckEolStatus(user_profile); tpm_firmware_update::ShowNotificationIfNeeded(user_profile); + UserSessionManager::GetInstance()->MaybeShowU2FNotification(); g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetTPMAutoUpdateModePolicyHandler()
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index 6b8b4e1..54e5e1d 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -81,6 +81,7 @@ #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/tether/tether_service.h" #include "chrome/browser/chromeos/tpm_firmware_update_notification.h" +#include "chrome/browser/chromeos/u2f_notification.h" #include "chrome/browser/component_updater/crl_set_component_installer.h" #include "chrome/browser/component_updater/sth_set_component_remover.h" #include "chrome/browser/first_run/first_run.h" @@ -2179,6 +2180,9 @@ // and show the message accordingly. tpm_firmware_update::ShowNotificationIfNeeded(profile); + // Show legacy U2F notification if applicable. + MaybeShowU2FNotification(); + g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetTPMAutoUpdateModePolicyHandler() @@ -2289,6 +2293,7 @@ token_handle_util_.reset(); first_run::GoodiesDisplayer::Delete(); always_on_vpn_manager_.reset(); + u2f_notification_.reset(); } void UserSessionManager::SetSwitchesForUser( @@ -2311,6 +2316,13 @@ all_switches); } +void UserSessionManager::MaybeShowU2FNotification() { + if (!u2f_notification_) { + u2f_notification_ = std::make_unique<U2FNotification>(); + u2f_notification_->Check(); + } +} + void UserSessionManager::CreateTokenUtilIfMissing() { if (!token_handle_util_.get()) token_handle_util_.reset(new TokenHandleUtil());
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h index 7aa0b09..ed6318e0 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.h +++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -28,6 +28,7 @@ #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h" #include "chrome/browser/chromeos/login/signin/token_handle_util.h" #include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/u2f_notification.h" #include "chromeos/dbus/session_manager/session_manager_client.h" #include "chromeos/login/auth/authenticator.h" #include "chromeos/login/auth/user_context.h" @@ -312,6 +313,9 @@ // password from the UserContext regardless of the value of |send_password|. void OnUserNetworkPolicyParsed(bool send_password); + // Shows U2F notification if necessary. + void MaybeShowU2FNotification(); + private: friend class test::UserSessionManagerTestApi; friend struct base::DefaultSingletonTraits<UserSessionManager>; @@ -611,6 +615,8 @@ std::unique_ptr<ChildPolicyObserver> child_policy_observer_; + std::unique_ptr<U2FNotification> u2f_notification_; + base::WeakPtrFactory<UserSessionManager> weak_factory_; DISALLOW_COPY_AND_ASSIGN(UserSessionManager);
diff --git a/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc b/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc index 1cb34c8..4332d7c 100644 --- a/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc +++ b/chrome/browser/chromeos/policy/device_wilco_dtc_configuration_handler.cc
@@ -14,7 +14,6 @@ chromeos::WilcoDtcSupportdManager* GetWilcoDtcSupportdManager() { chromeos::WilcoDtcSupportdManager* const wilco_dtc_supportd_manager = chromeos::WilcoDtcSupportdManager::Get(); - DCHECK(wilco_dtc_supportd_manager); return wilco_dtc_supportd_manager; } @@ -32,14 +31,18 @@ void DeviceWilcoDtcConfigurationHandler::OnDeviceExternalDataCleared( const std::string& policy) { - GetWilcoDtcSupportdManager()->SetConfigurationData(nullptr); + auto* wilco_manager = GetWilcoDtcSupportdManager(); + if (wilco_manager) + wilco_manager->SetConfigurationData(nullptr); } void DeviceWilcoDtcConfigurationHandler::OnDeviceExternalDataFetched( const std::string& policy, std::unique_ptr<std::string> data, const base::FilePath& file_path) { - GetWilcoDtcSupportdManager()->SetConfigurationData(std::move(data)); + auto* wilco_manager = GetWilcoDtcSupportdManager(); + if (wilco_manager) + wilco_manager->SetConfigurationData(std::move(data)); } void DeviceWilcoDtcConfigurationHandler::Shutdown() {
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc index 46650a74..189f179 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -88,6 +88,7 @@ kDeviceQuirksDownloadEnabled, kDeviceRebootOnUserSignout, kDeviceScheduledUpdateCheck, + kDeviceSecondFactorAuthenticationMode, kDeviceUnaffiliatedCrostiniAllowed, kDeviceWiFiAllowed, kDeviceWilcoDtcAllowed, @@ -801,6 +802,13 @@ kDeviceDockMacAddressSource, policy.device_dock_mac_address_source().source()); } + + if (policy.has_device_second_factor_authentication() && + policy.device_second_factor_authentication().has_mode()) { + new_values_cache->SetInteger( + kDeviceSecondFactorAuthenticationMode, + policy.device_second_factor_authentication().mode()); + } } void DecodeLogUploadPolicies(const em::ChromeDeviceSettingsProto& policy,
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc index c3c22ad..2fbfd62 100644 --- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc +++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -194,7 +194,7 @@ } void VerifyPolicyValue(const char* policy_key, - const base::Value* const ptr_to_expected_value) { + const base::Value* ptr_to_expected_value) { // The pointer might be null, so check before dereferencing. if (ptr_to_expected_value) EXPECT_EQ(*ptr_to_expected_value, *provider_->Get(policy_key)); @@ -211,8 +211,7 @@ } // Helper routine to check value of the LoginScreenDomainAutoComplete policy. - void VerifyDomainAutoComplete( - const base::Value* const ptr_to_expected_value) { + void VerifyDomainAutoComplete(const base::Value* ptr_to_expected_value) { VerifyPolicyValue(kAccountsPrefLoginScreenDomainAutoComplete, ptr_to_expected_value); } @@ -281,7 +280,7 @@ // Helper routine that sets the device DeviceWilcoDtcAllowed policy. void SetDeviceWilcoDtcAllowedSetting(bool device_wilco_dtc_allowed) { - em::DeviceWilcoDtcAllowedProto* const proto = + em::DeviceWilcoDtcAllowedProto* proto = device_policy_->payload().mutable_device_wilco_dtc_allowed(); proto->set_device_wilco_dtc_allowed(device_wilco_dtc_allowed); BuildAndInstallDevicePolicy(); @@ -290,12 +289,20 @@ void SetDeviceDockMacAddressSourceSetting( em::DeviceDockMacAddressSourceProto::Source device_dock_mac_address_source) { - em::DeviceDockMacAddressSourceProto* const proto = + em::DeviceDockMacAddressSourceProto* proto = device_policy_->payload().mutable_device_dock_mac_address_source(); proto->set_source(device_dock_mac_address_source); BuildAndInstallDevicePolicy(); } + void SetDeviceSecondFactorAuthenticationModeSetting( + em::DeviceSecondFactorAuthenticationProto::U2fMode mode) { + em::DeviceSecondFactorAuthenticationProto* proto = + device_policy_->payload().mutable_device_second_factor_authentication(); + proto->set_mode(mode); + BuildAndInstallDevicePolicy(); + } + ScopedTestingLocalState local_state_; std::unique_ptr<DeviceSettingsProvider> provider_; @@ -769,4 +776,29 @@ EXPECT_EQ(base::Value(3), *provider_->Get(kDeviceDockMacAddressSource)); } +TEST_F(DeviceSettingsProviderTest, + DeviceSecondFactorAuthenticationModeSetting) { + VerifyPolicyValue(kDeviceSecondFactorAuthenticationMode, nullptr); + + SetDeviceSecondFactorAuthenticationModeSetting( + em::DeviceSecondFactorAuthenticationProto::UNSET); + EXPECT_EQ(base::Value(0), + *provider_->Get(kDeviceSecondFactorAuthenticationMode)); + + SetDeviceSecondFactorAuthenticationModeSetting( + em::DeviceSecondFactorAuthenticationProto::DISABLED); + EXPECT_EQ(base::Value(1), + *provider_->Get(kDeviceSecondFactorAuthenticationMode)); + + SetDeviceSecondFactorAuthenticationModeSetting( + em::DeviceSecondFactorAuthenticationProto::U2F); + EXPECT_EQ(base::Value(2), + *provider_->Get(kDeviceSecondFactorAuthenticationMode)); + + SetDeviceSecondFactorAuthenticationModeSetting( + em::DeviceSecondFactorAuthenticationProto::U2F_EXTENDED); + EXPECT_EQ(base::Value(3), + *provider_->Get(kDeviceSecondFactorAuthenticationMode)); +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/u2f_notification.cc b/chrome/browser/chromeos/u2f_notification.cc new file mode 100644 index 0000000..58b9918 --- /dev/null +++ b/chrome/browser/chromeos/u2f_notification.cc
@@ -0,0 +1,172 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/u2f_notification.h" + +#include <memory> + +#include "ash/public/cpp/notification_utils.h" +#include "base/bind.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/stl_util.h" +#include "base/strings/string16.h" +#include "base/task/post_task.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/notifications/notification_display_service.h" +#include "chrome/browser/notifications/notification_display_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_navigator_params.h" +#include "chrome/grit/generated_resources.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/debug_daemon_client.h" +#include "components/policy/proto/chrome_device_policy.pb.h" +#include "components/strings/grit/components_strings.h" +#include "third_party/cros_system_api/dbus/debugd/dbus-constants.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/message_center/public/cpp/notification.h" + +namespace chromeos { +namespace { + +constexpr char kU2FNotificationId[] = "chrome://u2f_notification"; +constexpr char kU2FAdvisoryURL[] = + "https://sites.google.com/a/chromium.org/dev/chromium-os/" + "u2f-ecdsa-vulnerability"; + +// Notification button identifiers. +enum class ButtonIndex : int { + kLearnMore = 0, + kReset = 1, +}; + +} // namespace + +U2FNotification::U2FNotification() {} + +U2FNotification::~U2FNotification() {} + +void U2FNotification::Check() { + DBusThreadManager::Get()->GetDebugDaemonClient()->GetU2fFlags(base::BindOnce( + &U2FNotification::CheckStatus, weak_factory_.GetWeakPtr())); +} + +void U2FNotification::CheckStatus(base::Optional<std::set<std::string>> flags) { + if (!flags) { + LOG(ERROR) << "Failed to get U2F flags."; + return; + } + + // The legacy implementation is only enabled if either the U2F or G2F flags + // are present and the user_keys flag is off (the latter enables the improved + // implementation). + if (!(base::ContainsKey(*flags, debugd::u2f_flags::kU2f) || + base::ContainsKey(*flags, debugd::u2f_flags::kG2f)) || + base::ContainsKey(*flags, debugd::u2f_flags::kUserKeys)) { + return; + } + + CrosSettings* settings = CrosSettings::Get(); + switch (settings->PrepareTrustedValues(base::BindRepeating( + &U2FNotification::CheckStatus, weak_factory_.GetWeakPtr(), flags))) { + case CrosSettingsProvider::TEMPORARILY_UNTRUSTED: + // Retry happens via the callback registered above. + return; + case CrosSettingsProvider::PERMANENTLY_UNTRUSTED: + // No device settings, so they won't take effect. + break; + case CrosSettingsProvider::TRUSTED: + // If the 2FA setting is present and not set to "unset", it disables the + // legacy implementation, so bail if that is the case. This corresponds + // with behavior in u2fd and should be kept in sync. + constexpr auto kU2fModeUnset = + enterprise_management::DeviceSecondFactorAuthenticationProto::UNSET; + int mode; + if (settings->GetInteger(kDeviceSecondFactorAuthenticationMode, &mode) && + mode != kU2fModeUnset) { + return; + } + break; + } + + // Legacy implementation is on, notify. + ShowNotification(); +} + +void U2FNotification::ShowNotification() { + Profile* profile = ProfileManager::GetPrimaryUserProfile(); + + message_center::RichNotificationData data; + data.buttons.emplace_back(l10n_util::GetStringUTF16(IDS_LEARN_MORE)); + data.buttons.emplace_back( + l10n_util::GetStringUTF16(IDS_U2F_INSECURE_NOTIFICATION_RESET)); + std::unique_ptr<message_center::Notification> notification = + ash::CreateSystemNotification( + message_center::NOTIFICATION_TYPE_SIMPLE, kU2FNotificationId, + l10n_util::GetStringUTF16(IDS_U2F_INSECURE_NOTIFICATION_TITLE), + l10n_util::GetStringUTF16(IDS_U2F_INSECURE_NOTIFICATION_MESSAGE), + base::string16(), GURL(kU2FNotificationId), + message_center::NotifierId( + message_center::NotifierType::SYSTEM_COMPONENT, + kU2FNotificationId), + data, + base::MakeRefCounted<message_center::HandleNotificationClickDelegate>( + base::BindRepeating(&U2FNotification::OnNotificationClick, + weak_factory_.GetWeakPtr())), + gfx::kNoneIcon, + message_center::SystemNotificationWarningLevel::WARNING); + notification->SetSystemPriority(); + notification->set_pinned(false); + + NotificationDisplayServiceFactory::GetForProfile(profile)->Display( + NotificationHandler::Type::TRANSIENT, *notification, + nullptr /* metadata */); +} + +void U2FNotification::OnNotificationClick( + const base::Optional<int> button_index) { + Profile* profile = ProfileManager::GetPrimaryUserProfile(); + if (!button_index || !profile) { + return; + } + + switch (static_cast<ButtonIndex>(*button_index)) { + case ButtonIndex::kLearnMore: { + // Load the chromium.org advisory page in a new tab. + NavigateParams params(profile, GURL(kU2FAdvisoryURL), + ui::PAGE_TRANSITION_LINK); + params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; + params.window_action = NavigateParams::SHOW_WINDOW; + Navigate(¶ms); + break; + } + case ButtonIndex::kReset: { + // Add the user_keys flag. + DBusThreadManager::Get()->GetDebugDaemonClient()->GetU2fFlags( + base::BindOnce([](base::Optional<std::set<std::string>> flags) { + if (!flags) { + LOG(ERROR) << "Failed to get U2F flags."; + return; + } + flags->insert(debugd::u2f_flags::kUserKeys); + DBusThreadManager::Get()->GetDebugDaemonClient()->SetU2fFlags( + *flags, base::BindOnce([](bool result) { + if (!result) { + LOG(ERROR) << "Failed to set U2F flags."; + return; + } + })); + })); + + // TODO: Should we close in all cases? + NotificationDisplayServiceFactory::GetForProfile(profile)->Close( + NotificationHandler::Type::TRANSIENT, kU2FNotificationId); + break; + } + } +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/u2f_notification.h b/chrome/browser/chromeos/u2f_notification.h new file mode 100644 index 0000000..03ea6ddd --- /dev/null +++ b/chrome/browser/chromeos/u2f_notification.h
@@ -0,0 +1,45 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_U2F_NOTIFICATION_H_ +#define CHROME_BROWSER_CHROMEOS_U2F_NOTIFICATION_H_ + +#include <set> +#include <string> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" + +namespace chromeos { + +// Detects whether the legacy, never-officially-launched built-in U2F feature is +// enabled. If so, shows a notification to tell the user about a security issue. +class U2FNotification { + public: + U2FNotification(); + ~U2FNotification(); + + // Asynchronously checks whether the legacy implementation is enabled and if + // so, displays a notification. + void Check(); + + private: + // Checks status given the current U2F flags. + void CheckStatus(base::Optional<std::set<std::string>> flags); + + // Shows the notification. + void ShowNotification(); + + // Handles clicks on the notification. + void OnNotificationClick(const base::Optional<int> button_index); + + base::WeakPtrFactory<U2FNotification> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(U2FNotification); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_U2F_NOTIFICATION_H_
diff --git a/chrome/browser/extensions/content_verifier_browsertest.cc b/chrome/browser/extensions/content_verifier_browsertest.cc index faf1971..908bf9d 100644 --- a/chrome/browser/extensions/content_verifier_browsertest.cc +++ b/chrome/browser/extensions/content_verifier_browsertest.cc
@@ -45,6 +45,11 @@ namespace extensions { +namespace { +constexpr char kTenMegResourceExtensionId[] = + "mibjhafkjlepkpbjleahhallgddpjgle"; +} + class ContentVerifierTest : public ExtensionBrowserTest { public: ContentVerifierTest() {} @@ -327,6 +332,52 @@ extension->id(), extension->GetResourceURL(kResource)); } +// Tests that a resource reading failure due to FileURLLoader cancellation +// does not incorrectly result in content verificaton failure. +// Regression test for: http://crbug.com/977805. +IN_PROC_BROWSER_TEST_F(ContentVerifierTest, + PRE_ResourceReadCancellationDoesNotFailVerification) { + // This test extension is copied from the webstore that has actual + // signatures. + const Extension* extension = InstallExtensionFromWebstore( + test_data_dir_.AppendASCII("content_verifier/ten_meg_resource.crx"), 1); + ASSERT_TRUE(extension); + EXPECT_EQ(kTenMegResourceExtensionId, extension->id()); + + // Navigate to a large resource that *likely* won't complete before + // this test ends and results in FileDataPipeProducer shutdown. This results + // in FILE_ERROR_ABORT in FileDataPipeProducer::Observer::BytesRead(). + // + // Note that this can produce false-positive results because if the resource + // completes loading before shutdown, this test will still pass. There + // currently isn't a way to forcefully shut down FileDataPipeProducer. + // Also, whether to pursue such effort is debatable as it feels poking into + // the implementation detail a little too much. + const char kLargeResource[] = "ten_meg_background.js"; + ui_test_utils::NavigateToURLWithDisposition( + browser(), extension->GetResourceURL(kLargeResource), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_NONE); +} + +IN_PROC_BROWSER_TEST_F(ContentVerifierTest, + ResourceReadCancellationDoesNotFailVerification) { + // Expect the extension to not get disabled due to corruption. + extensions::ExtensionRegistry* registry = + extensions::ExtensionRegistry::Get(profile()); + { + // Add a helpful hint, in case the regression reappears. + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); + int reasons = prefs->GetDisableReasons(kTenMegResourceExtensionId); + EXPECT_EQ(disable_reason::DISABLE_NONE, reasons) + << "Unexpected disable reasons. Includes corruption: " + << (reasons & disable_reason::DISABLE_CORRUPTED); + } + const Extension* extension = + registry->enabled_extensions().GetByID(kTenMegResourceExtensionId); + ASSERT_TRUE(extension); +} + class ContentVerifierPolicyTest : public ContentVerifierTest { public: // We need to do this work here because the force-install policy values are
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc index 0506112..f7edc97 100644 --- a/chrome/browser/ui/views/tabs/tab.cc +++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -234,10 +234,10 @@ else base::RecordAction(UserMetricsAction("CloseTab_RecordingIndicator")); - const CloseTabSource source = - (event.type() == ui::ET_MOUSE_RELEASED && - !(event.flags() & ui::EF_FROM_TOUCH)) ? CLOSE_TAB_FROM_MOUSE - : CLOSE_TAB_FROM_TOUCH; + const CloseTabSource source = (event.type() == ui::ET_MOUSE_RELEASED && + !(event.flags() & ui::EF_FROM_TOUCH)) + ? CLOSE_TAB_FROM_MOUSE + : CLOSE_TAB_FROM_TOUCH; DCHECK_EQ(close_button_, sender); controller_->CloseTab(this, source); if (event.type() == ui::ET_GESTURE_TAP) @@ -525,9 +525,26 @@ void Tab::OnMouseMoved(const ui::MouseEvent& event) { tab_style_->SetHoverLocation(event.location()); controller_->OnMouseEventInTab(this, event); +#if defined(OS_LINUX) + MaybeUpdateHoverStatus(event); +#endif } void Tab::OnMouseEntered(const ui::MouseEvent& event) { + MaybeUpdateHoverStatus(event); +} + +void Tab::MaybeUpdateHoverStatus(const ui::MouseEvent& event) { +#if defined(OS_LINUX) + // Move the hit test area for hovering up so that it is not overlapped by tab + // hover cards when they are shown. + // TODO(crbug/978134): Once Linux/CrOS widget transparency is solved, remove + // this case. + constexpr int kHoverCardOverlap = 6; + if (event.location().y() >= height() - kHoverCardOverlap) + return; +#endif + mouse_hovered_ = true; tab_style_->ShowHover(TabStyle::ShowHoverStyle::kSubtle); UpdateForegroundColors();
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h index 6c28cd1a..b1155bc 100644 --- a/chrome/browser/ui/views/tabs/tab.h +++ b/chrome/browser/ui/views/tabs/tab.h
@@ -35,7 +35,7 @@ namespace gfx { class Animation; class LinearAnimation; -} +} // namespace gfx namespace views { class Label; } @@ -219,6 +219,10 @@ // and alert icon. void UpdateForegroundColors(); + // Considers switching to hovered mode or [re-]showing the hover card based on + // the mouse moving over the tab. + void MaybeUpdateHoverStatus(const ui::MouseEvent& event); + // The controller, never nullptr. TabController* const controller_;
diff --git a/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx b/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx new file mode 100644 index 0000000..7a35a0e --- /dev/null +++ b/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx Binary files differ
diff --git a/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx.INFO b/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx.INFO new file mode 100644 index 0000000..68c397b --- /dev/null +++ b/chrome/test/data/extensions/content_verifier/ten_meg_resource.crx.INFO
@@ -0,0 +1,5 @@ +Fetched from Chrome Web Store. +CRX ID = mibjhafkjlepkpbjleahhallgddpjgle + +This extension has a background script called ten_meg_background.js that is +10526729 bytes (> 10 meg) in size.
diff --git a/chrome/test/delayload/delayloads_unittest.cc b/chrome/test/delayload/delayloads_unittest.cc index d2307288..ee97aa1 100644 --- a/chrome/test/delayload/delayloads_unittest.cc +++ b/chrome/test/delayload/delayloads_unittest.cc
@@ -158,7 +158,7 @@ EXPECT_EQ(nullptr, ::GetModuleHandle(L"user32.dll")); } -TEST_F(DelayloadsTest, ChromeChildDllDelayloadsCheck) { +TEST_F(DelayloadsTest, DISABLED_ChromeChildDllDelayloadsCheck) { base::FilePath dll; ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll)); dll = dll.Append(L"chrome_child.dll"); @@ -209,7 +209,7 @@ } } -TEST_F(DelayloadsTest, ChromeChildDllLoadSanityTest) { +TEST_F(DelayloadsTest, DISABLED_ChromeChildDllLoadSanityTest) { // On Win7 we expect this test to result in user32.dll getting loaded. As a // result, we need to ensure it is executed in its own test process. This // "test" will re-launch with custom parameters to accomplish that.
diff --git a/chromeos/settings/cros_settings_names.cc b/chromeos/settings/cros_settings_names.cc index d23a2ce..9fe24df 100644 --- a/chromeos/settings/cros_settings_names.cc +++ b/chromeos/settings/cros_settings_names.cc
@@ -382,4 +382,10 @@ // respected when the device is shutdown. const char kDeviceScheduledUpdateCheck[] = "cros.device.device_scheduled_update_check"; + +// An enum pref that configures the operation mode of the built-in 2nd factor +// authenticator. +const char kDeviceSecondFactorAuthenticationMode[] = + "cros.device.device_second_factor_authentication_mode"; + } // namespace chromeos
diff --git a/chromeos/settings/cros_settings_names.h b/chromeos/settings/cros_settings_names.h index 68c04fb..b1676b604 100644 --- a/chromeos/settings/cros_settings_names.h +++ b/chromeos/settings/cros_settings_names.h
@@ -222,6 +222,9 @@ COMPONENT_EXPORT(CHROMEOS_SETTINGS) extern const char kDeviceScheduledUpdateCheck[]; +COMPONENT_EXPORT(CHROMEOS_SETTINGS) +extern const char kDeviceSecondFactorAuthenticationMode[]; + } // namespace chromeos #endif // CHROMEOS_SETTINGS_CROS_SETTINGS_NAMES_H_
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/file_url_loader_factory.cc index 5a2d068..669e3f3 100644 --- a/content/browser/file_url_loader_factory.cc +++ b/content/browser/file_url_loader_factory.cc
@@ -118,6 +118,23 @@ network::cors::OriginAccessList::AccessState::kAllowed; } +base::File::Error ToFileError(int net_error) { + // Note: For now, only return specific errors that our obervers care about. + switch (net_error) { + case net::OK: + return base::File::FILE_OK; + case net::ERR_FILE_NOT_FOUND: + return base::File::FILE_ERROR_NOT_FOUND; + case net::ERR_ACCESS_DENIED: + return base::File::FILE_ERROR_ACCESS_DENIED; + case net::ERR_ABORTED: + case net::ERR_CONNECTION_ABORTED: + return base::File::FILE_ERROR_ABORT; + default: + return base::File::FILE_ERROR_FAILED; + } +} + class FileURLDirectoryLoader : public network::mojom::URLLoader, public net::DirectoryLister::DirectoryListerDelegate { @@ -668,8 +685,11 @@ std::unique_ptr<FileURLLoaderObserver> observer) { client_->OnComplete(network::URLLoaderCompletionStatus(net_error)); client_.reset(); - if (observer) + if (observer) { + if (net_error != net::OK) + observer->OnBytesRead(nullptr, 0u, ToFileError(net_error)); observer->OnDoneReading(); + } MaybeDeleteSelf(); }
diff --git a/extensions/browser/content_verify_job.cc b/extensions/browser/content_verify_job.cc index a61c47e1..c1471f5 100644 --- a/extensions/browser/content_verify_job.cc +++ b/extensions/browser/content_verify_job.cc
@@ -42,6 +42,13 @@ base::ElapsedTimer timer; }; +bool IsIgnorableReadError(base::File::Error read_result) { + // Extension reload, for example, can cause benign + // FILE_ERROR_ABORT error. Do not incorrectly fail content verification in + // that case. See https://crbug.com/977805 for details. + return read_result == base::File::FILE_ERROR_ABORT; +} + } // namespace ContentVerifyJob::ContentVerifyJob(const ExtensionId& extension_id, @@ -89,10 +96,12 @@ base::BindOnce(&ContentVerifyJob::OnHashesReady, this)); } -void ContentVerifyJob::BytesRead(int count, const char* data) { +void ContentVerifyJob::BytesRead(const char* data, + int count, + base::File::Error read_result) { base::AutoLock auto_lock(lock_); DCHECK(!done_reading_); - BytesReadImpl(count, data); + BytesReadImpl(data, count, read_result); } void ContentVerifyJob::DoneReading() { @@ -104,6 +113,9 @@ return; DCHECK(!done_reading_); done_reading_ = true; + if (has_ignorable_read_error_) + return; + if (hashes_ready_) { if (!FinishBlock()) { DispatchFailureCallback(HASH_MISMATCH); @@ -114,12 +126,17 @@ } } -void ContentVerifyJob::BytesReadImpl(int count, const char* data) { +void ContentVerifyJob::BytesReadImpl(const char* data, + int count, + base::File::Error read_result) { ScopedElapsedTimer timer(&time_spent_); if (failed_) return; if (g_ignore_verification_for_tests) return; + if (IsIgnorableReadError(read_result)) + has_ignorable_read_error_ = true; + if (!hashes_ready_) { queue_.append(data, count); return; @@ -221,7 +238,7 @@ if (!queue_.empty()) { std::string tmp; queue_.swap(tmp); - BytesReadImpl(tmp.size(), base::data(tmp)); + BytesReadImpl(base::data(tmp), tmp.size(), base::File::FILE_OK); if (failed_) return; }
diff --git a/extensions/browser/content_verify_job.h b/extensions/browser/content_verify_job.h index b91e5e2f..258fe7d 100644 --- a/extensions/browser/content_verify_job.h +++ b/extensions/browser/content_verify_job.h
@@ -11,6 +11,7 @@ #include <string> #include "base/callback.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -73,7 +74,7 @@ // are read. Make sure to call DoneReading so that any final bytes that were // read that didn't align exactly on a block size boundary get their hash // checked as well. - void BytesRead(int count, const char* data); + void BytesRead(const char* data, int count, base::File::Error read_result); // Call once when finished adding bytes via BytesRead. void DoneReading(); @@ -104,7 +105,9 @@ void DidGetContentHashOnIO(scoped_refptr<const ContentHash> hash); // Same as BytesRead, but is run without acquiring lock. - void BytesReadImpl(int count, const char* data); + void BytesReadImpl(const char* data, + int count, + base::File::Error read_result); // Called each time we're done adding bytes for the current block, and are // ready to finish the hash operation for those bytes and make sure it @@ -118,6 +121,10 @@ // Called when our ContentHashReader has finished initializing. void OnHashesReady(std::unique_ptr<const ContentHashReader> hash_reader); + // True if BytesRead has seen some errors that can be ignored from content + // verification's perspective. + bool has_ignorable_read_error_ = false; + // Indicates whether the caller has told us they are done calling BytesRead. bool done_reading_;
diff --git a/extensions/browser/content_verify_job_unittest.cc b/extensions/browser/content_verify_job_unittest.cc index 8b10d8f..bbddc4c 100644 --- a/extensions/browser/content_verify_job_unittest.cc +++ b/extensions/browser/content_verify_job_unittest.cc
@@ -93,8 +93,8 @@ auto run_content_read_step = [](ContentVerifyJob* verify_job, std::string* resource_contents) { // Simulate serving |resource_contents| from |resource_path|. - verify_job->BytesRead(resource_contents->size(), - base::data(*resource_contents)); + verify_job->BytesRead(base::data(*resource_contents), + resource_contents->size(), base::File::FILE_OK); verify_job->DoneReading(); };
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc index e0761e1..aea24fb 100644 --- a/extensions/browser/extension_protocols.cc +++ b/extensions/browser/extension_protocols.cc
@@ -266,7 +266,8 @@ // proceed; see crbug.com/703888. if (verify_job_.get()) { std::string tmp; - verify_job_->BytesRead(0, base::data(tmp)); + verify_job_->BytesRead(base::data(tmp), 0, + base::File::FILE_ERROR_FAILED); verify_job_->DoneReading(); } } @@ -292,7 +293,7 @@ if (result > 0) { bytes_read_ += result; if (verify_job_.get()) - verify_job_->BytesRead(result, buffer->data()); + verify_job_->BytesRead(buffer->data(), result, base::File::FILE_OK); } } @@ -715,15 +716,22 @@ if (read_result == base::File::FILE_OK) { UMA_HISTOGRAM_COUNTS_1M("ExtensionUrlRequest.OnReadCompleteResult", read_result); - base::AutoLock auto_lock(lock_); - bytes_read_ += num_bytes_read; - if (verify_job_.get()) - verify_job_->BytesRead(num_bytes_read, static_cast<const char*>(data)); } else { net::Error net_error = net::FileErrorToNetError(read_result); base::UmaHistogramSparse("ExtensionUrlRequest.OnReadCompleteError", net_error); } + { + base::AutoLock auto_lock(lock_); + bytes_read_ += num_bytes_read; + if (verify_job_) { + // Note: We still pass the data to |verify_job_|, even if there was a + // read error, because some errors are ignorable. See + // ContentVerifyJob::BytesRead() for more details. + verify_job_->BytesRead(static_cast<const char*>(data), num_bytes_read, + read_result); + } + } } void OnDoneReading() override {
diff --git a/extensions/browser/extension_user_script_loader.cc b/extensions/browser/extension_user_script_loader.cc index 9d9e9b1b..db2d224 100644 --- a/extensions/browser/extension_user_script_loader.cc +++ b/extensions/browser/extension_user_script_loader.cc
@@ -73,7 +73,8 @@ info.extension_id, info.extension_root, info.relative_path)); if (job.get()) { job->Start(verifier); - job->BytesRead(info.content.size(), info.content.data()); + job->BytesRead(info.content.data(), info.content.size(), + base::File::FILE_OK); job->DoneReading(); } }
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc index 3d621690..8eed49b 100644 --- a/google_apis/gaia/gaia_auth_fetcher.cc +++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -47,6 +47,11 @@ const size_t kMaxMessageSize = 1024 * 1024; // 1MB +constexpr char kBadAuthenticationError[] = "BadAuthentication"; +constexpr char kBadAuthenticationShortError[] = "badauth"; +constexpr char kServiceUnavailableError[] = "ServiceUnavailable"; +constexpr char kServiceUnavailableShortError[] = "ire"; + std::unique_ptr<const GaiaAuthConsumer::ClientOAuthResult> ExtractOAuth2TokenPairResponse(const std::string& data) { std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data); @@ -178,13 +183,8 @@ // static const char GaiaAuthFetcher::kAccountDisabledError[] = "AccountDisabled"; // static -const char GaiaAuthFetcher::kBadAuthenticationError[] = "BadAuthentication"; -// static const char GaiaAuthFetcher::kCaptchaError[] = "CaptchaRequired"; // static -const char GaiaAuthFetcher::kServiceUnavailableError[] = - "ServiceUnavailable"; -// static const char GaiaAuthFetcher::kErrorParam[] = "Error"; // static const char GaiaAuthFetcher::kErrorUrlParam[] = "Url"; @@ -919,13 +919,15 @@ return GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DELETED); if (error == kAccountDisabledError) return GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED); - if (error == kBadAuthenticationError) { + if (error == kBadAuthenticationShortError || + error == kBadAuthenticationError) { return GoogleServiceAuthError( GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( GoogleServiceAuthError::InvalidGaiaCredentialsReason:: CREDENTIALS_REJECTED_BY_SERVER)); } - if (error == kServiceUnavailableError) { + if (error == kServiceUnavailableShortError || + error == kServiceUnavailableError) { return GoogleServiceAuthError( GoogleServiceAuthError::SERVICE_UNAVAILABLE); }
diff --git a/google_apis/gaia/gaia_auth_fetcher.h b/google_apis/gaia/gaia_auth_fetcher.h index ae48d42d..bcab70e8 100644 --- a/google_apis/gaia/gaia_auth_fetcher.h +++ b/google_apis/gaia/gaia_auth_fetcher.h
@@ -249,12 +249,8 @@ static const char kAccountDeletedErrorCode[]; static const char kAccountDisabledError[]; static const char kAccountDisabledErrorCode[]; - static const char kBadAuthenticationError[]; - static const char kBadAuthenticationErrorCode[]; static const char kCaptchaError[]; static const char kCaptchaErrorCode[]; - static const char kServiceUnavailableError[]; - static const char kServiceUnavailableErrorCode[]; static const char kErrorParam[]; static const char kErrorUrlParam[]; static const char kCaptchaUrlParam[]; @@ -404,8 +400,10 @@ FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, AccountDeletedError); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, AccountDisabledError); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, BadAuthenticationError); + FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, BadAuthenticationShortError); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, IncomprehensibleError); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ServiceUnavailableError); + FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ServiceUnavailableShortError); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, CheckNormalErrorCode); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, CheckTwoFactorResponse); FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, LoginNetFailure);
diff --git a/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/google_apis/gaia/gaia_auth_fetcher_unittest.cc index 248392f..8f9c4721 100644 --- a/google_apis/gaia/gaia_auth_fetcher_unittest.cc +++ b/google_apis/gaia/gaia_auth_fetcher_unittest.cc
@@ -299,6 +299,13 @@ EXPECT_EQ(error.state(), GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); } +TEST_F(GaiaAuthFetcherTest, BadAuthenticationShortError) { + std::string data = "Error=badauth\n"; + GoogleServiceAuthError error = + GaiaAuthFetcher::GenerateAuthError(data, net::OK); + EXPECT_EQ(error.state(), GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); +} + TEST_F(GaiaAuthFetcherTest, IncomprehensibleError) { std::string data = "Error=Gobbledygook\n"; GoogleServiceAuthError error = @@ -313,6 +320,13 @@ EXPECT_EQ(error.state(), GoogleServiceAuthError::SERVICE_UNAVAILABLE); } +TEST_F(GaiaAuthFetcherTest, ServiceUnavailableShortError) { + std::string data = "Error=ire\n"; + GoogleServiceAuthError error = + GaiaAuthFetcher::GenerateAuthError(data, net::OK); + EXPECT_EQ(error.state(), GoogleServiceAuthError::SERVICE_UNAVAILABLE); +} + TEST_F(GaiaAuthFetcherTest, StartAuthCodeForOAuth2TokenExchange_Success) { MockGaiaConsumer consumer; EXPECT_CALL(consumer, OnClientOAuthCode("test-code")).Times(0);
diff --git a/mojo/public/cpp/system/file_data_pipe_producer.cc b/mojo/public/cpp/system/file_data_pipe_producer.cc index f207e0d..3c928db 100644 --- a/mojo/public/cpp/system/file_data_pipe_producer.cc +++ b/mojo/public/cpp/system/file_data_pipe_producer.cc
@@ -206,6 +206,8 @@ void Finish(MojoResult result) { if (observer_) { + if (result != MOJO_RESULT_OK) + observer_->OnBytesRead(nullptr, 0u, base::File::FILE_ERROR_ABORT); observer_->OnDoneReading(); observer_ = nullptr; }
diff --git a/mojo/public/cpp/system/tests/file_data_pipe_producer_unittest.cc b/mojo/public/cpp/system/tests/file_data_pipe_producer_unittest.cc index 7c9b6e5..f14e397 100644 --- a/mojo/public/cpp/system/tests/file_data_pipe_producer_unittest.cc +++ b/mojo/public/cpp/system/tests/file_data_pipe_producer_unittest.cc
@@ -247,7 +247,7 @@ loop.Run(); EXPECT_EQ(0UL, reader.data().size()); - EXPECT_EQ(0, observer_data.num_read_errors); + EXPECT_EQ(1, observer_data.num_read_errors); EXPECT_EQ(0UL, observer_data.bytes_read); EXPECT_EQ(1, observer_data.done_called); }
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc index 6f6cb46..f9a83fb6 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -336,6 +336,9 @@ } bool HTMLFormControlElement::IsKeyboardFocusable() const { + if (RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) + return HTMLElement::IsKeyboardFocusable(); + // Skip tabIndex check in a parent class. return IsFocusable(); }
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc index 29233e1f..b691b8bd 100644 --- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc +++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager.cc
@@ -349,9 +349,24 @@ DCHECK(scrollable); + DCHECK(root_frame_->GetDocument()); + DCHECK(root_frame_->GetDocument()->View()); + // TODO(bokan): Overly-defensive since we'll be merging, remove from ToT. + if (!root_frame_->GetDocument() || !root_frame_->GetDocument()->View()) + return; + IntPoint location_in_root_frame{e.PositionInRootFrame().x, e.PositionInRootFrame().y}; + // Make sure we unlock all movement if the cursor is outside our bounds. This + // can happen when the cursor is enabled/disabled (e.g. position: -1,-1). + IntRect root_frame_rect = root_frame_->GetDocument()->View()->FrameRect(); + if (!root_frame_rect.Contains(location_in_root_frame)) { + ResetCurrentScrollable(); + LockCursor(false, false, false, false); + return; + } + IntSize scrollable_clip_size_in_root_frame = ScrollableAreaClipSizeInRootFrame(*scrollable); IntPoint location_in_scrollable = @@ -376,6 +391,8 @@ HitTestResult hit_test_result = HitTest(root_frame_->GetDocument()->GetLayoutView(), location); Node* node = hit_test_result.InnerNode(); + if (!node) + return; // Click on input boxes or media node should hide the cursor. if (HasEditableStyle(*node) || node->IsMediaElement()) {
diff --git a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc index 2bfadb9..d186267 100644 --- a/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc +++ b/third_party/blink/renderer/core/input/fallback_cursor_event_manager_test.cc
@@ -148,6 +148,38 @@ ExpectLock(false, false, false, false); } +TEST_F(FallbackCursorEventManagerTest, ResetOnOutOfFrame) { + SetBodyInnerHTML(R"HTML( + <style> + html, body { + margin: 0px; + } + .big { + height: 10000px; + width: 10000px; + } + </style> + <div class='big'></div> + )HTML"); + TurnOnFallbackCursorMode(); + + // Move below the scroll down line. + MouseMove(100, 500); + ExpectLock(false, false, false, true); + + // Ensure an invalid or out-of-bounds mouse move will reset the lock. + MouseMove(-1, -1); + ExpectLock(false, false, false, false); + + // Ensure an invalid or out-of-bounds mouse move will reset the lock. + MouseMove(790, 590); + ExpectLock(false, true, false, true); + + // Ensure an invalid or out-of-bounds mouse move will reset the lock. + MouseMove(800, 600); + ExpectLock(false, false, false, false); +} + TEST_F(FallbackCursorEventManagerTest, MouseMoveCursorLockOnRootFrame) { SetBodyInnerHTML(R"HTML( <style>
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc index 10a49c1..d0d9c8a 100644 --- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc +++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -487,10 +487,11 @@ // TODO(crbug.com/927066): We calculate an incorrect intrinsic logical height // when percentages are involved, so for now don't apply min-height: auto - // in such cases. + // in such cases. (This is only a problem if the child has a definite height) const LayoutBlock* child_block = DynamicTo<LayoutBlock>(child); if (IsColumnFlow() && child_block && - child_block->HasPercentHeightDescendants()) + child_block->HasPercentHeightDescendants() && + child_block->HasDefiniteLogicalHeight()) return false; return !child.ShouldApplySizeContainment() &&
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 1b93b8db..fefd14dc 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -2819,7 +2819,9 @@ name_sources->back().native_source = kAXTextFromNativeHTMLLabel; } - LabelsNodeList* labels = html_element->labels(); + LabelsNodeList* labels = nullptr; + if (AXObjectCache().MayHaveHTMLLabel(*html_element)) + labels = html_element->labels(); if (labels && labels->length() > 0) { HeapVector<Member<Element>> label_elements; for (unsigned label_index = 0; label_index < labels->length();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index 086d54e..c69788a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1017,6 +1017,19 @@ relation_cache_->UpdateAriaOwns(owner, id_vector, owned_children); } +bool AXObjectCacheImpl::MayHaveHTMLLabel(const HTMLElement& elem) { + // Return false if this type of element will not accept a <label for> label. + if (!elem.IsLabelable()) + return false; + + // Return true if a <label for> pointed to this element at some point. + if (relation_cache_->MayHaveHTMLLabelViaForAttribute(elem)) + return true; + + // Return true if any amcestor is a label, as in <label><input></label>. + return Traversal<HTMLLabelElement>::FirstAncestor(elem); +} + void AXObjectCacheImpl::CheckedStateChanged(Node* node) { PostNotification(node, ax::mojom::Event::kCheckedStateChanged); } @@ -1388,7 +1401,8 @@ } void AXObjectCacheImpl::LabelChangedWithCleanLayout(Element* element) { - TextChangedWithCleanLayout(ToHTMLLabelElement(element)->control()); + // Will call back to TextChanged() when done updating relation cache. + relation_cache_->LabelChanged(element); } void AXObjectCacheImpl::InlineTextBoxesUpdated(
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h index 75901b0..39420ae 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -234,6 +234,8 @@ const Vector<String>& id_vector, HeapVector<Member<AXObject>>& owned_children); + bool MayHaveHTMLLabel(const HTMLElement& elem); + // Synchronously returns whether or not we currently have permission to // call AOM event listeners. bool CanCallAOMEventListeners() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc index 895d5e2..30f0d383 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -9,6 +9,8 @@ namespace blink { +using namespace html_names; + AXRelationCache::AXRelationCache(AXObjectCacheImpl* object_cache) : object_cache_(object_cache) {} @@ -167,6 +169,14 @@ validated_owned_child_axids); } +bool AXRelationCache::MayHaveHTMLLabelViaForAttribute( + const HTMLElement& labelable) { + const AtomicString& id = labelable.GetIdAttribute(); + if (id.IsEmpty()) + return false; + return all_previously_seen_label_target_ids_.Contains(id); +} + // Fill source_objects with AXObjects for relations pointing to target. void AXRelationCache::GetReverseRelated( Node* target, @@ -260,8 +270,12 @@ } void AXRelationCache::LabelChanged(Node* node) { - if (HTMLElement* control = ToHTMLLabelElement(node)->control()) - TextChanged(Get(control)); + const AtomicString& id = ToHTMLElement(node)->FastGetAttribute(kForAttr); + if (!id.IsEmpty()) { + all_previously_seen_label_target_ids_.insert(id); + if (HTMLElement* control = ToHTMLLabelElement(node)->control()) + TextChanged(Get(control)); + } } } // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h index 78d1ef0..86c778da 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h +++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.h
@@ -38,6 +38,9 @@ const Vector<String>& id_vector, HeapVector<Member<AXObject>>& owned_children); + // Return true if any label ever pointed to the element via the for attribute. + bool MayHaveHTMLLabelViaForAttribute(const HTMLElement&); + // Given an element in the DOM tree that was either just added or whose id // just changed, check to see if another object wants to be its parent due to // aria-owns. If so, update the tree by calling childrenChanged() on the @@ -54,6 +57,8 @@ void UpdateReverseRelations(const AXObject* relation_source, const Vector<String>& target_ids); + void LabelChanged(Node*); + private: // If any object is related to this object via <label for>, aria-owns, // aria-describedby or aria-labeledby, update the text for the related object. @@ -90,13 +95,19 @@ // and fire the appropriate change events. HashMap<String, HashSet<AXID>> id_attr_to_related_mapping_; + // HTML id attributes that at one time havehad a <label for> pointing to it. + // IDs are not necessarily removed from this set. It is not necessary to + // remove IDs as false positives are ok. Being able to determine that a + // labelable element has never had an associated label allows the accessible + // name calculation to be optimized. + HashSet<AtomicString> all_previously_seen_label_target_ids_; + // Helpers that call back into object cache AXObject* ObjectFromAXID(AXID) const; AXObject* GetOrCreate(Node*); AXObject* Get(Node*); void ChildrenChanged(AXObject*); void TextChanged(AXObject*); - void LabelChanged(Node*); DISALLOW_COPY_AND_ASSIGN(AXRelationCache); };
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 9764bb33..899bff9 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1471,6 +1471,7 @@ crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-008.xht [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-012.html [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-014.html [ Failure ] +crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-015.html [ Failure ] crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-005.xht [ Failure ] crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ] crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-order.html [ Failure ]
diff --git a/third_party/blink/web_tests/accessibility/name-calc-inputs.html b/third_party/blink/web_tests/accessibility/name-calc-inputs.html index 80b9ce3e..c251c069 100644 --- a/third_party/blink/web_tests/accessibility/name-calc-inputs.html +++ b/third_party/blink/web_tests/accessibility/name-calc-inputs.html
@@ -79,7 +79,9 @@ <div class="container"> <label>label-wrapping-text7 - <input id="text7" type="text" title="text7-title"> + <span> <!-- Also testing ancestor label that is not direct parent --> + <input id="text7" type="text" title="text7-title"> + </span> </label> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-016.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-016.html new file mode 100644 index 0000000..a5dc8763 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-016.html
@@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title>CSS Flexbox: min-height: auto with flex items containing percentage-sized children</title> +<link rel="author" title="Google LLC" href="https://www.google.com/" /> +<link rel="help" href="https://drafts.csswg.org/css-flexbox/#min-size-auto" /> +<link rel="issue" href="https://bugs.chromium.org/p/chromium/issues/detail?id=981481" /> +<link rel="issue" href="https://bugs.chromium.org/p/chromium/issues/detail?id=984606" /> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht" /> + +<style> +.flexbox { + display: flex; + width: 100px; + flex-direction: column; +} +.item { + flex-basis: 0; + background: green; +} +.percentage { + height: 100%; +} +.fixed { + height: 100px; +} + +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + +<div class="flexbox"> + <div class="item"> + <div class="percentage"></div> + <div class="fixed"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-button-negative-tabindex.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-button-negative-tabindex.html new file mode 100644 index 0000000..bccdec77 --- /dev/null +++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-button-negative-tabindex.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<script src="../../../../../resources/testharness.js"></script> +<script src="../../../../../resources/testharnessreport.js"></script> +<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> +<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script> +<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script> +<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script> + +<style> + div { + width: 130px; + height: 130px; + border: 1px solid black; + } +</style> +<button tabindex="-1" id="unselectable">Unselectable</button> +<br> +<br> +<button id="selectable"">Selectable</button> + +<script> + const unselectable = document.getElementById("unselectable"); + const selectable = document.getElementById("selectable"); + + // This test checks that tabindex="-1" is effective on a button element, + // which overrides Element::IsKeyboardFocusable. + test(() => { + assert_true(!!window.internals); + + snav.triggerMove('Down'); + assert_equals(window.internals.interestedElement, + selectable, + "Expected interest to move to |selectable| button."); + + snav.triggerMove('Up'); + assert_equals(window.internals.interestedElement, + selectable, + "Expected interest to stay on |selectable| button."); + }, "Cannot navigate to button with tabindex'-1'"); +</script>
diff --git a/ui/display/display.cc b/ui/display/display.cc index f2153337b..71d6f3f 100644 --- a/ui/display/display.cc +++ b/ui/display/display.cc
@@ -215,12 +215,9 @@ bounds_(bounds), work_area_(bounds), device_scale_factor_(GetForcedDeviceScaleFactor()) { - gfx::ColorSpace color_space = HasForceDisplayColorProfile() - ? GetForcedDisplayColorProfile() - : gfx::ColorSpace::CreateSRGB(); - float sdr_white_level = - color_space.IsHDR() ? 200.f : gfx::ColorSpace::kDefaultSDRWhiteLevel; - SetColorSpaceAndDepth(color_space, sdr_white_level); + SetColorSpaceAndDepth(HasForceDisplayColorProfile() + ? GetForcedDisplayColorProfile() + : gfx::ColorSpace::CreateSRGB()); #if defined(USE_AURA) SetScaleAndBounds(device_scale_factor_, bounds); #endif