diff --git a/DEPS b/DEPS index 699c6c40..124107f 100644 --- a/DEPS +++ b/DEPS
@@ -133,11 +133,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': '7bc76b45a4b96be27c32572451ab00ca7edd1643', + 'skia_revision': '9b86955c65aab21ccc921116eb3cb38aa85ea417', # 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': '512e74e0bdb9d98c4d729551c294127fff4b0434', + 'v8_revision': '4894cb223bbf534d11c95d4c3319408b2e1c6ec3', # 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. @@ -145,11 +145,11 @@ # 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': '3dbe480b559e7919f303a15b4d4b0c788229c3ee', + 'angle_revision': 'f1e3609c24ce71b70a748da65573d0cf17cbab09', # 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': '9e4f690166c1bed061d45352073531aae5c412aa', + 'swiftshader_revision': 'a7a375565cdb8161657f696fc3e55a757edccafa', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # 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': 'ff395373a92527b8d1b0e0bd030587e94edf0935', + 'catapult_revision': 'e33ee3bd7c70f3145532a3e859ec2bf3ff7da89d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -1177,7 +1177,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '782cc0e470307ed1bdd562e884ca04125f891989', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'aa3f478c06dce2c2a699ef73957a99c851e98b33', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1348,7 +1348,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '46afbf9481fbcc939c998c898ca1031ce41cc6b1', + Var('webrtc_git') + '/src.git' + '@' + '2ebf5239782bf6b46d4aa812f34fa9f9e5a02be9', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1389,7 +1389,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9b48fcfc9759310a3a0011d6c3eeca8a41b99ecf', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@02f2aec134517824b0a6fe01d2ed476d729a3a8e', 'condition': 'checkout_src_internal', },
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 3a123c5..dcdc08b 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -48,6 +48,7 @@ "multi_user/multi_user_window_manager_impl.h", "new_window_controller.h", "public/cpp/arc_custom_tab.h", + "public/cpp/ash_prefs.h", "public/cpp/multi_user_window_manager.h", "public/cpp/multi_user_window_manager_delegate.h", "public/cpp/multi_user_window_manager_observer.h", @@ -162,6 +163,7 @@ "app_list/app_list_presenter_delegate_impl.cc", "app_list/app_list_presenter_delegate_impl.h", "ash_export.h", + "ash_prefs.cc", "ash_service.cc", "assistant/assistant_alarm_timer_controller.cc", "assistant/assistant_alarm_timer_controller.h", @@ -605,6 +607,8 @@ "sticky_keys/sticky_keys_overlay.cc", "sticky_keys/sticky_keys_overlay.h", "sticky_keys/sticky_keys_state.h", + "system/accessibility/accessibility_feature_disable_dialog.cc", + "system/accessibility/accessibility_feature_disable_dialog.h", "system/accessibility/accessibility_feature_pod_controller.cc", "system/accessibility/accessibility_feature_pod_controller.h", "system/accessibility/autoclick_menu_bubble_controller.cc", @@ -2114,6 +2118,8 @@ "mojo_test_interface_factory.h", "rotator/screen_rotation_animator_test_api.cc", "rotator/screen_rotation_animator_test_api.h", + "session/test_pref_service_provider.cc", + "session/test_pref_service_provider.h", "session/test_session_controller_client.cc", "session/test_session_controller_client.h", "shelf/overflow_bubble_view_test_api.cc",
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc index a61cc40..b877802 100644 --- a/ash/accessibility/accessibility_controller.cc +++ b/ash/accessibility/accessibility_controller.cc
@@ -317,41 +317,6 @@ // In production the prefs are owned by chrome. // TODO(jamescook): Move ownership to ash. - registry->RegisterForeignPref(prefs::kAccessibilityAutoclickEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityAutoclickDelayMs); - registry->RegisterForeignPref(prefs::kAccessibilityAutoclickEventType); - registry->RegisterForeignPref( - prefs::kAccessibilityAutoclickRevertToLeftClick); - registry->RegisterForeignPref( - prefs::kAccessibilityAutoclickStabilizePosition); - registry->RegisterForeignPref( - prefs::kAccessibilityAutoclickMovementThreshold); - registry->RegisterForeignPref(prefs::kAccessibilityAutoclickMenuPosition); - registry->RegisterForeignPref(prefs::kAccessibilityCaretHighlightEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityCursorHighlightEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityDictationEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityFocusHighlightEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityHighContrastEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityLargeCursorEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityLargeCursorDipSize); - registry->RegisterForeignPref(prefs::kAccessibilityMonoAudioEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityScreenMagnifierEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityScreenMagnifierScale); - registry->RegisterForeignPref(prefs::kAccessibilitySpokenFeedbackEnabled); - registry->RegisterForeignPref(prefs::kAccessibilitySelectToSpeakEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityStickyKeysEnabled); - registry->RegisterForeignPref(prefs::kAccessibilitySwitchAccessEnabled); - registry->RegisterForeignPref(prefs::kAccessibilityVirtualKeyboardEnabled); - registry->RegisterForeignPref( - prefs::kHighContrastAcceleratorDialogHasBeenAccepted); - registry->RegisterForeignPref( - prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted); - registry->RegisterForeignPref( - prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted); - registry->RegisterForeignPref( - prefs::kDictationAcceleratorDialogHasBeenAccepted); - registry->RegisterForeignPref( - prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted); } void AccessibilityController::SetHighContrastAcceleratorDialogAccepted() { @@ -940,7 +905,8 @@ NotifyAccessibilityStatusChanged(); - Shell::Get()->autoclick_controller()->SetEnabled(enabled); + Shell::Get()->autoclick_controller()->SetEnabled( + enabled, true /* show confirmation dialog */); } void AccessibilityController::UpdateAutoclickDelayFromPref() {
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc new file mode 100644 index 0000000..423de1cd --- /dev/null +++ b/ash/ash_prefs.cc
@@ -0,0 +1,61 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/public/cpp/ash_prefs.h" + +#include "ash/accessibility/accessibility_controller.h" +#include "ash/app_list/app_list_controller_impl.h" +#include "ash/assistant/assistant_controller.h" +#include "ash/kiosk_next/kiosk_next_shell_controller.h" +#include "ash/login/login_screen_controller.h" +#include "ash/magnifier/docked_magnifier_controller.h" +#include "ash/shelf/shelf_controller.h" +#include "ash/system/bluetooth/bluetooth_power_controller.h" +#include "ash/system/caps_lock_notification_controller.h" +#include "ash/system/message_center/message_center_controller.h" +#include "ash/system/network/vpn_list_view.h" +#include "ash/system/night_light/night_light_controller.h" +#include "ash/system/palette/palette_tray.h" +#include "ash/system/palette/palette_welcome_bubble.h" +#include "ash/system/power/power_prefs.h" +#include "ash/system/session/logout_button_tray.h" +#include "ash/touch/touch_devices_controller.h" + +namespace ash { + +namespace { + +// Registers prefs whose default values are same in user and signin prefs. +void RegisterProfilePrefs(PrefRegistrySimple* registry, bool for_test) { + AccessibilityController::RegisterProfilePrefs(registry, for_test); + AppListControllerImpl::RegisterProfilePrefs(registry); + AssistantController::RegisterProfilePrefs(registry); + BluetoothPowerController::RegisterProfilePrefs(registry); + CapsLockNotificationController::RegisterProfilePrefs(registry, for_test); + DockedMagnifierController::RegisterProfilePrefs(registry, for_test); + KioskNextShellController::RegisterProfilePrefs(registry, for_test); + LoginScreenController::RegisterProfilePrefs(registry, for_test); + LogoutButtonTray::RegisterProfilePrefs(registry); + MessageCenterController::RegisterProfilePrefs(registry); + NightLightController::RegisterProfilePrefs(registry); + PaletteTray::RegisterProfilePrefs(registry); + PaletteWelcomeBubble::RegisterProfilePrefs(registry); + ShelfController::RegisterProfilePrefs(registry); + TouchDevicesController::RegisterProfilePrefs(registry); + tray::VPNListView::RegisterProfilePrefs(registry); +} + +} // namespace + +void RegisterSigninProfilePrefs(PrefRegistrySimple* registry, bool for_test) { + RegisterProfilePrefs(registry, for_test); + PowerPrefs::RegisterSigninProfilePrefs(registry, for_test); +} + +void RegisterUserProfilePrefs(PrefRegistrySimple* registry, bool for_test) { + RegisterProfilePrefs(registry, for_test); + PowerPrefs::RegisterUserProfilePrefs(registry, for_test); +} + +} // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 4ce443ff..db66514 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -491,6 +491,12 @@ <message name="IDS_ASH_AUTOCLICK_OPTION_CHANGE_POSITION" desc="The tooltip text for a menu option for automatic clicks menu that results in the menu changing position on the screen. The menu will toggle through several pre-set positions."> Toggle menu position </message> + <message name="IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_TITLE" desc="The title for the modal dialog shown when the user disables automatic clicks, to confirm they meant to disable the feature."> + Disable automatic clicks? + </message> + <message name="IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_BODY" desc="The message in the modal dialog shown when the user disables automatic clicks, to confirm they meant to disable the feature"> + Are you sure you want to disable automatic clicks? + </message> <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD" desc="The label used in the accessibility menu of the system tray to toggle on/off the onscreen keyboard."> On-screen keyboard </message> @@ -1378,6 +1384,10 @@ <message name="IDS_ASH_CONTINUE_BUTTON" desc="The text for continue button on accessibility confirmation dialogs."> Continue </message> + <message name="IDS_ASH_DISABLE_BUTTON" desc="The text for the button on a dialog to confirm disabling a feature."> + Disable + </message> + <message name="IDS_ASH_IME_MENU_ACCESSIBLE_NAME" desc="The accessible text for opt-in IME menu icon in status tray."> IME menu button
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc index d98e8cfe..f6c7662 100644 --- a/ash/autoclick/autoclick_controller.cc +++ b/ash/autoclick/autoclick_controller.cc
@@ -10,6 +10,8 @@ #include "ash/public/cpp/shell_window_ids.h" #include "ash/shelf/shelf.h" #include "ash/shell.h" +#include "ash/strings/grit/ash_strings.h" +#include "ash/system/accessibility/accessibility_feature_disable_dialog.h" #include "ash/system/accessibility/autoclick_menu_bubble_controller.h" #include "ash/wm/root_window_finder.h" #include "base/bind.h" @@ -90,12 +92,12 @@ tap_down_target_->AddObserver(this); } -void AutoclickController::SetEnabled(bool enabled) { +void AutoclickController::SetEnabled(bool enabled, + bool show_confirmation_dialog) { if (enabled_ == enabled) return; - enabled_ = enabled; - if (enabled_) { + if (enabled) { Shell::Get()->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem); // Only create the bubble controller when needed. Most users will not enable @@ -107,9 +109,36 @@ std::make_unique<AutoclickMenuBubbleController>(); menu_bubble_controller_->ShowBubble(event_type_, menu_position_); } + enabled_ = enabled; } else { - Shell::Get()->RemovePreTargetHandler(this); - menu_bubble_controller_ = nullptr; + if (show_confirmation_dialog) { + // If a dialog exists already, no need to show it again. + if (disable_dialog_) + return; + // Show a confirmation dialog before disabling autoclick. + auto* dialog = new AccessibilityFeatureDisableDialog( + IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_TITLE, + IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_BODY, + // Callback for if the user accepts the dialog + base::BindOnce([]() { + // If they accept, actually disable autoclick. + Shell::Get()->autoclick_controller()->SetEnabled( + false, false /* do not show the dialog */); + }), + // Callback for if the user cancels the dialog - marks the + // feature as enabled again in prefs. + base::BindOnce([]() { + AccessibilityController* controller = + Shell::Get()->accessibility_controller(); + // If they cancel, ensure autoclick is enabled. + controller->SetAutoclickEnabled(true); + })); + disable_dialog_ = dialog->GetWeakPtr(); + } else { + Shell::Get()->RemovePreTargetHandler(this); + menu_bubble_controller_ = nullptr; + enabled_ = enabled; + } } CancelAutoclickAction();
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h index 998507ed..6df031d 100644 --- a/ash/autoclick/autoclick_controller.h +++ b/ash/autoclick/autoclick_controller.h
@@ -24,6 +24,7 @@ namespace ash { +class AccessibilityFeatureDisableDialog; class AutoclickDragEventRewriter; class AutoclickRingHandler; class AutoclickMenuBubbleController; @@ -38,8 +39,10 @@ AutoclickController(); ~AutoclickController() override; - // Set whether autoclicking is enabled. - void SetEnabled(bool enabled); + // Set whether autoclicking is enabled. If |show_confirmation_dialog|, a + // confirmation dialog will be shown when disabling autoclick to ensure + // the user doesn't accidentally lock themselves out of the feature. + void SetEnabled(bool enabled, bool show_confirmation_dialog); // Returns true if autoclicking is enabled. bool IsEnabled() const; @@ -85,6 +88,9 @@ AutoclickMenuBubbleController* GetMenuBubbleControllerForTesting() { return menu_bubble_controller_.get(); } + AccessibilityFeatureDisableDialog* GetDisableDialogForTesting() { + return disable_dialog_.get(); + } private: void SetTapDownTarget(aura::Window* target); @@ -157,6 +163,9 @@ std::unique_ptr<AutoclickDragEventRewriter> drag_event_rewriter_; std::unique_ptr<AutoclickMenuBubbleController> menu_bubble_controller_; + // Holds a weak pointer to the dialog shown when autoclick is being disabled. + base::WeakPtr<AccessibilityFeatureDisableDialog> disable_dialog_; + DISALLOW_COPY_AND_ASSIGN(AutoclickController); };
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc index 781aa84..fb7521a 100644 --- a/ash/autoclick/autoclick_unittest.cc +++ b/ash/autoclick/autoclick_unittest.cc
@@ -6,6 +6,7 @@ #include "ash/autoclick/autoclick_controller.h" #include "ash/shelf/shelf.h" #include "ash/shell.h" +#include "ash/system/accessibility/accessibility_feature_disable_dialog.h" #include "ash/system/accessibility/autoclick_menu_bubble_controller.h" #include "ash/system/accessibility/autoclick_menu_view.h" #include "ash/test/ash_test_base.h" @@ -25,6 +26,7 @@ #include "ui/events/event_utils.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/test/event_generator.h" +#include "ui/views/window/dialog_client_view.h" namespace ash { @@ -176,7 +178,7 @@ // Enable autoclick, and we should see a mouse pressed and // a mouse released event, simulating a click. - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetEventGenerator()->MoveMouseTo(0, 0); EXPECT_TRUE(GetAutoclickController()->IsEnabled()); events = WaitForMouseEvents(); @@ -198,7 +200,7 @@ EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[1].flags()); // Disable autoclick, and we should see the original behaviour. - GetAutoclickController()->SetEnabled(false); + GetAutoclickController()->SetEnabled(false, false /* do not show dialog */); EXPECT_FALSE(GetAutoclickController()->IsEnabled()); events = WaitForMouseEvents(); EXPECT_EQ(0u, events.size()); @@ -206,7 +208,7 @@ TEST_F(AutoclickTest, MouseMovement) { std::vector<ui::MouseEvent> events; - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); gfx::Point p1(0, 0); gfx::Point p2(20, 20); @@ -255,7 +257,8 @@ for (auto* root_window : root_windows) { gfx::Point center = root_window->GetBoundsInScreen().CenterPoint(); - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, + false /* do not show dialog */); GetEventGenerator()->MoveMouseTo(center); ClearMouseEvents(); EXPECT_EQ(2u, WaitForMouseEvents().size()); @@ -314,7 +317,7 @@ } TEST_F(AutoclickTest, MovementWithinThresholdWhileTimerRunning) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->SetMovementThreshold(20); int animation_delay = 5; int full_delay = UpdateAnimationDelayAndGetFullDelay(animation_delay); @@ -362,7 +365,7 @@ } TEST_F(AutoclickTest, SingleKeyModifier) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); MoveMouseWithFlagsTo(20, 20, ui::EF_SHIFT_DOWN); std::vector<ui::MouseEvent> events = WaitForMouseEvents(); EXPECT_EQ(2u, events.size()); @@ -371,7 +374,7 @@ } TEST_F(AutoclickTest, MultipleKeyModifiers) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); ui::EventFlags modifier_flags = static_cast<ui::EventFlags>( ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN); MoveMouseWithFlagsTo(30, 30, modifier_flags); @@ -382,7 +385,7 @@ } TEST_F(AutoclickTest, KeyModifiersReleased) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); ui::EventFlags modifier_flags = static_cast<ui::EventFlags>( ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN); @@ -403,7 +406,7 @@ } TEST_F(AutoclickTest, UserInputCancelsAutoclick) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); std::vector<ui::MouseEvent> events; // Pressing a normal key should cancel the autoclick. @@ -459,7 +462,7 @@ } TEST_F(AutoclickTest, SynthesizedMouseMovesIgnored) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); std::vector<ui::MouseEvent> events; GetEventGenerator()->MoveMouseTo(100, 100); events = WaitForMouseEvents(); @@ -478,7 +481,7 @@ } TEST_F(AutoclickTest, AutoclickChangeEventTypes) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->set_revert_to_left_click(false); GetAutoclickController()->SetAutoclickEventType( mojom::AutoclickEventType::kRightClick); @@ -542,7 +545,7 @@ } TEST_F(AutoclickTest, AutoclickDragAndDropEvents) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->set_revert_to_left_click(false); GetAutoclickController()->SetAutoclickEventType( mojom::AutoclickEventType::kDragAndDrop); @@ -574,7 +577,7 @@ } TEST_F(AutoclickTest, AutoclickRevertsToLeftClick) { - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); GetAutoclickController()->set_revert_to_left_click(true); GetAutoclickController()->SetAutoclickEventType( mojom::AutoclickEventType::kRightClick); @@ -635,7 +638,7 @@ TEST_F(AutoclickTest, WaitsToDrawAnimationAfterDwellBegins) { int animation_delay = 5; int full_delay = UpdateAnimationDelayAndGetFullDelay(animation_delay); - GetAutoclickController()->SetEnabled(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); std::vector<ui::MouseEvent> events; // Start a dwell at (210, 210). @@ -788,9 +791,6 @@ EXPECT_EQ(mojom::AutoclickEventType::kLeftClick, accessibility_controller->GetAutoclickEventType()); } - - // Reset state. - accessibility_controller->SetAutoclickEnabled(false); } TEST_F(AutoclickTest, @@ -834,14 +834,10 @@ FastForwardBy(full_delay); events = GetMouseEvents(); ASSERT_EQ(0u, events.size()); - - // Reset state. - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); } // The autoclick tray shouldn't stop the shelf from auto-hiding. TEST_F(AutoclickTest, ShelfAutohidesWithAutoclickBubble) { - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); Shelf* shelf = GetPrimaryShelf(); // Create a visible window so auto-hide behavior is enforced. @@ -860,9 +856,6 @@ ASSERT_TRUE(menu); EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); - - // Reset state. - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); } TEST_F(AutoclickTest, BubbleMovesWithShelfPositionChange) { @@ -912,8 +905,43 @@ shelf->GetIdealBounds().width()); // Reset state. - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM); } +TEST_F(AutoclickTest, ConfirmationDialogShownWhenDisablingFeature) { + // Enable and disable with the AccessibilityController to get real use-case + // of the dialog. + + // No dialog shown at start-up. + EXPECT_FALSE(GetAutoclickController()->GetDisableDialogForTesting()); + + // No dialog shown when enabling the feature. + Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true); + EXPECT_FALSE(GetAutoclickController()->GetDisableDialogForTesting()); + + // A dialog should be shown when disabling the feature. + Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); + AccessibilityFeatureDisableDialog* dialog = + GetAutoclickController()->GetDisableDialogForTesting(); + EXPECT_TRUE(dialog); + + // Canceling the dialog will cause the feature to continue to be enabled. + dialog->GetDialogClientView()->CancelWindow(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(GetAutoclickController()->GetDisableDialogForTesting()); + EXPECT_TRUE(Shell::Get()->accessibility_controller()->autoclick_enabled()); + EXPECT_TRUE(GetAutoclickController()->IsEnabled()); + + // Try to disable it again, and this time accept the dialog to actually + // disable the feature. + Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); + dialog = GetAutoclickController()->GetDisableDialogForTesting(); + EXPECT_TRUE(dialog); + dialog->GetDialogClientView()->AcceptWindow(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(GetAutoclickController()->GetDisableDialogForTesting()); + EXPECT_FALSE(Shell::Get()->accessibility_controller()->autoclick_enabled()); + EXPECT_FALSE(GetAutoclickController()->IsEnabled()); +} + } // namespace ash
diff --git a/ash/kiosk_next/kiosk_next_shell_controller.cc b/ash/kiosk_next/kiosk_next_shell_controller.cc index 1c226a03..2f74330e 100644 --- a/ash/kiosk_next/kiosk_next_shell_controller.cc +++ b/ash/kiosk_next/kiosk_next_shell_controller.cc
@@ -33,9 +33,6 @@ PrefRegistry::PUBLIC); return; } - // The registration has been moved to - // chromeos::Preferences::RegisterProfilePrefs to avoid race conditions. - registry->RegisterForeignPref(prefs::kKioskNextShellEnabled); } void KioskNextShellController::BindRequest(
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc index 251acab..f0d33b57 100644 --- a/ash/login/login_screen_controller.cc +++ b/ash/login/login_screen_controller.cc
@@ -77,9 +77,6 @@ registry->RegisterStringPref(prefs::kQuickUnlockPinSalt, ""); return; } - - // Pref is owned by chrome and flagged as PUBLIC. - registry->RegisterForeignPref(prefs::kQuickUnlockPinSalt); } void LoginScreenController::BindRequest(mojom::LoginScreenRequest request) { @@ -488,16 +485,6 @@ } } -void LoginScreenController::SetKioskApps( - std::vector<mojom::KioskAppInfoPtr> kiosk_apps, - SetKioskAppsCallback callback) { - Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow()) - ->shelf_widget() - ->login_shelf_view() - ->SetKioskApps(std::move(kiosk_apps)); - std::move(callback).Run(true); -} - void LoginScreenController::ShowKioskAppError(const std::string& message) { ToastData toast_data( "KioskAppError", base::UTF8ToUTF16(message), -1 /*duration_ms*/, @@ -555,6 +542,15 @@ } } +void LoginScreenController::SetKioskApps( + const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& launch_app) { + Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow()) + ->shelf_widget() + ->login_shelf_view() + ->SetKioskApps(kiosk_apps, launch_app); +} + void LoginScreenController::SetAddUserButtonEnabled(bool enable) { Shelf::ForWindow(Shell::Get()->GetPrimaryRootWindow()) ->shelf_widget() @@ -569,14 +565,6 @@ ->SetShutdownButtonEnabled(enable); } -void LoginScreenController::LaunchKioskApp(const std::string& app_id) { - login_screen_client_->LaunchKioskApp(app_id); -} - -void LoginScreenController::LaunchArcKioskApp(const AccountId& account_id) { - login_screen_client_->LaunchArcKioskApp(account_id); -} - void LoginScreenController::ShowResetScreen() { login_screen_client_->ShowResetScreen(); } @@ -592,6 +580,8 @@ } void LoginScreenController::NotifyUserActivity() { + if (!login_screen_client_) + return; login_screen_client_->OnUserActivity(); }
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h index bce52a0f..ef262ba 100644 --- a/ash/login/login_screen_controller.h +++ b/ash/login/login_screen_controller.h
@@ -9,8 +9,8 @@ #include "ash/ash_export.h" #include "ash/login/login_screen_controller_observer.h" +#include "ash/public/cpp/kiosk_app_menu.h" #include "ash/public/cpp/system_tray_focus_observer.h" -#include "ash/public/interfaces/kiosk_app_info.mojom.h" #include "ash/public/interfaces/login_screen.mojom.h" #include "base/macros.h" #include "base/observer_list.h" @@ -30,6 +30,7 @@ // This could send requests to LoginScreenClient and also handle requests from // LoginScreenClient through mojo. class ASH_EXPORT LoginScreenController : public mojom::LoginScreen, + public KioskAppMenu, public SystemTrayFocusObserver { public: // The current authentication stage. Used to get more verbose logging. @@ -95,8 +96,6 @@ void RequestPublicSessionKeyboardLayouts(const AccountId& account_id, const std::string& locale); void ShowFeedback(); - void LaunchKioskApp(const std::string& app_id); - void LaunchArcKioskApp(const AccountId& account_id); void ShowResetScreen(); void ShowAccountAccessHelpApp(); void FocusOobeDialog(); @@ -160,8 +159,6 @@ std::vector<mojom::InputMethodItemPtr> keyboard_layouts) override; void SetPublicSessionShowFullManagementDisclosure( bool is_full_management_disclosure_needed) override; - void SetKioskApps(std::vector<mojom::KioskAppInfoPtr> kiosk_apps, - SetKioskAppsCallback callback) override; void ShowKioskAppError(const std::string& message) override; void NotifyOobeDialogState(mojom::OobeDialogState state) override; void SetAddUserButtonEnabled(bool enable) override; @@ -172,6 +169,12 @@ void SetShowParentAccessDialog(bool show) override; void FocusLoginShelf(bool reverse) override; + // KioskAppMenu: + void SetKioskApps( + const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& launch_app) + override; + // Flushes the mojo pipes - to be used in tests. void FlushForTesting();
diff --git a/ash/login/mock_login_screen_client.h b/ash/login/mock_login_screen_client.h index 4fe8f30..a88525f 100644 --- a/ash/login/mock_login_screen_client.h +++ b/ash/login/mock_login_screen_client.h
@@ -5,7 +5,6 @@ #ifndef ASH_LOGIN_MOCK_LOGIN_SCREEN_CLIENT_H_ #define ASH_LOGIN_MOCK_LOGIN_SCREEN_CLIENT_H_ -#include "ash/public/interfaces/kiosk_app_info.mojom.h" #include "ash/public/interfaces/login_screen.mojom.h" #include "components/password_manager/core/browser/hash_password_manager.h" #include "mojo/public/cpp/bindings/binding_set.h" @@ -102,8 +101,6 @@ MOCK_METHOD2(RequestPublicSessionKeyboardLayouts, void(const AccountId& account_id, const std::string& locale)); MOCK_METHOD0(ShowFeedback, void()); - MOCK_METHOD1(LaunchKioskApp, void(const std::string& app_id)); - MOCK_METHOD1(LaunchArcKioskApp, void(const AccountId& account_id)); MOCK_METHOD0(ShowResetScreen, void()); MOCK_METHOD0(ShowAccountAccessHelpApp, void()); MOCK_METHOD0(FocusOobeDialog, void());
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc index b2c9534c..6d56df3 100644 --- a/ash/login/ui/lock_debug_view.cc +++ b/ash/login/ui/lock_debug_view.cc
@@ -18,7 +18,7 @@ #include "ash/login/ui/login_detachable_base_model.h" #include "ash/login/ui/non_accessible_view.h" #include "ash/login/ui/views_utils.h" -#include "ash/public/interfaces/kiosk_app_info.mojom.h" +#include "ash/public/cpp/kiosk_app_menu.h" #include "ash/shelf/login_shelf_view.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_widget.h" @@ -414,19 +414,18 @@ } void AddKioskApp(ShelfWidget* shelf_widget) { - mojom::KioskAppInfoPtr app_info = mojom::KioskAppInfo::New(); - app_info->identifier = mojom::KioskAppIdentifier::New(); - app_info->identifier->set_app_id(kDebugKioskAppId); - app_info->name = base::UTF8ToUTF16(kDebugKioskAppName); - kiosk_apps_.push_back(std::move(app_info)); - shelf_widget->login_shelf_view()->SetKioskApps(mojo::Clone(kiosk_apps_)); + KioskAppMenuEntry menu_item; + menu_item.app_id = kDebugKioskAppId; + menu_item.name = base::UTF8ToUTF16(kDebugKioskAppName); + kiosk_apps_.push_back(std::move(menu_item)); + shelf_widget->login_shelf_view()->SetKioskApps(kiosk_apps_, {}); } void RemoveKioskApp(ShelfWidget* shelf_widget) { if (kiosk_apps_.empty()) return; kiosk_apps_.pop_back(); - shelf_widget->login_shelf_view()->SetKioskApps(mojo::Clone(kiosk_apps_)); + shelf_widget->login_shelf_view()->SetKioskApps(kiosk_apps_, {}); } void AddSystemInfo(const std::string& os_version, @@ -522,7 +521,7 @@ mojom::TrayActionState lock_screen_note_state_; // List of kiosk apps loaded. - std::vector<mojom::KioskAppInfoPtr> kiosk_apps_; + std::vector<KioskAppMenuEntry> kiosk_apps_; // Called when a new user list has been received. base::RepeatingClosure on_users_received_; @@ -1126,7 +1125,8 @@ views::View* container) { // Creates a button with |text| that cannot be focused. auto* button = views::MdTextButton::CreateSecondaryUiButton( - this, base::ASCIIToUTF16(text)); + this, base::ASCIIToUTF16(text)) + .release(); button->SetID(id); button->SetFocusBehavior(views::View::FocusBehavior::NEVER); container->AddChildView(login_views_utils::WrapViewForPreferredSize(button));
diff --git a/ash/magnifier/docked_magnifier_controller.cc b/ash/magnifier/docked_magnifier_controller.cc index bf1a064..caa5d65 100644 --- a/ash/magnifier/docked_magnifier_controller.cc +++ b/ash/magnifier/docked_magnifier_controller.cc
@@ -148,12 +148,11 @@ bool for_test) { if (for_test) { // In tests there is no remote pref service. Make ash own the prefs. + // TODO(xiyuan): move ownership to ash. registry->RegisterBooleanPref(prefs::kDockedMagnifierEnabled, false, PrefRegistry::PUBLIC); - } else { - // TODO(warx): move ownership to ash. - registry->RegisterForeignPref(prefs::kDockedMagnifierEnabled); } + registry->RegisterDoublePref(prefs::kDockedMagnifierScale, kDefaultMagnifierScale, PrefRegistry::PUBLIC); }
diff --git a/ash/policy/policy_recommendation_restorer_unittest.cc b/ash/policy/policy_recommendation_restorer_unittest.cc index 000d8f43..7fb4360 100644 --- a/ash/policy/policy_recommendation_restorer_unittest.cc +++ b/ash/policy/policy_recommendation_restorer_unittest.cc
@@ -5,6 +5,7 @@ #include "ash/policy/policy_recommendation_restorer.h" #include "ash/public/cpp/ash_pref_names.h" +#include "ash/public/cpp/ash_prefs.h" #include "ash/session/session_controller_impl.h" #include "ash/session/test_session_controller_client.h" #include "ash/shell.h" @@ -38,17 +39,16 @@ // Register sigin prefs but not connected to pref service yet. This allows // us set pref values before ash connects to pref service for testing. - Shell::RegisterSigninProfilePrefs(prefs_->registry(), true /* for_test */); + RegisterSigninProfilePrefs(prefs_->registry(), true /* for_test */); restorer_ = Shell::Get()->policy_recommendation_restorer(); } void ConnectToSigninPrefService() { - SessionControllerImpl* session_controller = - Shell::Get()->session_controller(); - session_controller->SetSigninScreenPrefServiceForTest( + GetSessionControllerClient()->SetSigninScreenPrefService( base::WrapUnique(prefs_)); - ASSERT_EQ(session_controller->GetSigninScreenPrefService(), prefs_); + ASSERT_EQ(Shell::Get()->session_controller()->GetSigninScreenPrefService(), + prefs_); // Manually trigger a user activity, so that the delay is not skipped due to // no user input since a pref is started observing recommended value. See // PolicyRecommendationRestorer::Restore() for the information.
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn index 56e71f1..24c3ab2 100644 --- a/ash/public/cpp/BUILD.gn +++ b/ash/public/cpp/BUILD.gn
@@ -79,6 +79,8 @@ "immersive/immersive_revealed_lock.cc", "immersive/immersive_revealed_lock.h", "keyboard_shortcut_viewer.h", + "kiosk_app_menu.cc", + "kiosk_app_menu.h", "lock_screen_widget_factory.cc", "lock_screen_widget_factory.h", "login_constants.h",
diff --git a/ash/public/cpp/ash_prefs.h b/ash/public/cpp/ash_prefs.h new file mode 100644 index 0000000..68df2815 --- /dev/null +++ b/ash/public/cpp/ash_prefs.h
@@ -0,0 +1,25 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_PUBLIC_CPP_ASH_PREFS_H_ +#define ASH_PUBLIC_CPP_ASH_PREFS_H_ + +#include "ash/ash_export.h" + +class PrefRegistrySimple; + +namespace ash { + +// Register ash related sign-in/user profile prefs to |registry|. When +// |for_test| is true this registers foreign user profile prefs (e.g. chrome +// prefs) as if they are owned by ash. This allows test code to read the pref +// values. +ASH_EXPORT void RegisterSigninProfilePrefs(PrefRegistrySimple* registry, + bool for_test = false); +ASH_EXPORT void RegisterUserProfilePrefs(PrefRegistrySimple* registry, + bool for_test = false); + +} // namespace ash + +#endif // ASH_PUBLIC_CPP_ASH_PREFS_H_
diff --git a/ash/public/cpp/ash_view_ids.h b/ash/public/cpp/ash_view_ids.h index 089045f..f1e83c0 100644 --- a/ash/public/cpp/ash_view_ids.h +++ b/ash/public/cpp/ash_view_ids.h
@@ -13,10 +13,10 @@ // Ash IDs start above the range used in Chrome (c/b/ui/view_ids.h). VIEW_ID_ASH_START = 10000, - // Row for auto click feature in accessibility detailed view. - VIEW_ID_ACCESSIBILITY_AUTOCLICK, - // Icon that indicates auto click is enabled. - VIEW_ID_ACCESSIBILITY_AUTOCLICK_ENABLED, + // Row for the virtual keyboard feature in accessibility detailed view. + VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD, + // Icon that indicates the virtual keyboard is enabled. + VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED, // Accessibility feature pod button in main view. VIEW_ID_ACCESSIBILITY_TRAY_ITEM, VIEW_ID_BLUETOOTH_DEFAULT_VIEW,
diff --git a/ash/public/cpp/kiosk_app_menu.cc b/ash/public/cpp/kiosk_app_menu.cc new file mode 100644 index 0000000..b5641bf --- /dev/null +++ b/ash/public/cpp/kiosk_app_menu.cc
@@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/public/cpp/kiosk_app_menu.h" + +namespace ash { + +namespace { +KioskAppMenu* g_instance = nullptr; +} + +KioskAppMenuEntry::KioskAppMenuEntry() = default; +KioskAppMenuEntry::KioskAppMenuEntry(const KioskAppMenuEntry& other) = default; +KioskAppMenuEntry::KioskAppMenuEntry(KioskAppMenuEntry&& other) = default; +KioskAppMenuEntry::~KioskAppMenuEntry() = default; + +KioskAppMenuEntry& KioskAppMenuEntry::operator=(KioskAppMenuEntry&& other) = + default; +KioskAppMenuEntry& KioskAppMenuEntry::operator=( + const KioskAppMenuEntry& other) = default; + +// static +KioskAppMenu* KioskAppMenu::Get() { + return g_instance; +} + +KioskAppMenu::KioskAppMenu() { + DCHECK_EQ(nullptr, g_instance); + g_instance = this; +} + +KioskAppMenu::~KioskAppMenu() { + DCHECK_EQ(this, g_instance); + g_instance = nullptr; +} + +} // namespace ash
diff --git a/ash/public/cpp/kiosk_app_menu.h b/ash/public/cpp/kiosk_app_menu.h new file mode 100644 index 0000000..fa755b26 --- /dev/null +++ b/ash/public/cpp/kiosk_app_menu.h
@@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_PUBLIC_CPP_KIOSK_APP_MENU_H_ +#define ASH_PUBLIC_CPP_KIOSK_APP_MENU_H_ + +#include <string> +#include <vector> + +#include "ash/public/cpp/ash_public_export.h" +#include "base/callback_forward.h" +#include "base/strings/string16.h" +#include "components/account_id/account_id.h" +#include "ui/gfx/image/image_skia.h" + +namespace ash { + +// Metadata about a kiosk app. Used for display in the kiosk app menu in the +// login screen shelf. +struct ASH_PUBLIC_EXPORT KioskAppMenuEntry { + KioskAppMenuEntry(); + KioskAppMenuEntry(const KioskAppMenuEntry& other); + KioskAppMenuEntry(KioskAppMenuEntry&& other); + ~KioskAppMenuEntry(); + + KioskAppMenuEntry& operator=(KioskAppMenuEntry&& other); + KioskAppMenuEntry& operator=(const KioskAppMenuEntry& other); + + // For Chrome kiosk apps only, the extension app id. + std::string app_id; + + // For ARC kiosk apps only, the account id for the app. + AccountId account_id; + + base::string16 name; + + gfx::ImageSkia icon; +}; + +// An interface implemented by Ash to allow Chrome to control the kiosk app +// menu, which appears in the login shelf. +class ASH_PUBLIC_EXPORT KioskAppMenu { + public: + // Returns the singleton instance. + static KioskAppMenu* Get(); + + // Update the kiosk app data. |launch_app| will be called if the user selects + // an item (app) from the menu. + virtual void SetKioskApps( + const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& + launch_app) = 0; + + protected: + KioskAppMenu(); + virtual ~KioskAppMenu(); +}; + +} // namespace ash + +#endif // ASH_PUBLIC_CPP_KIOSK_APP_MENU_H_
diff --git a/ash/public/cpp/manifest.cc b/ash/public/cpp/manifest.cc index df991f083..478ba1c 100644 --- a/ash/public/cpp/manifest.cc +++ b/ash/public/cpp/manifest.cc
@@ -27,7 +27,6 @@ #include "ash/public/interfaces/new_window.mojom.h" #include "ash/public/interfaces/night_light_controller.mojom.h" #include "ash/public/interfaces/note_taking_controller.mojom.h" -#include "ash/public/interfaces/pref_connector.mojom.h" #include "ash/public/interfaces/process_creation_time_recorder.mojom.h" #include "ash/public/interfaces/shelf.mojom.h" #include "ash/public/interfaces/shelf_integration_test_api.mojom.h" @@ -107,7 +106,6 @@ "pref_client") .RequireCapability(content::mojom::kServiceName, "navigation") .RequireCapability(data_decoder::mojom::kServiceName, "image_decoder") - .RequireCapability(mojom::kPrefConnectorServiceName, "pref_connector") .RequireCapability(viz::mojom::kVizServiceName, "ozone") .RequireCapability(viz::mojom::kVizServiceName, "viz_host") .RequireCapability(ws::mojom::kServiceName, "ozone")
diff --git a/ash/public/cpp/rounded_corner_decorator_unittest.cc b/ash/public/cpp/rounded_corner_decorator_unittest.cc index 76dc1e92..c6a27730 100644 --- a/ash/public/cpp/rounded_corner_decorator_unittest.cc +++ b/ash/public/cpp/rounded_corner_decorator_unittest.cc
@@ -40,11 +40,12 @@ // Test that the decorator doesn't try to apply itself to destroyed layers. TEST_P(RoundedCornerDecoratorTest, RoundedCornerMaskProperlyInvalidatesItself) { - constexpr std::array<uint32_t, 4> kRadii = {4, 4, 4, 4}; + constexpr int kCornerRadius = 4; + constexpr gfx::RoundedCornersF kRadii(kCornerRadius); std::unique_ptr<aura::Window> window(aura::test::CreateTestWindowWithBounds( gfx::Rect(100, 100, 100, 100), root_window())); auto decorator = std::make_unique<ash::RoundedCornerDecorator>( - window.get(), window.get(), window->layer(), kRadii[0]); + window.get(), window.get(), window->layer(), kCornerRadius); // Confirm a mask layer exists and the decorator is valid. EXPECT_TRUE(window->layer()); @@ -66,11 +67,12 @@ // Test that mask layer changes bounds with the window it is applied to. TEST_P(RoundedCornerDecoratorTest, RoundedCornerMaskChangesBoundsOnWindowBoundsChange) { - constexpr std::array<uint32_t, 4> kRadii = {4, 4, 4, 4}; + constexpr int kCornerRadius = 4; + constexpr gfx::RoundedCornersF kRadii(kCornerRadius); std::unique_ptr<aura::Window> window(aura::test::CreateTestWindowWithBounds( gfx::Rect(100, 100, 100, 100), root_window())); auto decorator = std::make_unique<ash::RoundedCornerDecorator>( - window.get(), window.get(), window->layer(), kRadii[0]); + window.get(), window.get(), window->layer(), kCornerRadius); if (UseShaderForRoundedCorner()) { ASSERT_FALSE(window->layer()->layer_mask_layer());
diff --git a/ash/public/cpp/session/session_controller_client.h b/ash/public/cpp/session/session_controller_client.h index 47aea35..b022e0b 100644 --- a/ash/public/cpp/session/session_controller_client.h +++ b/ash/public/cpp/session/session_controller_client.h
@@ -10,6 +10,9 @@ #include "ash/public/cpp/session/session_types.h" #include "components/account_id/account_id.h" +class AccountId; +class PrefService; + namespace ash { class ASH_PUBLIC_EXPORT SessionControllerClient { @@ -34,6 +37,12 @@ // expect that Ash is listening to D-Bus signals they emit. virtual void EmitAshInitialized() = 0; + // Returns the sign-in screen pref service if available. + virtual PrefService* GetSigninScreenPrefService() = 0; + + // Returns the pref service for the given user if available. + virtual PrefService* GetUserPrefService(const AccountId& account_id) = 0; + protected: virtual ~SessionControllerClient() = default; };
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn index 162ac134..83d2ee9 100644 --- a/ash/public/interfaces/BUILD.gn +++ b/ash/public/interfaces/BUILD.gn
@@ -38,7 +38,6 @@ "ime_controller.mojom", "ime_info.mojom", "keyboard_controller.mojom", - "kiosk_app_info.mojom", "kiosk_next_shell.mojom", "locale.mojom", "login_screen.mojom", @@ -48,7 +47,6 @@ "new_window.mojom", "night_light_controller.mojom", "note_taking_controller.mojom", - "pref_connector.mojom", "process_creation_time_recorder.mojom", "shelf.mojom", "shelf_integration_test_api.mojom",
diff --git a/ash/public/interfaces/kiosk_app_info.mojom b/ash/public/interfaces/kiosk_app_info.mojom deleted file mode 100644 index 4374043f9..0000000 --- a/ash/public/interfaces/kiosk_app_info.mojom +++ /dev/null
@@ -1,24 +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. - -module ash.mojom; - -import "components/account_id/interfaces/account_id.mojom"; -import "mojo/public/mojom/base/string16.mojom"; -import "ui/gfx/image/mojo/image.mojom"; - -union KioskAppIdentifier { - // For chrome kiosk apps only, the extension app id. - string app_id; - // For ARC kiosk apps only, the account id for the app. - signin.mojom.AccountId account_id; -}; - -// Metadata about a kiosk app. Used for display in the kiosk app menu in the -// login screen shelf. -struct KioskAppInfo { - KioskAppIdentifier identifier; - mojo_base.mojom.String16 name; - gfx.mojom.ImageSkia icon; -}; \ No newline at end of file
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom index 9d2bcc8..66f6ea4 100644 --- a/ash/public/interfaces/login_screen.mojom +++ b/ash/public/interfaces/login_screen.mojom
@@ -6,7 +6,6 @@ import "ash/public/interfaces/user_info.mojom"; import "ash/public/interfaces/login_user_info.mojom"; -import "ash/public/interfaces/kiosk_app_info.mojom"; import "chromeos/components/proximity_auth/public/interfaces/auth_type.mojom"; import "components/account_id/interfaces/account_id.mojom"; import "mojo/public/mojom/base/string16.mojom"; @@ -218,10 +217,6 @@ SetPublicSessionShowFullManagementDisclosure( bool show_full_management_disclosure); - // Update the kiosk app data for the login screen. - // Returns true on success. - SetKioskApps(array<KioskAppInfo> kiosk_apps) => (bool success); - // Display a toast describing the latest kiosk app launch error. ShowKioskAppError(string message); @@ -365,12 +360,6 @@ // Request to show a feedback report dialog in chrome. ShowFeedback(); - // Launch the specific kiosk app. - LaunchKioskApp(string app_id); - - // Launch the specific ARC++ kiosk app. - LaunchArcKioskApp(signin.mojom.AccountId account_id); - // Show the powerwash (device reset) dialog. ShowResetScreen();
diff --git a/ash/public/interfaces/pref_connector.mojom b/ash/public/interfaces/pref_connector.mojom deleted file mode 100644 index 0b8c350..0000000 --- a/ash/public/interfaces/pref_connector.mojom +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module ash.mojom; - -import "components/account_id/interfaces/account_id.mojom"; -import "services/preferences/public/mojom/preferences.mojom"; - -const string kPrefConnectorServiceName = "ash_pref_connector"; - -// A connector of PrefStoreConnectors. Provides ash with access to per-profile -// prefs. -interface PrefConnector { - // Provides a PrefStoreConnector for the signin screen profile prefs. Once - // connected the prefs continue to be available even after the user logs in. - GetPrefStoreConnectorForSigninScreen( - prefs.mojom.PrefStoreConnector& connector); - - // Provides, via |connector|, a PrefStoreConnector for |account_id|. If - // |account_id| is invalid or unknown, |connector| will be closed. - GetPrefStoreConnectorForUser(signin.mojom.AccountId account_id, - prefs.mojom.PrefStoreConnector& connector); -};
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc index 0d51b19..39a81b0 100644 --- a/ash/session/session_controller_impl.cc +++ b/ash/session/session_controller_impl.cc
@@ -12,7 +12,6 @@ #include "ash/metrics/user_metrics_recorder.h" #include "ash/public/cpp/session/session_activation_observer.h" #include "ash/public/cpp/session/session_controller_client.h" -#include "ash/public/interfaces/pref_connector.mojom.h" #include "ash/public/interfaces/user_info.mojom.h" #include "ash/session/multiprofiles_intro_dialog.h" #include "ash/session/session_aborted_dialog.h" @@ -35,18 +34,13 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_type.h" -#include "services/preferences/public/cpp/pref_service_factory.h" -#include "services/preferences/public/mojom/preferences.mojom.h" -#include "services/service_manager/public/cpp/connector.h" #include "ui/message_center/message_center.h" using session_manager::SessionState; namespace ash { -SessionControllerImpl::SessionControllerImpl( - service_manager::Connector* connector) - : connector_(connector) {} +SessionControllerImpl::SessionControllerImpl() = default; SessionControllerImpl::~SessionControllerImpl() { // Abort pending start lock request. @@ -249,16 +243,12 @@ } PrefService* SessionControllerImpl::GetSigninScreenPrefService() const { - return signin_screen_prefs_.get(); + return client_ ? client_->GetSigninScreenPrefService() : nullptr; } PrefService* SessionControllerImpl::GetUserPrefServiceForUser( const AccountId& account_id) const { - auto it = per_user_prefs_.find(account_id); - if (it != per_user_prefs_.end()) - return it->second.get(); - - return nullptr; + return client_ ? client_->GetUserPrefService(account_id) : nullptr; } PrefService* SessionControllerImpl::GetPrimaryUserPrefService() const { @@ -276,7 +266,7 @@ // object instead of session state because prefs load is async after login. if (last_active_user_prefs_) return last_active_user_prefs_; - return signin_screen_prefs_.get(); + return GetSigninScreenPrefService(); } void SessionControllerImpl::AddObserver(SessionObserver* observer) { @@ -288,7 +278,7 @@ } void SessionControllerImpl::SetClient(SessionControllerClient* client) { - client_ = std::move(client); + client_ = client; } void SessionControllerImpl::SetSessionInfo(const SessionInfo& info) { @@ -361,16 +351,17 @@ // When switching to a user for whose PrefService is not ready, // |last_active_user_prefs_| continues to point to the PrefService of the // most-recently active user with a loaded PrefService. - auto it = per_user_prefs_.find(user_sessions_[0]->user_info.account_id); - if (it != per_user_prefs_.end()) - last_active_user_prefs_ = it->second.get(); + PrefService* user_pref_service = + GetUserPrefServiceForUser(user_sessions_[0]->user_info.account_id); + if (user_pref_service) + last_active_user_prefs_ = user_pref_service; for (auto& observer : observers_) { observer.OnActiveUserSessionChanged( user_sessions_[0]->user_info.account_id); } - if (it != per_user_prefs_.end()) + if (user_pref_service) MaybeNotifyOnActiveUserPrefServiceChanged(); UpdateLoginStatus(); @@ -483,17 +474,6 @@ primary_session_id_ = 0u; } -void SessionControllerImpl::SetSigninScreenPrefServiceForTest( - std::unique_ptr<PrefService> prefs) { - OnSigninScreenPrefServiceInitialized(std::move(prefs)); -} - -void SessionControllerImpl::ProvideUserPrefServiceForTest( - const AccountId& account_id, - std::unique_ptr<PrefService> pref_service) { - OnProfilePrefServiceInitialized(account_id, std::move(pref_service)); -} - void SessionControllerImpl::SetIsDemoSession() { if (is_demo_session_) return; @@ -527,11 +507,7 @@ session_activation_observer_holder_.NotifyLockStateChanged(locked); } - // Request signin profile prefs only once. - if (!signin_screen_prefs_requested_) { - ConnectToSigninScreenPrefService(); - signin_screen_prefs_requested_ = true; - } + EnsureSigninScreenPrefService(); if (was_user_session_blocked && !IsUserSessionBlocked()) EnsureActiveWindowAfterUnblockingUserSession(); @@ -545,22 +521,8 @@ user_sessions_.push_back(std::make_unique<UserSession>(user_session)); - if (connector_) { - auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); - Shell::RegisterUserProfilePrefs(pref_registry.get()); - ash::mojom::PrefConnectorPtr pref_connector_connector; - connector_->BindInterface(mojom::kPrefConnectorServiceName, - &pref_connector_connector); - prefs::mojom::PrefStoreConnectorPtr pref_connector; - pref_connector_connector->GetPrefStoreConnectorForUser( - account_id, mojo::MakeRequest(&pref_connector)); - - prefs::ConnectToPrefService( - std::move(pref_connector), std::move(pref_registry), - base::Bind(&SessionControllerImpl::OnProfilePrefServiceInitialized, - weak_ptr_factory_.GetWeakPtr(), account_id)); - } - + OnProfilePrefServiceInitialized(account_id, + GetUserPrefServiceForUser(account_id)); UpdateLoginStatus(); for (auto& observer : observers_) observer.OnUserSessionAdded(account_id); @@ -640,40 +602,27 @@ std::move(start_lock_callback_).Run(true /* locked */); } -void SessionControllerImpl::ConnectToSigninScreenPrefService() { - DCHECK(!signin_screen_prefs_requested_); - - // Null in tests. - if (!connector_) +void SessionControllerImpl::EnsureSigninScreenPrefService() { + // Obtain and notify signin profile prefs only once. + if (signin_screen_prefs_obtained_) return; - // Connect to the PrefService for the signin profile. - auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); - Shell::RegisterSigninProfilePrefs(pref_registry.get()); - ash::mojom::PrefConnectorPtr pref_connector_connector; - connector_->BindInterface(mojom::kPrefConnectorServiceName, - &pref_connector_connector); - prefs::mojom::PrefStoreConnectorPtr pref_connector; - pref_connector_connector->GetPrefStoreConnectorForSigninScreen( - mojo::MakeRequest(&pref_connector)); - prefs::ConnectToPrefService( - std::move(pref_connector), std::move(pref_registry), - base::Bind(&SessionControllerImpl::OnSigninScreenPrefServiceInitialized, - weak_ptr_factory_.GetWeakPtr())); + PrefService* const signin_prefs = GetSigninScreenPrefService(); + if (!signin_prefs) + return; + + OnSigninScreenPrefServiceInitialized(signin_prefs); } void SessionControllerImpl::OnSigninScreenPrefServiceInitialized( - std::unique_ptr<PrefService> pref_service) { - // |pref_service| can be null when running standalone without chrome. - if (!pref_service) - return; + PrefService* pref_service) { + DCHECK(pref_service); + DCHECK(!signin_screen_prefs_obtained_); - DCHECK(!signin_screen_prefs_); - signin_screen_prefs_ = std::move(pref_service); + signin_screen_prefs_obtained_ = true; - for (auto& observer : observers_) { - observer.OnSigninScreenPrefServiceInitialized(signin_screen_prefs_.get()); - } + for (auto& observer : observers_) + observer.OnSigninScreenPrefServiceInitialized(pref_service); if (on_active_user_prefs_changed_notify_deferred_) { // Notify obsevers with the deferred OnActiveUserPrefServiceChanged(). Do @@ -687,18 +636,14 @@ void SessionControllerImpl::OnProfilePrefServiceInitialized( const AccountId& account_id, - std::unique_ptr<PrefService> pref_service) { - // |pref_service| can be null when running standalone without chrome. + PrefService* pref_service) { + // |pref_service| can be null in tests. if (!pref_service) return; - PrefService* pref_service_ptr = pref_service.get(); - bool inserted = - per_user_prefs_.emplace(account_id, std::move(pref_service)).second; - DCHECK(inserted); DCHECK(!user_sessions_.empty()); if (account_id == user_sessions_[0]->user_info.account_id) { - last_active_user_prefs_ = pref_service_ptr; + last_active_user_prefs_ = pref_service; MaybeNotifyOnActiveUserPrefServiceChanged(); } @@ -707,7 +652,7 @@ void SessionControllerImpl::MaybeNotifyOnActiveUserPrefServiceChanged() { DCHECK(last_active_user_prefs_); - if (!signin_screen_prefs_) { + if (!signin_screen_prefs_obtained_) { // We must guarantee that OnSigninScreenPrefServiceInitialized() is called // before OnActiveUserPrefServiceChanged(), so defer notifying the // observers until the sign in prefs are received.
diff --git a/ash/session/session_controller_impl.h b/ash/session/session_controller_impl.h index 6688309..ad8247e1 100644 --- a/ash/session/session_controller_impl.h +++ b/ash/session/session_controller_impl.h
@@ -24,14 +24,11 @@ class AccountId; class PrefService; -namespace service_manager { -class Connector; -} - namespace ash { class SessionControllerClient; class SessionObserver; +class TestSessionControllerClient; // Implements mojom::SessionController to cache session related info such as // session state, meta data about user sessions to support synchronous @@ -40,11 +37,7 @@ public: using UserSessions = std::vector<std::unique_ptr<UserSession>>; - // |connector| is used to connect to other services for connecting to per-user - // PrefServices. If |connector| is null, no per-user PrefService instances - // will be created. In tests, ProvideUserPrefServiceForTest() can be used to - // inject a PrefService for a user when |connector| is null. - explicit SessionControllerImpl(service_manager::Connector* connector); + SessionControllerImpl(); ~SessionControllerImpl() override; base::TimeDelta session_length_limit() const { return session_length_limit_; } @@ -217,11 +210,10 @@ // Test helpers. void ClearUserSessionsForTest(); - void SetSigninScreenPrefServiceForTest(std::unique_ptr<PrefService> prefs); - void ProvideUserPrefServiceForTest(const AccountId& account_id, - std::unique_ptr<PrefService> pref_service); private: + friend class TestSessionControllerClient; + // Marks the session as a demo session for Demo Mode. void SetIsDemoSession(); void SetSessionState(session_manager::SessionState state); @@ -241,15 +233,13 @@ // run |start_lock_callback_| to indicate ash is locked successfully. void OnLockAnimationFinished(); - // Connects over mojo to the PrefService for the signin screen profile. - void ConnectToSigninScreenPrefService(); + // Ensure that the sign-in screen PrefService is obtained. + void EnsureSigninScreenPrefService(); - void OnSigninScreenPrefServiceInitialized( - std::unique_ptr<PrefService> pref_service); + void OnSigninScreenPrefServiceInitialized(PrefService* pref_service); - void OnProfilePrefServiceInitialized( - const AccountId& account_id, - std::unique_ptr<PrefService> pref_service); + void OnProfilePrefServiceInitialized(const AccountId& account_id, + PrefService* pref_service); // Notifies observers that the active user pref service changed only if the // signin profile pref service has been connected and observers were notified @@ -312,16 +302,10 @@ base::ObserverList<SessionObserver> observers_; - service_manager::Connector* const connector_; - - // Prefs for the incognito profile used by the signin screen. - std::unique_ptr<PrefService> signin_screen_prefs_; - SessionActivationObserverHolder session_activation_observer_holder_; - bool signin_screen_prefs_requested_ = false; + bool signin_screen_prefs_obtained_ = false; - std::map<AccountId, std::unique_ptr<PrefService>> per_user_prefs_; PrefService* last_active_user_prefs_ = nullptr; base::WeakPtrFactory<SessionControllerImpl> weak_ptr_factory_{this};
diff --git a/ash/session/session_controller_impl_unittest.cc b/ash/session/session_controller_impl_unittest.cc index 6225b5a..7edb966 100644 --- a/ash/session/session_controller_impl_unittest.cc +++ b/ash/session/session_controller_impl_unittest.cc
@@ -10,6 +10,7 @@ #include <vector> #include "ash/login_status.h" +#include "ash/public/cpp/ash_prefs.h" #include "ash/session/session_observer.h" #include "ash/session/test_session_controller_client.h" #include "ash/shell.h" @@ -100,7 +101,7 @@ // testing::Test: void SetUp() override { - controller_ = std::make_unique<SessionControllerImpl>(nullptr); + controller_ = std::make_unique<SessionControllerImpl>(); controller_->AddObserver(&observer_); } @@ -472,10 +473,8 @@ EXPECT_EQ(nullptr, observer.last_user_pref_service()); auto pref_service = std::make_unique<TestingPrefServiceSimple>(); - Shell::RegisterUserProfilePrefs(pref_service->registry(), - true /* for_test */); - controller->ProvideUserPrefServiceForTest(kUserAccount1, - std::move(pref_service)); + RegisterUserProfilePrefs(pref_service->registry(), true /* for_test */); + session->SetUserPrefService(kUserAccount1, std::move(pref_service)); EXPECT_EQ(controller->GetUserPrefServiceForUser(kUserAccount1), observer.last_user_pref_service()); EXPECT_EQ(controller->GetUserPrefServiceForUser(kUserAccount1), @@ -500,10 +499,8 @@ // becoming initialized. observer.clear_last_user_pref_service(); pref_service = std::make_unique<TestingPrefServiceSimple>(); - Shell::RegisterUserProfilePrefs(pref_service->registry(), - true /* for_test */); - controller->ProvideUserPrefServiceForTest(kUserAccount2, - std::move(pref_service)); + RegisterUserProfilePrefs(pref_service->registry(), true /* for_test */); + session->SetUserPrefService(kUserAccount2, std::move(pref_service)); EXPECT_EQ(nullptr, observer.last_user_pref_service()); session->SwitchActiveUser(AccountId::FromUserEmail(kUser2));
diff --git a/ash/session/test_pref_service_provider.cc b/ash/session/test_pref_service_provider.cc new file mode 100644 index 0000000..351b580 --- /dev/null +++ b/ash/session/test_pref_service_provider.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 "ash/session/test_pref_service_provider.h" + +#include <algorithm> + +#include "ash/public/cpp/ash_prefs.h" +#include "base/logging.h" +#include "components/account_id/account_id.h" +#include "components/prefs/testing_pref_service.h" + +namespace ash { + +TestPrefServiceProvider::TestPrefServiceProvider() = default; +TestPrefServiceProvider::~TestPrefServiceProvider() = default; + +void TestPrefServiceProvider::CreateSigninPrefsIfNeeded() { + if (signin_prefs_) + return; + + auto pref_service = std::make_unique<TestingPrefServiceSimple>(); + RegisterSigninProfilePrefs(pref_service->registry(), true /* for_test */); + signin_prefs_ = std::move(pref_service); +} + +void TestPrefServiceProvider::SetSigninPrefs( + std::unique_ptr<PrefService> signin_prefs) { + DCHECK(!signin_prefs_); + signin_prefs_ = std::move(signin_prefs); +} + +PrefService* TestPrefServiceProvider::GetSigninPrefs() { + return signin_prefs_.get(); +} + +void TestPrefServiceProvider::CreateUserPrefs(const AccountId& account_id) { + auto pref_service = std::make_unique<TestingPrefServiceSimple>(); + RegisterUserProfilePrefs(pref_service->registry(), true /* for_test */); + SetUserPrefs(account_id, std::move(pref_service)); +} + +void TestPrefServiceProvider::SetUserPrefs( + const AccountId& account_id, + std::unique_ptr<PrefService> pref_service) { + DCHECK(user_prefs_map_.find(account_id) == user_prefs_map_.end()); + user_prefs_map_[account_id] = std::move(pref_service); +} + +PrefService* TestPrefServiceProvider::GetUserPrefs( + const AccountId& account_id) { + auto it = user_prefs_map_.find(account_id); + if (it == user_prefs_map_.end()) + return nullptr; + + return it->second.get(); +} + +} // namespace ash
diff --git a/ash/session/test_pref_service_provider.h b/ash/session/test_pref_service_provider.h new file mode 100644 index 0000000..96b3e5b --- /dev/null +++ b/ash/session/test_pref_service_provider.h
@@ -0,0 +1,47 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SESSION_TEST_PREF_SERVICE_PROVIDER_H_ +#define ASH_SESSION_TEST_PREF_SERVICE_PROVIDER_H_ + +#include <map> +#include <memory> + +#include "base/macros.h" + +class AccountId; +class PrefService; + +namespace ash { + +// Helper class to own sign-in and user pref services. This simulates production +// code that these pref services outlives session controller and its client. +// Some of the tests re-create user sessions to simulate re-login or create its +// own SessionControllerClient. This class holds on to the pref services so that +// various ash components (by way of PrefChangeRegsitrar) still holds valid +// references to them when that happens. +class TestPrefServiceProvider { + public: + TestPrefServiceProvider(); + ~TestPrefServiceProvider(); + + void CreateSigninPrefsIfNeeded(); + void SetSigninPrefs(std::unique_ptr<PrefService> signin_prefs); + PrefService* GetSigninPrefs(); + + void CreateUserPrefs(const AccountId& account_id); + void SetUserPrefs(const AccountId& account_id, + std::unique_ptr<PrefService> pref_service); + PrefService* GetUserPrefs(const AccountId& account_id); + + private: + std::unique_ptr<PrefService> signin_prefs_; + std::map<AccountId, std::unique_ptr<PrefService>> user_prefs_map_; + + DISALLOW_COPY_AND_ASSIGN(TestPrefServiceProvider); +}; + +} // namespace ash + +#endif // ASH_SESSION_TEST_PREF_SERVICE_PROVIDER_H_
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc index 3618b7b..b9b396a4 100644 --- a/ash/session/test_session_controller_client.cc +++ b/ash/session/test_session_controller_client.cc
@@ -9,14 +9,14 @@ #include "ash/login_status.h" #include "ash/session/session_controller_impl.h" -#include "ash/shell.h" +#include "ash/session/test_pref_service_provider.h" #include "base/bind.h" #include "base/logging.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "components/account_id/account_id.h" -#include "components/prefs/testing_pref_service.h" +#include "components/prefs/pref_service.h" #include "components/session_manager/session_manager_types.h" #include "components/user_manager/user_type.h" @@ -43,8 +43,9 @@ } TestSessionControllerClient::TestSessionControllerClient( - SessionControllerImpl* controller) - : controller_(controller) { + SessionControllerImpl* controller, + TestPrefServiceProvider* prefs_provider) + : controller_(controller), prefs_provider_(prefs_provider) { DCHECK(controller_); Reset(); } @@ -59,6 +60,8 @@ session_info_.state = controller_->GetSessionState(); controller_->SetClient(this); + if (g_provide_signin_pref_service && prefs_provider_) + controller_->EnsureSigninScreenPrefService(); } void TestSessionControllerClient::Reset() { @@ -70,13 +73,8 @@ controller_->ClearUserSessionsForTest(); controller_->SetSessionInfo(session_info_); - if (g_provide_signin_pref_service && - !controller_->GetSigninScreenPrefService()) { - auto pref_service = std::make_unique<TestingPrefServiceSimple>(); - Shell::RegisterSigninProfilePrefs(pref_service->registry(), - true /* for_test */); - controller_->SetSigninScreenPrefServiceForTest(std::move(pref_service)); - } + if (g_provide_signin_pref_service && prefs_provider_) + prefs_provider_->CreateSigninPrefsIfNeeded(); } void TestSessionControllerClient::SetCanLockScreen(bool can_lock) { @@ -154,7 +152,7 @@ session.should_show_notification_tray = true; controller_->UpdateUserSession(std::move(session)); - if (provide_pref_service && + if (provide_pref_service && prefs_provider_ && !controller_->GetUserPrefServiceForUser(account_id)) { ProvidePrefServiceForUser(account_id); } @@ -163,12 +161,9 @@ void TestSessionControllerClient::ProvidePrefServiceForUser( const AccountId& account_id) { DCHECK(!controller_->GetUserPrefServiceForUser(account_id)); - - auto pref_service = std::make_unique<TestingPrefServiceSimple>(); - Shell::RegisterUserProfilePrefs(pref_service->registry(), - true /* for_test */); - controller_->ProvideUserPrefServiceForTest(account_id, - std::move(pref_service)); + prefs_provider_->CreateUserPrefs(account_id); + controller_->OnProfilePrefServiceInitialized( + account_id, prefs_provider_->GetUserPrefs(account_id)); } void TestSessionControllerClient::LockScreen() { @@ -184,6 +179,22 @@ base::RunLoop().RunUntilIdle(); } +void TestSessionControllerClient::SetSigninScreenPrefService( + std::unique_ptr<PrefService> pref_service) { + prefs_provider_->SetSigninPrefs(std::move(pref_service)); + controller_->OnSigninScreenPrefServiceInitialized( + prefs_provider_->GetSigninPrefs()); +} + +void TestSessionControllerClient::SetUserPrefService( + const AccountId& account_id, + std::unique_ptr<PrefService> pref_service) { + DCHECK(!controller_->GetUserPrefServiceForUser(account_id)); + prefs_provider_->SetUserPrefs(account_id, std::move(pref_service)); + controller_->OnProfilePrefServiceInitialized( + account_id, prefs_provider_->GetUserPrefs(account_id)); +} + void TestSessionControllerClient::RequestLockScreen() { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&TestSessionControllerClient::SetSessionState, @@ -258,4 +269,13 @@ void TestSessionControllerClient::EmitAshInitialized() {} +PrefService* TestSessionControllerClient::GetSigninScreenPrefService() { + return prefs_provider_ ? prefs_provider_->GetSigninPrefs() : nullptr; +} + +PrefService* TestSessionControllerClient::GetUserPrefService( + const AccountId& account_id) { + return prefs_provider_ ? prefs_provider_->GetUserPrefs(account_id) : nullptr; +} + } // namespace ash
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h index d214705e..5ee5c8a 100644 --- a/ash/session/test_session_controller_client.h +++ b/ash/session/test_session_controller_client.h
@@ -18,11 +18,13 @@ #include "components/user_manager/user_type.h" class AccountId; +class PrefService; namespace ash { enum class AddUserSessionPolicy; class SessionControllerImpl; +class TestPrefServiceProvider; // Implement SessionControllerClient to simulate chrome behavior // in tests. This breaks the ash/chrome dependency to allow testing ash code in @@ -33,7 +35,8 @@ // not run BrowserMain, e.g. testing::Test based test. class TestSessionControllerClient : public ash::SessionControllerClient { public: - explicit TestSessionControllerClient(SessionControllerImpl* controller); + TestSessionControllerClient(SessionControllerImpl* controller, + TestPrefServiceProvider* prefs_provider); ~TestSessionControllerClient() override; static void DisableAutomaticallyProvideSigninPref(); @@ -93,6 +96,13 @@ // Spins message loop to finish pending lock screen request if any. void FlushForTest(); + // Use |pref_service| for sign-in profile pref service. + void SetSigninScreenPrefService(std::unique_ptr<PrefService> pref_service); + + // Use |pref_service| for the user identified by |account_id|. + void SetUserPrefService(const AccountId& account_id, + std::unique_ptr<PrefService> pref_service); + // ash::SessionControllerClient: void RequestLockScreen() override; void RequestSignOut() override; @@ -100,9 +110,12 @@ void CycleActiveUser(CycleUserDirection direction) override; void ShowMultiProfileLogin() override; void EmitAshInitialized() override; + PrefService* GetSigninScreenPrefService() override; + PrefService* GetUserPrefService(const AccountId& account_id) override; private: SessionControllerImpl* const controller_; + TestPrefServiceProvider* const prefs_provider_; int fake_session_id_ = 0; SessionInfo session_info_;
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc index d6a7b81..33014b4 100644 --- a/ash/shelf/login_shelf_view.cc +++ b/ash/shelf/login_shelf_view.cc
@@ -265,25 +265,27 @@ bool LaunchAppForTesting(const std::string& app_id) { for (size_t i = 0; i < kiosk_apps_.size(); ++i) { - if (kiosk_apps_[i]->identifier->get_app_id() != app_id) - continue; - - ExecuteCommand(i, 0); - return true; + if (kiosk_apps_[i].app_id == app_id) { + ExecuteCommand(i, 0); + return true; + } } return false; } // Replace the existing items list with a new list of kiosk app menu items. - void SetApps(std::vector<mojom::KioskAppInfoPtr> kiosk_apps) { - kiosk_apps_ = std::move(kiosk_apps); + void SetApps(const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& + launch_app) { + launch_app_callback_ = launch_app; + kiosk_apps_ = kiosk_apps; Clear(); const gfx::Size kAppIconSize(16, 16); for (size_t i = 0; i < kiosk_apps_.size(); ++i) { gfx::ImageSkia icon = gfx::ImageSkiaOperations::CreateResizedImage( - kiosk_apps_[i]->icon, skia::ImageOperations::RESIZE_GOOD, + kiosk_apps_[i].icon, skia::ImageOperations::RESIZE_GOOD, kAppIconSize); - AddItemWithIcon(i, kiosk_apps_[i]->name, icon); + AddItemWithIcon(i, kiosk_apps_[i].name, icon); } } @@ -359,20 +361,7 @@ // the state is reset (when login screen reappears). is_launch_enabled_ = false; - const mojom::KioskAppInfoPtr& kiosk_app = kiosk_apps_[command_id]; - - switch (kiosk_app->identifier->which()) { - case mojom::KioskAppIdentifier::Tag::ACCOUNT_ID: - Shell::Get()->login_screen_controller()->LaunchArcKioskApp( - kiosk_app->identifier->get_account_id()); - return; - case mojom::KioskAppIdentifier::Tag::APP_ID: - Shell::Get()->login_screen_controller()->LaunchKioskApp( - kiosk_app->identifier->get_app_id()); - return; - default: - NOTREACHED(); - } + launch_app_callback_.Run(kiosk_apps_[command_id]); } bool IsCommandIdChecked(int command_id) const override { return false; } @@ -380,8 +369,9 @@ bool IsCommandIdEnabled(int command_id) const override { return true; } private: + base::RepeatingCallback<void(const KioskAppMenuEntry&)> launch_app_callback_; std::unique_ptr<views::MenuRunner> menu_runner_; - std::vector<mojom::KioskAppInfoPtr> kiosk_apps_; + std::vector<KioskAppMenuEntry> kiosk_apps_; bool is_launch_enabled_ = true; DISALLOW_COPY_AND_ASSIGN(KioskAppsButton); @@ -548,8 +538,9 @@ } void LoginShelfView::SetKioskApps( - std::vector<mojom::KioskAppInfoPtr> kiosk_apps) { - kiosk_apps_button_->SetApps(std::move(kiosk_apps)); + const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& launch_app) { + kiosk_apps_button_->SetApps(kiosk_apps, launch_app); UpdateUi(); }
diff --git a/ash/shelf/login_shelf_view.h b/ash/shelf/login_shelf_view.h index c48a073..6cffcda 100644 --- a/ash/shelf/login_shelf_view.h +++ b/ash/shelf/login_shelf_view.h
@@ -13,7 +13,7 @@ #include "ash/lock_screen_action/lock_screen_action_background_observer.h" #include "ash/login/login_screen_controller_observer.h" #include "ash/login/ui/login_data_dispatcher.h" -#include "ash/public/interfaces/kiosk_app_info.mojom.h" +#include "ash/public/cpp/kiosk_app_menu.h" #include "ash/public/interfaces/login_screen.mojom.h" #include "ash/shutdown_controller.h" #include "ash/system/locale/locale_update_controller.h" @@ -80,8 +80,12 @@ // then notifies LoginShelfView to update its own UI. void UpdateAfterSessionChange(); - // Sets the list of kiosk apps that can be launched from the login shelf. - void SetKioskApps(std::vector<mojom::KioskAppInfoPtr> kiosk_apps); + // Sets the contents of the kiosk app menu, as well as the callback used when + // a menu item is selected. + void SetKioskApps( + const std::vector<KioskAppMenuEntry>& kiosk_apps, + const base::RepeatingCallback<void(const KioskAppMenuEntry&)>& + launch_app); // Sets the state of the login dialog. void SetLoginDialogState(mojom::OobeDialogState state); @@ -188,7 +192,9 @@ ScopedObserver<LocaleUpdateController, LocaleChangeObserver> locale_change_observer_{this}; - KioskAppsButton* kiosk_apps_button_ = nullptr; // Owned by view hierarchy + // The kiosk app button will only be created for the primary display's login + // shelf. + KioskAppsButton* kiosk_apps_button_ = nullptr; // This is used in tests to wait until UI is updated. std::unique_ptr<TestUiUpdateDelegate> test_ui_update_delegate_;
diff --git a/ash/shelf/login_shelf_view_unittest.cc b/ash/shelf/login_shelf_view_unittest.cc index 6693a7f8..eb87b8a 100644 --- a/ash/shelf/login_shelf_view_unittest.cc +++ b/ash/shelf/login_shelf_view_unittest.cc
@@ -14,7 +14,7 @@ #include "ash/login/mock_login_screen_client.h" #include "ash/login/ui/login_test_base.h" #include "ash/login/ui/views_utils.h" -#include "ash/public/interfaces/kiosk_app_info.mojom.h" +#include "ash/public/cpp/kiosk_app_menu.h" #include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/session/test_session_controller_client.h" @@ -264,15 +264,13 @@ LoginShelfView::kBrowseAsGuest, LoginShelfView::kAddUser})); - std::vector<mojom::KioskAppInfoPtr> kiosk_apps; - kiosk_apps.push_back(mojom::KioskAppInfo::New()); - kiosk_apps.push_back(mojom::KioskAppInfo::New()); - login_shelf_view_->SetKioskApps(std::move(kiosk_apps)); + std::vector<KioskAppMenuEntry> kiosk_apps(2); + login_shelf_view_->SetKioskApps(kiosk_apps, {}); EXPECT_TRUE(ShowsShelfButtons( {LoginShelfView::kShutdown, LoginShelfView::kBrowseAsGuest, LoginShelfView::kAddUser, LoginShelfView::kApps})); - login_shelf_view_->SetKioskApps(std::vector<mojom::KioskAppInfoPtr>()); + login_shelf_view_->SetKioskApps({}, {}); EXPECT_TRUE(ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kBrowseAsGuest, LoginShelfView::kAddUser})); @@ -349,9 +347,8 @@ // Kiosk app button is visible when dialog state == OobeDialogState::HIDDEN // or GAIA_SIGNIN. login_shelf_view_->SetLoginDialogState(mojom::OobeDialogState::GAIA_SIGNIN); - std::vector<mojom::KioskAppInfoPtr> kiosk_apps; - kiosk_apps.push_back(mojom::KioskAppInfo::New()); - login_shelf_view_->SetKioskApps(std::move(kiosk_apps)); + std::vector<KioskAppMenuEntry> kiosk_apps(1); + login_shelf_view_->SetKioskApps(kiosk_apps, {}); EXPECT_TRUE( ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kApps})); @@ -365,7 +362,7 @@ LoginShelfView::kApps})); // Kiosk app button is hidden when no app exists. - login_shelf_view_->SetKioskApps(std::vector<mojom::KioskAppInfoPtr>()); + login_shelf_view_->SetKioskApps({}, {}); EXPECT_TRUE( ShowsShelfButtons({LoginShelfView::kShutdown, LoginShelfView::kAddUser})); }
diff --git a/ash/shell.cc b/ash/shell.cc index d25afe58..a2f089f 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -74,6 +74,7 @@ #include "ash/policy/policy_recommendation_restorer.h" #include "ash/public/cpp/ash_constants.h" #include "ash/public/cpp/ash_features.h" +#include "ash/public/cpp/ash_prefs.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/shell_window_ids.h" @@ -104,10 +105,8 @@ #include "ash/system/model/system_tray_model.h" #include "ash/system/network/sms_observer.h" #include "ash/system/network/vpn_list.h" -#include "ash/system/network/vpn_list_view.h" #include "ash/system/night_light/night_light_controller.h" #include "ash/system/palette/palette_tray.h" -#include "ash/system/palette/palette_welcome_bubble.h" #include "ash/system/power/backlights_forced_off_setter.h" #include "ash/system/power/notification_reporter.h" #include "ash/system/power/peripheral_battery_notifier.h" @@ -118,7 +117,6 @@ #include "ash/system/power/video_activity_notifier.h" #include "ash/system/screen_layout_observer.h" #include "ash/system/screen_security/screen_switch_check_controller.h" -#include "ash/system/session/logout_button_tray.h" #include "ash/system/session/logout_confirmation_controller.h" #include "ash/system/status_area_widget.h" #include "ash/system/system_notification_controller.h" @@ -241,26 +239,6 @@ DISALLOW_COPY_AND_ASSIGN(AshVisibilityController); }; -// Registers prefs whose default values are same in user and signin prefs. -void RegisterProfilePrefs(PrefRegistrySimple* registry, bool for_test) { - AccessibilityController::RegisterProfilePrefs(registry, for_test); - AppListControllerImpl::RegisterProfilePrefs(registry); - AssistantController::RegisterProfilePrefs(registry); - BluetoothPowerController::RegisterProfilePrefs(registry); - CapsLockNotificationController::RegisterProfilePrefs(registry, for_test); - DockedMagnifierController::RegisterProfilePrefs(registry, for_test); - KioskNextShellController::RegisterProfilePrefs(registry, for_test); - LoginScreenController::RegisterProfilePrefs(registry, for_test); - LogoutButtonTray::RegisterProfilePrefs(registry); - MessageCenterController::RegisterProfilePrefs(registry); - NightLightController::RegisterProfilePrefs(registry); - PaletteTray::RegisterProfilePrefs(registry); - PaletteWelcomeBubble::RegisterProfilePrefs(registry); - ShelfController::RegisterProfilePrefs(registry); - TouchDevicesController::RegisterProfilePrefs(registry); - tray::VPNListView::RegisterProfilePrefs(registry); -} - } // namespace // static @@ -400,20 +378,6 @@ DisplayPrefs::RegisterForeignPrefs(registry); } -// static -void Shell::RegisterSigninProfilePrefs(PrefRegistrySimple* registry, - bool for_test) { - RegisterProfilePrefs(registry, for_test); - PowerPrefs::RegisterSigninProfilePrefs(registry, for_test); -} - -// static -void Shell::RegisterUserProfilePrefs(PrefRegistrySimple* registry, - bool for_test) { - RegisterProfilePrefs(registry, for_test); - PowerPrefs::RegisterUserProfilePrefs(registry, for_test); -} - display::DisplayConfigurator* Shell::display_configurator() { return display_manager_->configurator(); } @@ -630,7 +594,7 @@ locale_update_controller_(std::make_unique<LocaleUpdateController>()), media_controller_(std::make_unique<MediaController>(connector)), new_window_controller_(std::make_unique<NewWindowController>()), - session_controller_(std::make_unique<SessionControllerImpl>(connector)), + session_controller_(std::make_unique<SessionControllerImpl>()), note_taking_controller_(std::make_unique<NoteTakingController>()), shell_delegate_(std::move(shell_delegate)), shell_state_(std::make_unique<ShellState>()),
diff --git a/ash/shell.h b/ash/shell.h index baab7eb..cd1e686 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -277,15 +277,6 @@ static void RegisterLocalStatePrefs(PrefRegistrySimple* registry, bool for_test); - // Registers all ash related signin/user profile prefs to the given - // |registry|. Can be called before Shell is initialized. When |for_test| is - // true this registers foreign user profile prefs (e.g. chrome prefs) as if - // they are owned by ash. This allows test code to read the pref values. - static void RegisterSigninProfilePrefs(PrefRegistrySimple* registry, - bool for_test = false); - static void RegisterUserProfilePrefs(PrefRegistrySimple* registry, - bool for_test = false); - // If necessary, initializes the Wayland server. void InitWaylandServer(std::unique_ptr<exo::FileHelper> file_helper); void DestroyWaylandServer();
diff --git a/ash/shell/example_session_controller_client.cc b/ash/shell/example_session_controller_client.cc index 61eb779..190bf8f5 100644 --- a/ash/shell/example_session_controller_client.cc +++ b/ash/shell/example_session_controller_client.cc
@@ -19,7 +19,7 @@ ExampleSessionControllerClient::ExampleSessionControllerClient( SessionControllerImpl* controller) - : TestSessionControllerClient(controller) { + : TestSessionControllerClient(controller, /*prefs_provider=*/nullptr) { DCHECK_EQ(instance, nullptr); DCHECK(controller); instance = this;
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.cc b/ash/system/accessibility/accessibility_feature_disable_dialog.cc new file mode 100644 index 0000000..90315799 --- /dev/null +++ b/ash/system/accessibility/accessibility_feature_disable_dialog.cc
@@ -0,0 +1,92 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/system/accessibility/accessibility_feature_disable_dialog.h" + +#include <memory> +#include <utility> + +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/session/session_controller_impl.h" +#include "ash/shell.h" +#include "ash/strings/grit/ash_strings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_types.h" +#include "ui/views/border.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/layout_provider.h" +#include "ui/views/widget/widget.h" + +namespace ash { + +AccessibilityFeatureDisableDialog::AccessibilityFeatureDisableDialog( + int window_title_text_id, + int dialog_text_id, + base::OnceClosure on_accept_callback, + base::OnceClosure on_cancel_callback) + : window_title_(l10n_util::GetStringUTF16(window_title_text_id)), + on_accept_callback_(std::move(on_accept_callback)), + on_cancel_callback_(std::move(on_cancel_callback)), + weak_ptr_factory_(this) { + SetLayoutManager(std::make_unique<views::FillLayout>()); + SetBorder(views::CreateEmptyBorder( + views::LayoutProvider::Get()->GetDialogInsetsForContentType( + views::TEXT, views::TEXT))); + AddChildView(std::make_unique<views::Label>( + l10n_util::GetStringUTF16(dialog_text_id))); + + // Parent the dialog widget to the LockSystemModalContainer, or + // OverlayContainer to ensure that it will get displayed on respective + // lock/signin or OOBE screen. + SessionControllerImpl* session_controller = + Shell::Get()->session_controller(); + int container_id = kShellWindowId_SystemModalContainer; + if (session_controller->GetSessionState() == + session_manager::SessionState::OOBE) { + container_id = kShellWindowId_OverlayContainer; + } else if (session_controller->IsUserSessionBlocked()) { + container_id = kShellWindowId_LockSystemModalContainer; + } + + views::Widget* widget = CreateDialogWidget( + this, nullptr, + Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(), container_id)); + widget->Show(); +} + +AccessibilityFeatureDisableDialog::~AccessibilityFeatureDisableDialog() = + default; + +bool AccessibilityFeatureDisableDialog::Cancel() { + std::move(on_cancel_callback_).Run(); + return true; +} + +bool AccessibilityFeatureDisableDialog::Accept() { + std::move(on_accept_callback_).Run(); + return true; +} + +ui::ModalType AccessibilityFeatureDisableDialog::GetModalType() const { + return ui::MODAL_TYPE_SYSTEM; +} + +base::string16 AccessibilityFeatureDisableDialog::GetWindowTitle() const { + return window_title_; +} + +base::string16 AccessibilityFeatureDisableDialog::GetDialogButtonLabel( + ui::DialogButton button) const { + if (button == ui::DIALOG_BUTTON_OK) + return l10n_util::GetStringUTF16(IDS_ASH_DISABLE_BUTTON); + return views::DialogDelegateView::GetDialogButtonLabel(button); +} + +base::WeakPtr<AccessibilityFeatureDisableDialog> +AccessibilityFeatureDisableDialog::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +} // namespace ash \ No newline at end of file
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.h b/ash/system/accessibility/accessibility_feature_disable_dialog.h new file mode 100644 index 0000000..60a8cc9 --- /dev/null +++ b/ash/system/accessibility/accessibility_feature_disable_dialog.h
@@ -0,0 +1,49 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_SYSTEM_ACCESSIBILITY_ACCESSIBILITY_FEATURE_DISABLE_DIALOG_H_ +#define ASH_SYSTEM_ACCESSIBILITY_ACCESSIBILITY_FEATURE_DISABLE_DIALOG_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "ui/views/window/dialog_delegate.h" + +namespace ash { + +// Defines a dialog for accessibility features that require confirmation from +// users prior to being disabled. For features like automatic clicks and switch +// access, accidentally disabling the feature could cause users to be unable to +// use their devices. +class AccessibilityFeatureDisableDialog : public views::DialogDelegateView { + public: + AccessibilityFeatureDisableDialog(int window_title_text_id, + int dialog_text_id, + base::OnceClosure on_accept_callback, + base::OnceClosure on_cancel_callback); + ~AccessibilityFeatureDisableDialog() override; + + // views::DialogDelegateView: + bool Cancel() override; + bool Accept() override; + ui::ModalType GetModalType() const override; + base::string16 GetWindowTitle() const override; + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; + + base::WeakPtr<AccessibilityFeatureDisableDialog> GetWeakPtr(); + + private: + const base::string16 window_title_; + base::OnceClosure on_accept_callback_; + base::OnceClosure on_cancel_callback_; + + base::WeakPtrFactory<AccessibilityFeatureDisableDialog> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(AccessibilityFeatureDisableDialog); +}; + +} // namespace ash + +#endif // ASH_SYSTEM_ACCESSIBILITY_ACCESSIBILITY_FEATURE_DISABLE_DIALOG_H_ \ No newline at end of file
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc index 94fe7a5..178513e 100644 --- a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc +++ b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc
@@ -74,9 +74,11 @@ for (int i = 0; i < 2; i++) { EXPECT_TRUE(GetBubbleController()); EXPECT_TRUE(GetMenuView()); - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false); + Shell::Get()->autoclick_controller()->SetEnabled( + false, false /* do not show dialog */); EXPECT_FALSE(GetBubbleController()); - Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true); + Shell::Get()->autoclick_controller()->SetEnabled( + true, false /* do not show dialog */); } }
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc index 92d159f..97b66a57 100644 --- a/ash/system/accessibility/tray_accessibility.cc +++ b/ash/system/accessibility/tray_accessibility.cc
@@ -191,9 +191,6 @@ kSystemMenuAccessibilityAutoClickIcon, l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK), autoclick_enabled_); - autoclick_view_->SetID(ash::VIEW_ID_ACCESSIBILITY_AUTOCLICK); - autoclick_view_->right_view()->SetID( - ash::VIEW_ID_ACCESSIBILITY_AUTOCLICK_ENABLED); virtual_keyboard_enabled_ = controller->virtual_keyboard_enabled(); virtual_keyboard_view_ = AddScrollListCheckableItem( @@ -201,6 +198,9 @@ l10n_util::GetStringUTF16( IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD), virtual_keyboard_enabled_); + virtual_keyboard_view_->SetID(ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD); + virtual_keyboard_view_->right_view()->SetID( + ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableExperimentalAccessibilitySwitchAccess)) {
diff --git a/ash/system/caps_lock_notification_controller.cc b/ash/system/caps_lock_notification_controller.cc index 151a5be..59450c9 100644 --- a/ash/system/caps_lock_notification_controller.cc +++ b/ash/system/caps_lock_notification_controller.cc
@@ -85,8 +85,6 @@ static_cast<int>(ui::chromeos::ModifierKey::kSearchKey)); return; } - // Pref is owned by chrome and flagged as PUBLIC. - registry->RegisterForeignPref(prefs::kLanguageRemapSearchKeyTo); } void CapsLockNotificationController::OnCapsLockChanged(bool enabled) {
diff --git a/ash/system/power/power_prefs.cc b/ash/system/power/power_prefs.cc index 7b49edb..ad3b5a4 100644 --- a/ash/system/power/power_prefs.cc +++ b/ash/system/power/power_prefs.cc
@@ -130,9 +130,6 @@ registry->RegisterBooleanPref( prefs::kEnableAutoScreenLock, false, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF | PrefRegistry::PUBLIC); - } else { - registry->RegisterForeignPref(prefs::kAllowScreenLock); - registry->RegisterForeignPref(prefs::kEnableAutoScreenLock); } }
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc index 39aafc83..9778d6c 100644 --- a/ash/test/ash_test_helper.cc +++ b/ash/test/ash_test_helper.cc
@@ -17,6 +17,7 @@ #include "ash/mojo_test_interface_factory.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/test/test_keyboard_controller_observer.h" +#include "ash/session/test_pref_service_provider.h" #include "ash/session/test_session_controller_client.h" #include "ash/shell.h" #include "ash/shell_init_params.h" @@ -156,8 +157,9 @@ Shell::Get()->OnLocalStatePrefServiceInitialized(std::move(pref_service)); } - session_controller_client_.reset( - new TestSessionControllerClient(shell->session_controller())); + prefs_provider_ = std::make_unique<TestPrefServiceProvider>(); + session_controller_client_.reset(new TestSessionControllerClient( + shell->session_controller(), prefs_provider_.get())); session_controller_client_->InitializeAndSetClient(); if (start_session)
diff --git a/ash/test/ash_test_helper.h b/ash/test/ash_test_helper.h index 22f3ba5..184febc 100644 --- a/ash/test/ash_test_helper.h +++ b/ash/test/ash_test_helper.h
@@ -44,6 +44,7 @@ class AppListTestHelper; class AshTestViewsDelegate; class TestKeyboardControllerObserver; +class TestPrefServiceProvider; class TestShellDelegate; // A helper class that does common initialization required for Ash. Creates a @@ -92,6 +93,7 @@ std::unique_ptr<TestSessionControllerClient> session_controller_client) { session_controller_client_ = std::move(session_controller_client); } + TestPrefServiceProvider* prefs_provider() { return prefs_provider_.get(); } AppListTestHelper* app_list_test_helper() { return app_list_test_helper_.get(); @@ -121,6 +123,7 @@ bool power_policy_controller_initialized_ = false; std::unique_ptr<TestSessionControllerClient> session_controller_client_; + std::unique_ptr<TestPrefServiceProvider> prefs_provider_; std::unique_ptr<ui::TestContextFactories> context_factories_;
diff --git a/ash/touch/touch_devices_controller_unittest.cc b/ash/touch/touch_devices_controller_unittest.cc index f57ee61..8a2a1d02 100644 --- a/ash/touch/touch_devices_controller_unittest.cc +++ b/ash/touch/touch_devices_controller_unittest.cc
@@ -6,6 +6,7 @@ #include "ash/accelerators/debug_commands.h" #include "ash/public/cpp/ash_pref_names.h" +#include "ash/public/cpp/ash_prefs.h" #include "ash/public/cpp/ash_switches.h" #include "ash/session/session_controller_impl.h" #include "ash/session/test_session_controller_client.h" @@ -198,10 +199,9 @@ // Simulate active user pref service is changed. auto pref_service = std::make_unique<TestingPrefServiceSimple>(); - Shell::RegisterUserProfilePrefs(pref_service->registry(), - true /* for_test */); - Shell::Get()->session_controller()->ProvideUserPrefServiceForTest( - kUserAccount1, std::move(pref_service)); + RegisterUserProfilePrefs(pref_service->registry(), true /* for_test */); + GetSessionControllerClient()->SetUserPrefService(kUserAccount1, + std::move(pref_service)); histogram_tester.ExpectTotalCount("Touchpad.TapDragging.Started", 1); histogram_tester.ExpectTotalCount("Touchpad.TapDragging.Changed", 0);
diff --git a/ash/wm/ash_focus_rules_unittest.cc b/ash/wm/ash_focus_rules_unittest.cc index 125cec5..1cd002c0 100644 --- a/ash/wm/ash_focus_rules_unittest.cc +++ b/ash/wm/ash_focus_rules_unittest.cc
@@ -6,6 +6,7 @@ #include "ash/public/cpp/shell_window_ids.h" #include "ash/session/session_controller_impl.h" +#include "ash/session/test_pref_service_provider.h" #include "ash/session/test_session_controller_client.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" @@ -32,8 +33,9 @@ // test lock screen widget. class LockScreenSessionControllerClient : public TestSessionControllerClient { public: - explicit LockScreenSessionControllerClient(SessionControllerImpl* controller) - : TestSessionControllerClient(controller) { + LockScreenSessionControllerClient(SessionControllerImpl* controller, + TestPrefServiceProvider* prefs_provider) + : TestSessionControllerClient(controller, prefs_provider) { InitializeAndSetClient(); CreatePredefinedUserSessions(1); } @@ -96,7 +98,8 @@ AshTestBase::SetUp(); ash_test_helper()->set_test_session_controller_client( std::make_unique<LockScreenSessionControllerClient>( - Shell::Get()->session_controller())); + Shell::Get()->session_controller(), + ash_test_helper()->prefs_provider())); } aura::Window* CreateWindowInActiveDesk() {
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc index 29d939f5..434c351 100644 --- a/ash/wm/desks/desk_preview_view.cc +++ b/ash/wm/desks/desk_preview_view.cc
@@ -11,6 +11,7 @@ #include "base/containers/flat_map.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_tree_owner.h" +#include "ui/gfx/geometry/rounded_corners_f.h" namespace ash { @@ -19,7 +20,8 @@ // The desk preview border size in dips. constexpr int kBorderSize = 2; -constexpr std::array<uint32_t, 4> kCornerRadius = {2, 2, 2, 2}; +// The rounded corner radii, also in dips. +constexpr gfx::RoundedCornersF kCornerRadii(2); // Holds data about the original desk's layers to determine what we should do // when we attempt to mirror those layers. @@ -113,13 +115,13 @@ background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR); auto* background_layer = background_view_->layer(); - background_layer->SetRoundedCornerRadius(kCornerRadius); + background_layer->SetRoundedCornerRadius(kCornerRadii); background_layer->SetIsFastRoundedCorner(true); AddChildView(background_view_); wallpaper_preview_->SetPaintToLayer(); auto* wallpaper_preview_layer = wallpaper_preview_->layer(); - wallpaper_preview_layer->SetRoundedCornerRadius(kCornerRadius); + wallpaper_preview_layer->SetRoundedCornerRadius(kCornerRadii); wallpaper_preview_layer->SetIsFastRoundedCorner(true); AddChildView(wallpaper_preview_);
diff --git a/ash/wm/overview/caption_container_view.cc b/ash/wm/overview/caption_container_view.cc index b2aeb6e..69aa2952 100644 --- a/ash/wm/overview/caption_container_view.cc +++ b/ash/wm/overview/caption_container_view.cc
@@ -238,10 +238,8 @@ return; const float scale = preview_view_->layer()->transform().Scale2d().x(); - constexpr std::array<uint32_t, 4> kEmptyRadii = {0, 0, 0, 0}; - const std::array<uint32_t, 4> kRadii = {rounding / scale, rounding / scale, - rounding / scale, rounding / scale}; - preview_view_->layer()->SetRoundedCornerRadius(show ? kRadii : kEmptyRadii); + const gfx::RoundedCornersF radii(show ? rounding / scale : 0.0f); + preview_view_->layer()->SetRoundedCornerRadius(radii); preview_view_->layer()->SetIsFastRoundedCorner(true); }
diff --git a/ash/wm/overview/drop_target_view.cc b/ash/wm/overview/drop_target_view.cc index bdc525df..63324bda 100644 --- a/ash/wm/overview/drop_target_view.cc +++ b/ash/wm/overview/drop_target_view.cc
@@ -54,9 +54,7 @@ background_view_->layer()->SetColor(kDropTargetBackgroundColor); background_view_->layer()->SetOpacity(kDropTargetBackgroundOpacity); if (ash::features::ShouldUseShaderRoundedCorner()) { - const std::array<uint32_t, 4> kRadii = { - kOverviewWindowRoundingDp, kOverviewWindowRoundingDp, - kOverviewWindowRoundingDp, kOverviewWindowRoundingDp}; + constexpr gfx::RoundedCornersF kRadii(kOverviewWindowRoundingDp); background_view_->layer()->SetRoundedCornerRadius(kRadii); background_view_->layer()->SetIsFastRoundedCorner(true); }
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc index bb329bc..9ba48d2 100644 --- a/ash/wm/overview/overview_session_unittest.cc +++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2458,8 +2458,7 @@ const ui::Layer* layer = item->transform_window_.IsMinimized() ? GetPreviewView(item)->layer() : transform_window(item).window()->layer(); - const std::array<uint32_t, 4>& radii = layer->rounded_corner_radii(); - return (radii[0] + radii[1] + radii[2] + radii[3]) > 0; + return !layer->rounded_corner_radii().IsEmpty(); } private:
diff --git a/ash/wm/overview/rounded_label_widget.cc b/ash/wm/overview/rounded_label_widget.cc index c73ea7a..d4786529 100644 --- a/ash/wm/overview/rounded_label_widget.cc +++ b/ash/wm/overview/rounded_label_widget.cc
@@ -38,9 +38,8 @@ layer()->SetFillsBoundsOpaquely(false); if (ash::features::ShouldUseShaderRoundedCorner()) { - const std::array<uint32_t, 4> kRadii = {rounding_dp, rounding_dp, - rounding_dp, rounding_dp}; - layer()->SetRoundedCornerRadius(kRadii); + const gfx::RoundedCornersF radii(rounding_dp); + layer()->SetRoundedCornerRadius(radii); layer()->SetIsFastRoundedCorner(true); }
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc index b4d9d8c..9821a12 100644 --- a/ash/wm/overview/scoped_overview_transform_window.cc +++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -467,11 +467,9 @@ ui::Layer* layer = window_->layer(); if (ash::features::ShouldUseShaderRoundedCorner()) { const float scale = layer->transform().Scale2d().x(); - static constexpr std::array<uint32_t, 4> kEmptyRadii = {0, 0, 0, 0}; - const std::array<uint32_t, 4> kRadii = { - kOverviewWindowRoundingDp / scale, kOverviewWindowRoundingDp / scale, - kOverviewWindowRoundingDp / scale, kOverviewWindowRoundingDp / scale}; - layer->SetRoundedCornerRadius(show ? kRadii : kEmptyRadii); + const gfx::RoundedCornersF radii(show ? kOverviewWindowRoundingDp / scale + : 0.0f); + layer->SetRoundedCornerRadius(radii); layer->SetIsFastRoundedCorner(true); return; }
diff --git a/base/mac/foundation_util.h b/base/mac/foundation_util.h index 9dd50e04..d7f709d 100644 --- a/base/mac/foundation_util.h +++ b/base/mac/foundation_util.h
@@ -397,5 +397,11 @@ const CFErrorRef err); BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, const CFStringRef str); +BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, CFRange); + +#if defined(__OBJC__) +BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, id); +BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, NSRange); +#endif #endif // BASE_MAC_FOUNDATION_UTIL_H_
diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm index 26a4041..b5003fbf 100644 --- a/base/mac/foundation_util.mm +++ b/base/mac/foundation_util.mm
@@ -498,3 +498,15 @@ } return o; } + +std::ostream& operator<<(std::ostream& o, CFRange range) { + return o << NSStringFromRange(NSMakeRange(range.location, range.length)); +} + +std::ostream& operator<<(std::ostream& o, id obj) { + return obj ? o << [obj description].UTF8String : o << "(nil)"; +} + +std::ostream& operator<<(std::ostream& o, NSRange range) { + return o << NSStringFromRange(range); +}
diff --git a/base/mac/foundation_util_unittest.mm b/base/mac/foundation_util_unittest.mm index f2b0accf..df8ef24b 100644 --- a/base/mac/foundation_util_unittest.mm +++ b/base/mac/foundation_util_unittest.mm
@@ -409,5 +409,24 @@ } } +#define EXPECT_LOG_EQ(expected, val) \ + EXPECT_EQ(expected, (std::ostringstream() << (val)).str()) + +TEST(FoundationLoggingTest, ObjCObject) { + EXPECT_LOG_EQ("Hello, world!", @"Hello, world!"); +} + +TEST(FoundationLoggingTest, ObjCNil) { + EXPECT_LOG_EQ("(nil)", static_cast<id>(nil)); +} + +TEST(FoundationLoggingTest, CFRange) { + EXPECT_LOG_EQ("{0, 100}", CFRangeMake(0, 100)); +} + +TEST(FoundationLoggingTest, NSRange) { + EXPECT_LOG_EQ("{0, 100}", NSMakeRange(0, 100)); +} + } // namespace mac } // namespace base
diff --git a/base/memory/platform_shared_memory_region.h b/base/memory/platform_shared_memory_region.h index d3ea593..85d3a83 100644 --- a/base/memory/platform_shared_memory_region.h +++ b/base/memory/platform_shared_memory_region.h
@@ -36,7 +36,11 @@ // Helper structs to keep two descriptors on POSIX. It's needed to support // ConvertToReadOnly(). struct BASE_EXPORT FDPair { + // The main shared memory descriptor that is used for mapping. May be either + // writable or read-only, depending on region's mode. int fd; + // The read-only descriptor, valid only in kWritable mode. Replaces |fd| when + // a region is converted to read-only. int readonly_fd; };
diff --git a/base/memory/read_only_shared_memory_region.h b/base/memory/read_only_shared_memory_region.h index 9ecf9cd9..08d0c1e 100644 --- a/base/memory/read_only_shared_memory_region.h +++ b/base/memory/read_only_shared_memory_region.h
@@ -102,15 +102,6 @@ return handle_.GetGUID(); } - private: - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, - SerializeSharedMemoryRegionMetadata); - friend class FieldTrialList; - friend class SharedMemoryHooks; - - explicit ReadOnlySharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle); - // Returns a platform shared memory handle. |this| remains the owner of the // handle. subtle::PlatformSharedMemoryRegion::PlatformHandle GetPlatformHandle() const { @@ -118,6 +109,12 @@ return handle_.GetPlatformHandle(); } + private: + friend class SharedMemoryHooks; + + explicit ReadOnlySharedMemoryRegion( + subtle::PlatformSharedMemoryRegion handle); + static void set_create_hook(CreateFunction* hook) { create_hook_ = hook; } static CreateFunction* create_hook_;
diff --git a/base/memory/unsafe_shared_memory_region.h b/base/memory/unsafe_shared_memory_region.h index 7a94d32..d82a8ae7 100644 --- a/base/memory/unsafe_shared_memory_region.h +++ b/base/memory/unsafe_shared_memory_region.h
@@ -109,14 +109,6 @@ return handle_.GetGUID(); } - private: - FRIEND_TEST_ALL_PREFIXES(DiscardableSharedMemoryTest, - LockShouldFailIfPlatformLockPagesFails); - friend class DiscardableSharedMemory; - friend class SharedMemoryHooks; - - explicit UnsafeSharedMemoryRegion(subtle::PlatformSharedMemoryRegion handle); - // Returns a platform shared memory handle. |this| remains the owner of the // handle. subtle::PlatformSharedMemoryRegion::PlatformHandle GetPlatformHandle() const { @@ -124,6 +116,11 @@ return handle_.GetPlatformHandle(); } + private: + friend class SharedMemoryHooks; + + explicit UnsafeSharedMemoryRegion(subtle::PlatformSharedMemoryRegion handle); + static void set_create_hook(CreateFunction* hook) { create_hook_ = hook; } static CreateFunction* create_hook_;
diff --git a/base/memory/writable_shared_memory_region.h b/base/memory/writable_shared_memory_region.h index debb3e1..804dc00 100644 --- a/base/memory/writable_shared_memory_region.h +++ b/base/memory/writable_shared_memory_region.h
@@ -22,6 +22,10 @@ // ReadOnlySharedMemoryRegion. However, unlike ReadOnlySharedMemoryRegion and // UnsafeSharedMemoryRegion, ownership of this region (while writable) is unique // and may only be transferred, not duplicated. +// +// Unlike ReadOnlySharedMemoryRegion and UnsafeSharedMemoryRegion, +// WritableSharedMemoryRegion doesn't provide GetPlatformHandle() method to +// ensure that the region is never duplicated while writable. class BASE_EXPORT WritableSharedMemoryRegion { public: using MappingType = WritableSharedMemoryMapping;
diff --git a/base/sampling_heap_profiler/module_cache_posix.cc b/base/sampling_heap_profiler/module_cache_posix.cc index 5d103cbd..108eb08 100644 --- a/base/sampling_heap_profiler/module_cache_posix.cc +++ b/base/sampling_heap_profiler/module_cache_posix.cc
@@ -8,6 +8,7 @@ #include <elf.h> #include "base/debug/elf_reader.h" +#include "build/build_config.h" namespace base { @@ -84,11 +85,19 @@ // static std::unique_ptr<ModuleCache::Module> ModuleCache::CreateModuleForAddress( uintptr_t address) { +#if defined(ARCH_CPU_ARM64) + // arm64 has execute-only memory (XOM) protecting code pages from being read. + // PosixModule reads executable pages in order to extract module info. This + // may result in a crash if the module is mapped as XOM + // (https://crbug.com/957801). + return nullptr; +#else Dl_info info; if (!dladdr(reinterpret_cast<const void*>(address), &info)) return nullptr; return std::make_unique<PosixModule>(info); +#endif } } // namespace base
diff --git a/base/sampling_heap_profiler/module_cache_unittest.cc b/base/sampling_heap_profiler/module_cache_unittest.cc index bf616710..abd8a9f 100644 --- a/base/sampling_heap_profiler/module_cache_unittest.cc +++ b/base/sampling_heap_profiler/module_cache_unittest.cc
@@ -62,8 +62,8 @@ bool is_native_; }; -#if defined(OS_POSIX) && !defined(OS_IOS) || defined(OS_WIN) || \ - defined(OS_FUCHSIA) +#if defined(OS_POSIX) && !defined(OS_IOS) && !defined(ARCH_CPU_ARM64) || \ + defined(OS_WIN) || defined(OS_FUCHSIA) #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, TestName) #else #define MAYBE_TEST(TestSuite, TestName) TEST(TestSuite, DISABLED_##TestName)
diff --git a/build/android/gradle/root.jinja b/build/android/gradle/root.jinja index a53591e..c7bd7b0c 100644 --- a/build/android/gradle/root.jinja +++ b/build/android/gradle/root.jinja
@@ -10,7 +10,7 @@ } dependencies { {% if channel == 'canary' %} - classpath "com.android.tools.build:gradle:3.5.0-alpha07" + classpath "com.android.tools.build:gradle:3.5.0-beta01" {% elif channel == 'beta' %} classpath "com.android.tools.build:gradle:3.1.0-beta4" {% else %}
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 1e09043dc..1f0d576 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc
@@ -615,7 +615,7 @@ SetNeedsCommit(); } -void Layer::SetRoundedCorner(const std::array<uint32_t, 4>& corner_radii) { +void Layer::SetRoundedCorner(const gfx::RoundedCornersF& corner_radii) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.corner_radii == corner_radii) return;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h index 5a2fa5b..696009b4 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h
@@ -34,6 +34,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/rrect_f.h" #include "ui/gfx/transform.h" @@ -249,16 +250,13 @@ // Set or get the rounded corner radii which is applied to the layer and its // subtree (as if they are together as a single composited entity) when // blitting into their target. Setting this makes the layer masked to bounds. - void SetRoundedCorner(const std::array<uint32_t, 4>& corner_radii); - const std::array<uint32_t, 4>& corner_radii() const { + void SetRoundedCorner(const gfx::RoundedCornersF& corner_radii); + const gfx::RoundedCornersF& corner_radii() const { return inputs_.corner_radii; } // Returns true if any of the corner has a non-zero radius set. - bool HasRoundedCorner() const { - return corner_radii()[0] + corner_radii()[1] + corner_radii()[2] + - corner_radii()[3]; - } + bool HasRoundedCorner() const { return !corner_radii().IsEmpty(); } // Set or get the flag that disables the requirement of a render surface for // this layer due to it having rounded corners. This improves performance at @@ -960,7 +958,7 @@ // Corner clip radius for the 4 corners of the layer in the following order: // top left, top right, bottom right, bottom left - std::array<uint32_t, 4> corner_radii; + gfx::RoundedCornersF corner_radii; // If set, disables this layer's rounded corner from triggering a render // surface on itself if possible.
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index cec08513..80a1ff2f 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc
@@ -351,8 +351,7 @@ grand_child->PushPropertiesTo(grand_child_impl.get())); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); - const std::array<uint32_t, 4> radii{1, 2, 3, 4}; - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner(radii)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner({1, 2, 3, 4})); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); child->PushPropertiesTo(child_impl.get());
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index e5f34003..073e50c 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -211,10 +211,8 @@ } static gfx::RRectF RoundedCornerBounds(Layer* layer) { - const std::array<uint32_t, 4> radii = layer->corner_radii(); - return gfx::RRectF(gfx::RectF(gfx::Rect(layer->bounds())), radii[0], radii[0], - radii[1], radii[1], radii[2], radii[2], radii[3], - radii[3]); + return gfx::RRectF(gfx::RectF(gfx::Rect(layer->bounds())), + layer->corner_radii()); } static gfx::RRectF RoundedCornerBounds(LayerImpl* layer) {
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java index eb8985c..a4ae5ae 100644 --- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java +++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java
@@ -1244,7 +1244,7 @@ if (mActivity.getCompositorViewHolder() != null) { mActivity.getCompositorViewHolder().onEnterVr(); } - ScreenOrientationProvider.setOrientationDelegate(this); + ScreenOrientationProvider.getInstance().setOrientationDelegate(this); // Hide system UI. VrModuleProvider.getDelegate().setSystemUiVisibilityForVr(mActivity); @@ -1263,7 +1263,7 @@ @TargetApi(Build.VERSION_CODES.KITKAT) private void restoreWindowMode() { - ScreenOrientationProvider.setOrientationDelegate(null); + ScreenOrientationProvider.getInstance().setOrientationDelegate(null); mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Restore orientation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java index 8b3b63e5..062facf4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -54,7 +54,6 @@ import org.chromium.chrome.browser.tab.AuthenticatorNavigationInterceptor; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.touchless.TouchlessModelCoordinator; -import org.chromium.chrome.browser.touchless.TouchlessUiController; import org.chromium.chrome.browser.touchless.TouchlessUiCoordinator; import org.chromium.chrome.browser.ui.ImmersiveModeManager; import org.chromium.chrome.browser.usage_stats.DigitalWellbeingClient; @@ -396,28 +395,6 @@ } /** - * Do not use, in the process of being deleted. - */ - @Deprecated - public void attachTouchlessMenuCoordinator(ChromeActivity activity) {} - - /** - * Do not use, in the process of being deleted. - */ - @Deprecated - public TouchlessUiController createTouchlessUiController(ChromeActivity activity) { - return null; - } - - /** - * Do not use, in the process of being deleted. - */ - @Deprecated - public TouchlessUiController getTouchlessUiControllerForActivity(ChromeActivity activity) { - return null; - } - - /** * Checks the Google Play services availability on the this device. * * This is a workaround for the @@ -501,9 +478,4 @@ * ChromeActivity initialization. */ public void startSystemSettingsObserver() {} - - /** - * Initializes the lifecycle tracker for Touchless mode. - */ - public void initTouchlessLifecycleTracker() {} }
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 a2644262..dafaea64 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -29,6 +29,7 @@ import android.util.DisplayMetrics; import android.util.Pair; import android.util.TypedValue; +import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -140,6 +141,7 @@ import org.chromium.chrome.browser.toolbar.ToolbarManager; import org.chromium.chrome.browser.toolbar.top.Toolbar; import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer; +import org.chromium.chrome.browser.touchless.TouchlessUiCoordinator; import org.chromium.chrome.browser.translate.TranslateBridge; import org.chromium.chrome.browser.ui.RootUiCoordinator; import org.chromium.chrome.browser.ui.system.StatusBarColorController; @@ -346,6 +348,11 @@ */ private RootUiCoordinator mRootUiCoordinator; + /** + * Coordinates Touchless UI across ChromeActivity-derived classes. + */ + private TouchlessUiCoordinator mTouchlessUiCoordinator; + private List<MenuOrKeyboardActionHandler> mMenuActionHandlers = new ArrayList<>(); @Override @@ -368,6 +375,9 @@ // a recommended pattern. mRootUiCoordinator = new RootUiCoordinator(this); + // See comments on #getTouchlessUiCoordinator for why we're doing this here. + getTouchlessUiCoordinator(); + VrModuleProvider.getDelegate().doPreInflationStartup(this, getSavedInstanceState()); // Force a partner customizations refresh if it has yet to be initialized. This can happen @@ -1343,6 +1353,9 @@ */ @Override public SnackbarManager getSnackbarManager() { + if (getTouchlessUiCoordinator() != null) { + return getTouchlessUiCoordinator().getSnackbarManager(); + } boolean useBottomSheetContainer = mBottomSheetController != null && mBottomSheetController.getBottomSheet().isSheetOpen() && !mBottomSheetController.getBottomSheet().isClosing(); @@ -1352,6 +1365,9 @@ @Override protected ModalDialogManager createModalDialogManager() { + if (getTouchlessUiCoordinator() != null) { + return getTouchlessUiCoordinator().createModalDialogManager(); + } return new ModalDialogManager( new AppModalPresenter(this), ModalDialogManager.ModalDialogType.APP); } @@ -2571,6 +2587,31 @@ return getToolbarLayoutId() != NO_TOOLBAR_LAYOUT; } + /** + * TODO(mthiesse): Figure out a way to clean this up. The problem is that the + * TouchlessUiCoordinator has an implementation of the ModalDialogManager, which is created in + * AsyncInitializationActivity#onCreateInternal, before any ChromeActivity init functions are + * called, and making AsyncInitializationActivity aware of the TouchlessUiCoordinator would be + * wrong. Hence, we create the UiCoordinator as soon as somebody tries to use it, but we also + * need to make sure it gets initialized early on regardless of whether somebody tries to use it + * as it monitors Lifecycles, etc. + */ + private TouchlessUiCoordinator getTouchlessUiCoordinator() { + if (mTouchlessUiCoordinator == null && FeatureUtilities.isNoTouchModeEnabled()) { + mTouchlessUiCoordinator = AppHooks.get().createTouchlessUiCoordinator(this); + } + return mTouchlessUiCoordinator; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (getTouchlessUiCoordinator() != null + && getTouchlessUiCoordinator().dispatchKeyEvent(event)) { + return true; + } + return super.dispatchKeyEvent(event); + } + /** Returns {@link BottomSheetController}, if present. */ @Nullable public BottomSheetController getBottomSheetController() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java index 51391c6..c4104bb8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -135,8 +135,6 @@ // Disable MemoryPressureMonitor polling when Chrome goes to the background. ApplicationStatus.registerApplicationStateListener( ChromeApplication::updateMemoryPressurePolling); - - AppHooks.get().initTouchlessLifecycleTracker(); } // Write installed modules to crash keys. This needs to be done as early as possible so that
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkSplashscreenMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkSplashscreenMetrics.java index 4ac5e82..e7b0109a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkSplashscreenMetrics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkSplashscreenMetrics.java
@@ -18,6 +18,9 @@ } @Override + public void onTranslucencyRemoved() {} + + @Override public void onSplashscreenHidden(long startTimestamp, long endTimestamp) { if (mShellApkLaunchTimeMs == -1) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java index 4ee8082..4e1d901 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java
@@ -126,7 +126,7 @@ } // Save current orientation mode, and then lock current orientation. - ScreenOrientationProvider.setOrientationDelegate(this); + ScreenOrientationProvider.getInstance().setOrientationDelegate(this); if (mRestoreOrientation == null) { mRestoreOrientation = mActivity.getRequestedOrientation(); } @@ -160,7 +160,7 @@ mCleanupInProgress = true; // Restore orientation. - ScreenOrientationProvider.setOrientationDelegate(null); + ScreenOrientationProvider.getInstance().setOrientationDelegate(null); if (mRestoreOrientation != null) mActivity.setRequestedOrientation(mRestoreOrientation); mRestoreOrientation = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java index 310a75028..79e5fd6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/SplashscreenObserver.java
@@ -8,6 +8,9 @@ * Observer interface for WebApp activity splashscreen. */ public interface SplashscreenObserver { + /** Called when the activity's translucency is removed. */ + void onTranslucencyRemoved(); + /** * Called when the splash screen is hidden. * @param startTimestamp Time that the splash screen was shown.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java index 0a3476f..8c46660 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -294,9 +294,6 @@ StrictMode.setThreadPolicy(oldPolicy); } - ScreenOrientationProvider.lockOrientation( - getWindowAndroid(), (byte) mWebappInfo.orientation()); - // When turning on TalkBack on Android, hitting app switcher to bring a WebappActivity to // front will speak "Web App", which is the label of WebappActivity. Therefore, we set title // of the WebappActivity explicitly to make it speak the short name of the Web App. @@ -304,6 +301,8 @@ super.performPreInflationStartup(); + applyScreenOrientation(); + if (mWebappInfo.displayMode() == WebDisplayMode.FULLSCREEN) { enterImmersiveMode(); } @@ -889,6 +888,32 @@ mSplashController.setDelegate(delegate); } + /** Sets the screen orientation. */ + private void applyScreenOrientation() { + if (mWebappInfo.isSplashProvidedByWebApk()) { + // When the splash screen is provided by the WebAPK, the activity is initially + // translucent. Setting the screen orientation while the activity is translucent + // throws an exception. Delay setting it. + ScreenOrientationProvider.getInstance().delayOrientationRequests(getWindowAndroid()); + + addSplashscreenObserver(new SplashscreenObserver() { + @Override + public void onTranslucencyRemoved() { + ScreenOrientationProvider.getInstance().runDelayedOrientationRequests( + getWindowAndroid()); + } + + @Override + public void onSplashscreenHidden(long startTimestamp, long endTimestamp) {} + }); + + // Fall through and queue up request for the default screen orientation because the web + // page might change it via JavaScript. + } + ScreenOrientationProvider.getInstance().lockOrientation( + getWindowAndroid(), (byte) mWebappInfo.orientation()); + } + /** * Register an observer to the splashscreen hidden/visible events for this activity. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java index 9139e1f..eb6a865 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/accessibility/AccessibilityTabModelListItem.java
@@ -60,7 +60,8 @@ private final float mFlingCommitDistance; private final int mDefaultLevel; private final int mIncognitoLevel; - private final ColorStateList mDarkIconColor; + private final ColorStateList mDefaultIconColor; + private final ColorStateList mIncognitoIconColor; private final ColorStateList mDefaultCloseIconColor; private final ColorStateList mIncognitoCloseIconColor; @@ -216,7 +217,9 @@ mDefaultHeight = context.getResources().getDimensionPixelOffset(R.dimen.accessibility_tab_height); - mDarkIconColor = ColorUtils.getIconTint(context, false); + mDefaultIconColor = ColorUtils.getIconTint(context, false); + mIncognitoIconColor = + AppCompatResources.getColorStateList(context, R.color.default_icon_color_dark); mDefaultCloseIconColor = AppCompatResources.getColorStateList(context, R.color.default_icon_color_secondary); mIncognitoCloseIconColor = @@ -342,7 +345,8 @@ mFaviconView.setImageBitmap(bitmap); } else { mFaviconView.setImageResource(R.drawable.ic_globe_24dp); - ApiCompatibilityUtils.setImageTintList(mFaviconView, mDarkIconColor); + ApiCompatibilityUtils.setImageTintList( + mFaviconView, mTab.isIncognito() ? mIncognitoIconColor : mDefaultIconColor); } } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java index 05fbe22..b220885 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java
@@ -22,6 +22,7 @@ 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.Feature; import org.chromium.base.test.util.Restriction; import org.chromium.chrome.browser.ChromeSwitches; @@ -438,6 +439,7 @@ @Test @MediumTest @Feature({"Browser", "RenderTest"}) + @DisabledTest(message = "https://crbug.com/962397") public void testFullscreenVideoControls() throws InterruptedException, TimeoutException, IOException { // There's occasionally slight AA differences along the play button, so tolerate a small
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 d566d8f..e0f916e 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
@@ -10,12 +10,10 @@ import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; -import android.view.KeyEvent; import android.view.ViewGroup; import org.chromium.base.VisibleForTesting; import org.chromium.base.metrics.RecordHistogram; -import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeInactivityTracker; import org.chromium.chrome.browser.IntentHandler; @@ -26,7 +24,6 @@ import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; -import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tab.TabState; @@ -34,7 +31,6 @@ import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.common.Referrer; import org.chromium.ui.base.PageTransition; -import org.chromium.ui.modaldialog.ModalDialogManager; /** * An Activity used to display WebContents on devices that don't support touch. @@ -209,11 +205,6 @@ protected void initializeToolbar() {} @Override - public ModalDialogManager createModalDialogManager() { - return getUiCoordinator().createModalDialogManager(); - } - - @Override protected ChromeFullscreenManager createFullscreenManager() { return new ChromeFullscreenManager(this, ChromeFullscreenManager.ControlsPosition.NONE); } @@ -250,12 +241,6 @@ @Override public void performPreInflationStartup() { super.performPreInflationStartup(); - getUiCoordinator(); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return getUiCoordinator().dispatchKeyEvent(event) || super.dispatchKeyEvent(event); } @Override @@ -271,27 +256,8 @@ getFullscreenManager().exitPersistentFullscreenMode(); } - private TouchlessUiCoordinator getUiCoordinator() { - if (mUiCoordinator == null) { - mUiCoordinator = AppHooks.get().createTouchlessUiCoordinator(this); - } - return mUiCoordinator; - } - - @Override - public SnackbarManager getSnackbarManager() { - return getUiCoordinator().getSnackbarManager(); - } - @Override protected TabDelegate createTabDelegate(boolean incognito) { return new TouchlessTabDelegate(incognito); } - - /** - * TODO(mthiesse): Delete this, it's to keep downstream compiling with a 3-sided patch. - */ - public TouchlessUiController getTouchlessUiController() { - return null; - } }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiController.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiController.java deleted file mode 100644 index e16785b..0000000 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiController.java +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.touchless; - -import android.view.KeyEvent; - -import org.chromium.ui.modelutil.PropertyModel; - -/** A controller for touchless UI. */ -public interface TouchlessUiController { - /** - * Add a model to the queue to be shown. - * @param model The model to add. - */ - default void addModelToQueue(PropertyModel model) {} - - /** - * Remove a model from the queue to be shown. - * @param model The model to remove. - */ - default void removeModelFromQueue(PropertyModel model) {} - - /** - * A notification that a key event occurred. - * @param event The event object. - * @return Whether the event was consumed. - */ - boolean onKeyEvent(KeyEvent event); - - /** - * Clean up anything that needs to be. - */ - void destroy(); -}
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java index b7aa78c..7d8f115 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java
@@ -53,6 +53,8 @@ @Override public void onPostInflationStartup() { + // Only NoTouchActivity wants the progress bar and tooltips. + if (!(mActivity instanceof NoTouchActivity)) return; ViewGroup coordinatorLayout = (ViewGroup) mActivity.findViewById(R.id.coordinator); mTooltipView = new TooltipView(mActivity); mProgressBarView = new ProgressBarView(mActivity); @@ -65,8 +67,11 @@ @Override public void onFinishNativeInitialization() { - mKeyFunctionsIPHCoordinator = - new KeyFunctionsIPHCoordinator(mTooltipView, mActivity.getActivityTabProvider()); + // Only NoTouchActivity wants the tooltips. + if (mActivity instanceof NoTouchActivity) { + mKeyFunctionsIPHCoordinator = new KeyFunctionsIPHCoordinator( + mTooltipView, mActivity.getActivityTabProvider()); + } mTouchlessZoomHelper = new TouchlessZoomHelper(mActivity.getActivityTabProvider()); }
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni index 7d12ed8..98ffbcf 100644 --- a/chrome/android/touchless/touchless_java_sources.gni +++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -44,7 +44,6 @@ "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessSuggestionsBinder.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.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/TouchlessUiCoordinator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinatorImpl.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java", @@ -71,7 +70,6 @@ touchess_fallback_java_sources = [ "touchless/fallback/java/src/org/chromium/chrome/browser/touchless/TouchlessDelegate.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessModelCoordinator.java", - "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiController.java", "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessUiCoordinator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogProperties.java", ]
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn index d2b5f313..e21d3b6 100644 --- a/chrome/app/BUILD.gn +++ b/chrome/app/BUILD.gn
@@ -617,7 +617,6 @@ if (is_chromeos) { deps += [ "//ash/public/cpp:manifest", - "//chrome/browser/chromeos:ash_pref_connector_manifest", "//chrome/services/cups_ipp_parser/public/cpp:manifest", "//chrome/services/cups_proxy/public/cpp:manifest", "//chromeos/services/cellular_setup/public/cpp:manifest",
diff --git a/chrome/app/chrome_packaged_service_manifests.cc b/chrome/app/chrome_packaged_service_manifests.cc index 1ef15dc..781c392 100644 --- a/chrome/app/chrome_packaged_service_manifests.cc +++ b/chrome/app/chrome_packaged_service_manifests.cc
@@ -23,7 +23,6 @@ #if defined(OS_CHROMEOS) #include "ash/public/cpp/manifest.h" -#include "chrome/browser/chromeos/prefs/ash_pref_connector_manifest.h" #include "chrome/services/cups_ipp_parser/public/cpp/manifest.h" // nogncheck #include "chrome/services/cups_proxy/public/cpp/manifest.h" #include "chromeos/services/cellular_setup/public/cpp/manifest.h" @@ -166,7 +165,6 @@ #endif #if defined(OS_CHROMEOS) ash::GetManifest(), - GetAshPrefConnectorManifest(), GetCupsIppParserManifest(), chromeos::cellular_setup::GetManifest(), chromeos::printing::GetCupsProxyManifest(),
diff --git a/chrome/app/file_pre_reader_win.cc b/chrome/app/file_pre_reader_win.cc index 38f5439a..957f9d2 100644 --- a/chrome/app/file_pre_reader_win.cc +++ b/chrome/app/file_pre_reader_win.cc
@@ -10,17 +10,22 @@ #include "base/files/file.h" #include "base/files/memory_mapped_file.h" -#include "base/win/windows_version.h" void PreReadFile(const base::FilePath& file_path) { - // On Win10 RS6 and higher with the increased prefetch limit we don't need - // to do in process prefetch. On OS releases Win8/Server 2012 to Win10 RS5 - // use ::PrefetchVirtualMemory(). This is better than a simple data file - // read, more from a RAM perspective than CPU. This is because reading the - // file as data results in double mapping to Image/executable pages for all - // pages of code executed. On Win7 just do a simple file read as data. + // On Win8 and higher use ::PrefetchVirtualMemory(). This is better than + // a simple data file read, more from a RAM perspective than CPU. This is + // because reading the file as data results in double mapping to + // Image/executable pages for all pages of code executed. On Win7 just do a + // simple file read as data. - if (base::win::GetVersion() == base::win::Version::WIN7) { + // ::PrefetchVirtualMemory() isn't available on Win7. + HMODULE kernel32_library = GetModuleHandleA("kernel32.dll"); + + auto prefetch_virtual_memory = + reinterpret_cast<decltype(&::PrefetchVirtualMemory)>( + GetProcAddress(kernel32_library, "PrefetchVirtualMemory")); + + if (!prefetch_virtual_memory) { // On Win7 read in the file as data since the OS doesn't have // the support for better options. @@ -55,13 +60,6 @@ _WIN32_MEMORY_RANGE_ENTRY address_range = {mapped_file.data(), mapped_file.length() / 2}; - // ::PrefetchVirtualMemory() isn't available on Win7. - HMODULE kernel32_library = GetModuleHandleA("kernel32.dll"); - - auto prefetch_virtual_memory = - reinterpret_cast<decltype(&::PrefetchVirtualMemory)>( - GetProcAddress(kernel32_library, "PrefetchVirtualMemory")); - // NB: PrefetchVirtualMemory requires the file to be opened with // only read access or it will fail.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 2f252dc6..6452b5b 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1108,6 +1108,7 @@ "performance_manager/performance_manager_clock.h", "performance_manager/performance_manager_tab_helper.cc", "performance_manager/performance_manager_tab_helper.h", + "performance_manager/persistence/site_data/disk_site_data_store.h", "performance_manager/public/graph/frame_node.h", "performance_manager/public/graph/graph.h", "performance_manager/public/graph/node_attached_data.h", @@ -3564,7 +3565,7 @@ "//chrome/chrome_watcher:client", "//chrome/common:metrics_constants_util_win", "//chrome/common:version_header", - "//chrome/credential_provider/gaiacp:common", + "//chrome/credential_provider/common:common_constants", "//chrome/install_static:install_static_util", "//chrome/notification_helper:constants", "//chrome/services/util_win/public/mojom",
diff --git a/chrome/browser/accessibility/image_annotation_browsertest.cc b/chrome/browser/accessibility/image_annotation_browsertest.cc index ebcbe7ae..32ff719 100644 --- a/chrome/browser/accessibility/image_annotation_browsertest.cc +++ b/chrome/browser/accessibility/image_annotation_browsertest.cc
@@ -273,9 +273,9 @@ // that never happens, the test will time out. content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); - while (8 > DescribeNodesWithAnnotations( - content::GetAccessibilityTreeSnapshot(web_contents)) - .size()) { + while (10 > DescribeNodesWithAnnotations( + content::GetAccessibilityTreeSnapshot(web_contents)) + .size()) { content::WaitForAccessibilityTreeToChange(web_contents); } @@ -292,7 +292,9 @@ "image Appears to say: printer.png Annotation", "image Appears to say: red.png Annotation", "link Appears to say: printer.png Annotation", - "image Appears to say: printer.png Annotation")); + "image Appears to say: printer.png Annotation", + "link Appears to say: green.png Annotation", + "image Appears to say: green.png Annotation")); } IN_PROC_BROWSER_TEST_F(ImageAnnotationBrowserTest, ImageDoc) {
diff --git a/chrome/browser/android/explore_sites/catalog.cc b/chrome/browser/android/explore_sites/catalog.cc index 27a7909..ac2763f 100644 --- a/chrome/browser/android/explore_sites/catalog.cc +++ b/chrome/browser/android/explore_sites/catalog.cc
@@ -7,20 +7,16 @@ #include <sstream> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/values.h" namespace explore_sites { // static -std::unique_ptr<NTPCatalog> NTPCatalog::create( - const base::DictionaryValue* json) { - if (!json || !json->is_dict()) +std::unique_ptr<NTPCatalog> NTPCatalog::create(const base::Value& json) { + if (!json.is_dict()) return nullptr; - const base::ListValue* categories = static_cast<const base::ListValue*>( - json->FindKeyOfType("categories", base::Value::Type::LIST)); - + const base::Value* categories = json.FindListKey("categories"); if (!categories) return nullptr; @@ -29,27 +25,21 @@ if (!category.is_dict()) { return nullptr; } - const base::DictionaryValue* category_dict = - static_cast<const base::DictionaryValue*>(&category); - const base::Value* id = - category_dict->FindKeyOfType("id", base::Value::Type::STRING); - const base::Value* title = - category_dict->FindKeyOfType("title", base::Value::Type::STRING); - const base::Value* icon_url_str = - category_dict->FindKeyOfType("icon_url", base::Value::Type::STRING); + const std::string* id = category.FindStringKey("id"); + const std::string* title = category.FindStringKey("title"); + const std::string* icon_url_str = category.FindStringKey("icon_url"); if (!id || !title || !icon_url_str) continue; - GURL icon_url(icon_url_str->GetString()); + GURL icon_url(*icon_url_str); if (icon_url.is_empty()) continue; - catalog_categories.push_back( - {id->GetString(), title->GetString(), icon_url}); + catalog_categories.push_back({*id, *title, icon_url}); } - auto catalog = base::WrapUnique(new NTPCatalog(catalog_categories)); + auto catalog = std::make_unique<NTPCatalog>(catalog_categories); DVLOG(1) << "Catalog parsed: " << catalog->ToString(); return catalog;
diff --git a/chrome/browser/android/explore_sites/catalog.h b/chrome/browser/android/explore_sites/catalog.h index 0f1ad10..239afef 100644 --- a/chrome/browser/android/explore_sites/catalog.h +++ b/chrome/browser/android/explore_sites/catalog.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_ANDROID_EXPLORE_SITES_CATALOG_H_ #define CHROME_BROWSER_ANDROID_EXPLORE_SITES_CATALOG_H_ +#include <memory> #include <string> #include <utility> #include <vector> @@ -12,7 +13,7 @@ #include "url/gurl.h" namespace base { -class DictionaryValue; +class Value; } namespace explore_sites { @@ -34,7 +35,7 @@ }; // The NTPCatalog does not take ownership of |json|. - static std::unique_ptr<NTPCatalog> create(const base::DictionaryValue* json); + static std::unique_ptr<NTPCatalog> create(const base::Value& json); explicit NTPCatalog(const std::vector<Category>& category_list); ~NTPCatalog();
diff --git a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc b/chrome/browser/android/explore_sites/ntp_json_fetcher.cc index f97b7671..e29c64d 100644 --- a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc +++ b/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
@@ -113,8 +113,7 @@ return; } - auto catalog = NTPCatalog::create( - static_cast<base::DictionaryValue*>(parsed_json.get())); + auto catalog = NTPCatalog::create(*parsed_json); std::move(callback_).Run(std::move(catalog)); }
diff --git a/chrome/browser/ash_service_registry.cc b/chrome/browser/ash_service_registry.cc index e19a480..6c70986 100644 --- a/chrome/browser/ash_service_registry.cc +++ b/chrome/browser/ash_service_registry.cc
@@ -6,7 +6,6 @@ #include "ash/ash_service.h" #include "ash/public/interfaces/constants.mojom.h" -#include "chrome/browser/chromeos/prefs/pref_connector_service.h" #include "content/public/common/service_manager_connection.h" namespace ash_service_registry { @@ -14,8 +13,6 @@ std::unique_ptr<service_manager::Service> HandleServiceRequest( const std::string& service_name, service_manager::mojom::ServiceRequest request) { - if (service_name == ash::mojom::kPrefConnectorServiceName) - return std::make_unique<AshPrefConnector>(std::move(request)); return service_name == ash::mojom::kServiceName ? std::make_unique<ash::AshService>(std::move(request)) : nullptr;
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc index f4994b94..ad2a2f1d 100644 --- a/chrome/browser/chrome_browser_main_win.cc +++ b/chrome/browser/chrome_browser_main_win.cc
@@ -82,6 +82,8 @@ #include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" +#include "content/public/common/service_manager_connection.h" +#include "services/service_manager/public/cpp/connector.h" #include "ui/base/cursor/cursor_loader_win.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_win.h" @@ -195,11 +197,13 @@ // Initializes the ModuleDatabase on its owning sequence. Also starts the // enumeration of registered modules in the Windows Registry. -void InitializeModuleDatabase(bool is_third_party_blocking_policy_enabled) { +void InitializeModuleDatabase( + std::unique_ptr<service_manager::Connector> connector, + bool is_third_party_blocking_policy_enabled) { DCHECK(ModuleDatabase::GetTaskRunner()->RunsTasksInCurrentSequence()); - ModuleDatabase::SetInstance( - std::make_unique<ModuleDatabase>(is_third_party_blocking_policy_enabled)); + ModuleDatabase::SetInstance(std::make_unique<ModuleDatabase>( + std::move(connector), is_third_party_blocking_policy_enabled)); auto* module_database = ModuleDatabase::GetInstance(); module_database->StartDrainingModuleLoadAttemptsLog(); @@ -411,8 +415,12 @@ #endif ModuleDatabase::GetTaskRunner()->PostTask( - FROM_HERE, base::BindOnce(&InitializeModuleDatabase, - third_party_blocking_policy_enabled)); + FROM_HERE, + base::BindOnce(&InitializeModuleDatabase, + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->Clone(), + third_party_blocking_policy_enabled)); *module_watcher = ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent)); }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 18e6bd9..cbde5729 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -1373,8 +1373,8 @@ "login/ui/captive_portal_window_proxy.h", "login/ui/input_events_blocker.cc", "login/ui/input_events_blocker.h", - "login/ui/kiosk_app_menu_updater.cc", - "login/ui/kiosk_app_menu_updater.h", + "login/ui/kiosk_app_menu_controller.cc", + "login/ui/kiosk_app_menu_controller.h", "login/ui/login_display.cc", "login/ui/login_display.h", "login/ui/login_display_host.cc", @@ -1791,8 +1791,6 @@ "power/renderer_freezer.h", "preferences.cc", "preferences.h", - "prefs/pref_connector_service.cc", - "prefs/pref_connector_service.h", "printing/automatic_usb_printer_configurer.cc", "printing/automatic_usb_printer_configurer.h", "printing/bulk_printers_calculator.cc", @@ -2768,20 +2766,6 @@ generate_python = false } -source_set("ash_pref_connector_manifest") { - sources = [ - "prefs/ash_pref_connector_manifest.cc", - "prefs/ash_pref_connector_manifest.h", - ] - - deps = [ - "//ash/public/cpp", - "//base", - "//services/preferences/public/mojom", - "//services/service_manager/public/cpp", - ] -} - device_policy_remover_path = "$target_gen_dir/device_policy_remover.cc" action("device_policy_remover_generate") {
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc index d47be4e..5730af88 100644 --- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc +++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
@@ -15,6 +15,7 @@ #include "components/arc/session/connection_holder.h" #include "components/arc/test/connection_holder_util.h" #include "components/arc/test/fake_app_instance.h" +#include "components/prefs/pref_service.h" #include "services/ws/common/switches.h" namespace extensions {
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc index 69dff306..11395fd2 100644 --- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc +++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc
@@ -46,6 +46,8 @@ // (https://crbug.com/846645). const int kInitialEnrollmentModulusPowerOutdatedServer = 14; +const int kMaxRequestStateKeysTries = 10; + // Maximum time to wait for the auto-enrollment check to reach a decision. // Note that this encompasses all steps |AutoEnrollmentController| performs in // order to determine if the device should be auto-enrolled. @@ -427,6 +429,7 @@ safeguard_timer_.Start(FROM_HERE, kSafeguardTimeout, base::BindRepeating(&AutoEnrollmentController::Timeout, weak_ptr_factory_.GetWeakPtr())); + request_state_keys_tries_ = 0; // The system clock sync state is not known yet, and this // |AutoEnrollmentController| could wait for it if requested. @@ -481,11 +484,6 @@ testing_auto_enrollment_client_factory_ = auto_enrollment_client_factory; } -void AutoEnrollmentController::FireSafeguardTimerForTesting() { - DCHECK(safeguard_timer_.IsRunning()); - safeguard_timer_.FireNow(); -} - AutoEnrollmentController::InitialEnrollmentRequirement AutoEnrollmentController::GetInitialEnrollmentRequirement() { system::StatisticsProvider* provider = @@ -639,6 +637,7 @@ case DeviceSettingsService::OWNERSHIP_NONE: switch (auto_enrollment_check_type_) { case AutoEnrollmentCheckType::kFRE: + ++request_state_keys_tries_; // For FRE, request state keys first. g_browser_process->platform_part() ->browser_policy_connector_chromeos() @@ -675,6 +674,13 @@ if (state_keys.empty()) { LOG(ERROR) << "No state keys available"; if (fre_requirement_ == FRERequirement::kExplicitlyRequired) { + if (request_state_keys_tries_ >= kMaxRequestStateKeysTries) { + if (safeguard_timer_.IsRunning()) + safeguard_timer_.Stop(); + Timeout(); + return; + } + ++request_state_keys_tries_; // Retry to fetch the state keys. For devices where FRE is required to be // checked, we can't proceed with empty state keys. g_browser_process->platform_part()
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h index 9ff003bd..99269ae 100644 --- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h +++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h
@@ -143,8 +143,6 @@ void SetAutoEnrollmentClientFactoryForTesting( policy::AutoEnrollmentClient::Factory* auto_enrollment_client_factory); - void FireSafeguardTimerForTesting(); - private: class SystemClockSyncWaiter; @@ -286,6 +284,9 @@ // it's never set back to |false|. bool system_clock_sync_wait_requested_ = false; + // Keeps track of number of tries to request state keys. + int request_state_keys_tries_ = 0; + // TODO(igorcov): Merge the two weak_ptr factories in one. base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_{ this};
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc index f2a07fe..6ee4121 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc +++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -137,12 +137,6 @@ ->GetStateKeysBroker(); } - void FireSafeguardTimer() { - WizardController::default_controller() - ->GetAutoEnrollmentController() - ->FireSafeguardTimerForTesting(); - } - private: DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentLocalPolicyServer); }; @@ -534,9 +528,6 @@ host()->StartWizard(AutoEnrollmentCheckScreenView::kScreenId); OobeScreenWaiter(AutoEnrollmentCheckScreenView::kScreenId).Wait(); - // Chrome waits for state keys to be available, force a timeout. - FireSafeguardTimer(); - OobeScreenWaiter(ErrorScreenView::kScreenId).Wait(); test::OobeJS().ExpectHasNoClass("allow-guest-signin", {"error-message"}); }
diff --git a/chrome/browser/chromeos/login/error_screen_browsertest.cc b/chrome/browser/chromeos/login/error_screen_browsertest.cc new file mode 100644 index 0000000..956beed --- /dev/null +++ b/chrome/browser/chromeos/login/error_screen_browsertest.cc
@@ -0,0 +1,170 @@ +// 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 "base/run_loop.h" +#include "base/strings/strcat.h" +#include "base/test/bind_test_util.h" +#include "chrome/browser/chromeos/login/login_wizard.h" +#include "chrome/browser/chromeos/login/screens/error_screen.h" +#include "chrome/browser/chromeos/login/test/js_checker.h" +#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h" +#include "chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chromeos/dbus/shill/shill_profile_client.h" +#include "chromeos/network/network_state_test_helper.h" +#include "third_party/cros_system_api/dbus/shill/dbus-constants.h" + +namespace chromeos { + +namespace { + +constexpr char kWifiServiceName[] = "stub_wifi"; +constexpr char kWifiNetworkName[] = "wifi-test-network"; + +ErrorScreen* GetScreen() { + return static_cast<ErrorScreen*>( + WizardController::default_controller()->GetScreen( + ErrorScreenView::kScreenId)); +} + +} // namespace + +class NetworkErrorScreenTest : public InProcessBrowserTest { + public: + NetworkErrorScreenTest() = default; + ~NetworkErrorScreenTest() override = default; + + void SetUpOnMainThread() override { + network_helper_ = std::make_unique<NetworkStateTestHelper>( + /*use_default_devices_and_services=*/false); + InProcessBrowserTest::SetUpOnMainThread(); + + ShowLoginWizard(WelcomeView::kScreenId); + OobeScreenWaiter(WelcomeView::kScreenId).Wait(); + } + + void ShowErrorScreenWithNetworkList() { + // The only reason we set UI state to UI_STATE_UPDATE is to show a list + // of networks on the error screen. There are other UI states that show + // the network list, picked one arbitrarily. + GetScreen()->SetUIState(NetworkError::UI_STATE_UPDATE); + + GetScreen()->Show(); + + // Wait until network list adds the wifi test network. + test::OobeJS() + .CreateWaiter(WifiElementSelector(kWifiNetworkName) + " != null") + ->Wait(); + } + + void TearDownOnMainThread() override { + network_helper_.reset(); + InProcessBrowserTest::TearDownOnMainThread(); + } + + protected: + std::string WifiElementSelector(const std::string& wifi_network_name) { + return base::StrCat( + {"$('offline-network-control').$$('#networkSelect')" + ".getNetworkListForTest()" + ".querySelector('cr-network-list-item[aria-label=\"", + wifi_network_name, "\"]')"}); + } + + void ClickOnWifiNetwork(const std::string& wifi_network_name) { + test::OobeJS().Evaluate(WifiElementSelector(wifi_network_name) + + ".click()"); + } + + void SetUpDisconnectedWifiNetwork() { + network_helper_->device_test()->ClearDevices(); + network_helper_->service_test()->ClearServices(); + + network_helper_->device_test()->AddDevice( + "/device/stub_wifi_device", shill::kTypeWifi, "stub_wifi_device"); + network_helper_->service_test()->AddService( + kWifiServiceName, "wifi_guid", kWifiNetworkName, shill::kTypeWifi, + shill::kStateDisconnect, true); + network_helper_->service_test()->SetServiceProperty( + kWifiServiceName, shill::kConnectableProperty, base::Value(true)); + network_helper_->profile_test()->AddService( + ShillProfileClient::GetSharedProfilePath(), kWifiServiceName); + + // Network modification notifications are posted asynchronously. Wait until + // idle to ensure observers are notified. + base::RunLoop().RunUntilIdle(); + } + + private: + std::unique_ptr<NetworkStateTestHelper> network_helper_; + + DISALLOW_COPY_AND_ASSIGN(NetworkErrorScreenTest); +}; + +// Test that the network list contains the fake wifi network. +IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, ShowsNetwork) { + SetUpDisconnectedWifiNetwork(); + ShowErrorScreenWithNetworkList(); + + test::OobeJS() + .CreateWaiter(WifiElementSelector(kWifiNetworkName) + ".hidden == false") + ->Wait(); +} + +// Test that error screen hides when a network is connected and that showing and +// hiding the error screen does not modify WizardController's current_screen. +IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, SelectNetwork) { + SetUpDisconnectedWifiNetwork(); + EXPECT_EQ( + WizardController::default_controller()->current_screen()->screen_id(), + WelcomeView::kScreenId); + + ShowErrorScreenWithNetworkList(); + EXPECT_EQ( + WizardController::default_controller()->current_screen()->screen_id(), + WelcomeView::kScreenId); + + // Go back to welcome screen after hiding the error screen. + GetScreen()->SetParentScreen(WelcomeView::kScreenId); + ClickOnWifiNetwork(kWifiNetworkName); + + OobeScreenWaiter welecome_screen_waiter(WelcomeView::kScreenId); + welecome_screen_waiter.set_assert_next_screen(); + welecome_screen_waiter.Wait(); + + EXPECT_EQ( + WizardController::default_controller()->current_screen()->screen_id(), + WelcomeView::kScreenId); +} + +// Test ConnectRequestCallback is called when connecting to a network. +IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, ConnectRequestCallback) { + SetUpDisconnectedWifiNetwork(); + + bool callback_called = false; + auto subscription = GetScreen()->RegisterConnectRequestCallback( + base::BindLambdaForTesting([&]() { callback_called = true; })); + + ShowErrorScreenWithNetworkList(); + ClickOnWifiNetwork(kWifiNetworkName); + + EXPECT_TRUE(callback_called); +} + +// Test HideCallback is called after screen hides. +IN_PROC_BROWSER_TEST_F(NetworkErrorScreenTest, HideCallback) { + bool callback_called = false; + GetScreen()->SetHideCallback( + base::BindLambdaForTesting([&]() { callback_called = true; })); + + GetScreen()->Show(); + OobeScreenWaiter(ErrorScreenView::kScreenId).Wait(); + GetScreen()->Hide(); + + EXPECT_TRUE(callback_called); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/error_screen.cc b/chrome/browser/chromeos/login/screens/error_screen.cc index 76643b8..d8acc5bf 100644 --- a/chrome/browser/chromeos/login/screens/error_screen.cc +++ b/chrome/browser/chromeos/login/screens/error_screen.cc
@@ -72,6 +72,8 @@ constexpr const char ErrorScreen::kUserActionRebootButtonClicked[] = "reboot"; constexpr const char ErrorScreen::kUserActionShowCaptivePortalClicked[] = "show-captive-portal"; +constexpr const char ErrorScreen::kUserActionNetworkConnected[] = + "network-connected"; ErrorScreen::ErrorScreen(ErrorScreenView* view) : BaseScreen(ErrorScreenView::kScreenId), view_(view), weak_factory_(this) { @@ -222,6 +224,8 @@ OnLocalStateErrorPowerwashButtonClicked(); else if (action_id == kUserActionRebootButtonClicked) OnRebootButtonClicked(); + else if (action_id == kUserActionNetworkConnected) + Hide(); else BaseScreen::OnUserAction(action_id); }
diff --git a/chrome/browser/chromeos/login/screens/error_screen.h b/chrome/browser/chromeos/login/screens/error_screen.h index 38c139c..cc35274 100644 --- a/chrome/browser/chromeos/login/screens/error_screen.h +++ b/chrome/browser/chromeos/login/screens/error_screen.h
@@ -40,6 +40,7 @@ static const char kUserActionLocalStateErrorPowerwashButtonClicked[]; static const char kUserActionRebootButtonClicked[]; static const char kUserActionShowCaptivePortalClicked[]; + static const char kUserActionNetworkConnected[]; explicit ErrorScreen(ErrorScreenView* view); ~ErrorScreen() override;
diff --git a/chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.cc b/chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.cc new file mode 100644 index 0000000..c6fb849 --- /dev/null +++ b/chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.cc
@@ -0,0 +1,116 @@ +// 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 "chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h" + +#include <utility> + +#include "ash/public/cpp/kiosk_app_menu.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h" +#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chrome/browser/ui/ash/login_screen_client.h" +#include "content/public/browser/notification_service.h" +#include "extensions/grit/extensions_browser_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image_skia.h" + +namespace chromeos { + +KioskAppMenuController::KioskAppMenuController() { + kiosk_observer_.Add(KioskAppManager::Get()); + arc_kiosk_observer_.Add(ArcKioskAppManager::Get()); +} + +KioskAppMenuController::~KioskAppMenuController() = default; + +void KioskAppMenuController::OnKioskAppDataChanged(const std::string& app_id) { + SendKioskApps(); +} + +void KioskAppMenuController::OnKioskAppDataLoadFailure( + const std::string& app_id) { + SendKioskApps(); +} + +void KioskAppMenuController::OnKioskAppsSettingsChanged() { + SendKioskApps(); +} + +void KioskAppMenuController::OnArcKioskAppsChanged() { + SendKioskApps(); +} + +void KioskAppMenuController::SendKioskApps() { + if (!LoginScreenClient::HasInstance()) + return; + + std::vector<ash::KioskAppMenuEntry> output; + + std::vector<KioskAppManager::App> apps; + KioskAppManager::Get()->GetApps(&apps); + for (const auto& app : apps) { + ash::KioskAppMenuEntry menu_entry; + menu_entry.app_id = app.app_id; + menu_entry.name = base::UTF8ToUTF16(app.name); + if (app.icon.isNull()) { + menu_entry.icon = *ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_APP_DEFAULT_ICON) + .ToImageSkia(); + } else { + menu_entry.icon = app.icon; + } + output.push_back(std::move(menu_entry)); + } + + std::vector<ArcKioskAppData*> arc_apps; + ArcKioskAppManager::Get()->GetAllApps(&arc_apps); + for (ArcKioskAppData* app : arc_apps) { + ash::KioskAppMenuEntry menu_entry; + menu_entry.account_id = app->account_id(); + menu_entry.name = base::UTF8ToUTF16(app->name()); + if (app->icon().isNull()) { + menu_entry.icon = *ui::ResourceBundle::GetSharedInstance() + .GetImageNamed(IDR_APP_DEFAULT_ICON) + .ToImageSkia(); + } else { + menu_entry.icon = app->icon(); + } + output.push_back(std::move(menu_entry)); + } + ash::KioskAppMenu::Get()->SetKioskApps( + output, base::BindRepeating(&KioskAppMenuController::LaunchApp, + weak_factory_.GetWeakPtr())); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_KIOSK_APPS_LOADED, + content::NotificationService::AllSources(), + content::NotificationService::NoDetails()); + + KioskAppLaunchError::Error error = KioskAppLaunchError::Get(); + if (error == KioskAppLaunchError::NONE) + return; + + // Clear any old pending Kiosk launch errors + KioskAppLaunchError::RecordMetricAndClear(); + + LoginScreenClient::Get()->login_screen()->ShowKioskAppError( + KioskAppLaunchError::GetErrorMessage(error)); +} + +void KioskAppMenuController::LaunchApp(const ash::KioskAppMenuEntry& app) { + auto* host = chromeos::LoginDisplayHost::default_host(); + if (!app.app_id.empty()) + host->StartAppLaunch(app.app_id, false, false); + else if (app.account_id.is_valid()) + host->StartArcKiosk(app.account_id); + else + NOTREACHED(); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h b/chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h similarity index 61% rename from chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h rename to chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h index af668605..710b7164 100644 --- a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h +++ b/chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h
@@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_UPDATER_H_ -#define CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_UPDATER_H_ +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_ +#include "ash/public/cpp/kiosk_app_menu.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/scoped_observer.h" @@ -15,12 +16,12 @@ namespace chromeos { // Observer class to update the Kiosk app menu when Kiosk app data is changed. -class KioskAppMenuUpdater +class KioskAppMenuController : public KioskAppManagerObserver, public ArcKioskAppManager::ArcKioskAppManagerObserver { public: - KioskAppMenuUpdater(); - ~KioskAppMenuUpdater() override; + KioskAppMenuController(); + ~KioskAppMenuController() override; // Manually dispatch kiosk app data to Ash. void SendKioskApps(); @@ -34,17 +35,17 @@ void OnArcKioskAppsChanged() override; private: - // Mojo SendKioskApps() callback. - void OnKioskAppsSet(bool success); + void LaunchApp(const ash::KioskAppMenuEntry& app); - ScopedObserver<KioskAppManager, KioskAppMenuUpdater> kiosk_observer_; - ScopedObserver<ArcKioskAppManager, KioskAppMenuUpdater> arc_kiosk_observer_; + ScopedObserver<KioskAppManager, KioskAppMenuController> kiosk_observer_{this}; + ScopedObserver<ArcKioskAppManager, KioskAppMenuController> + arc_kiosk_observer_{this}; - base::WeakPtrFactory<KioskAppMenuUpdater> weak_factory_{this}; + base::WeakPtrFactory<KioskAppMenuController> weak_factory_{this}; - DISALLOW_COPY_AND_ASSIGN(KioskAppMenuUpdater); + DISALLOW_COPY_AND_ASSIGN(KioskAppMenuController); }; } // namespace chromeos -#endif // CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_UPDATER_H_ +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc b/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc deleted file mode 100644 index 3248a54..0000000 --- a/chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.cc +++ /dev/null
@@ -1,113 +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 "chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h" - -#include <utility> - -#include "ash/public/interfaces/kiosk_app_info.mojom.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/browser/chrome_notification_types.h" -#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_data.h" -#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h" -#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" -#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" -#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h" -#include "chrome/browser/ui/ash/login_screen_client.h" -#include "content/public/browser/notification_service.h" -#include "extensions/grit/extensions_browser_resources.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/image/image_skia.h" - -namespace chromeos { - -KioskAppMenuUpdater::KioskAppMenuUpdater() - : kiosk_observer_(this), arc_kiosk_observer_(this) { - kiosk_observer_.Add(KioskAppManager::Get()); - arc_kiosk_observer_.Add(ArcKioskAppManager::Get()); -} - -KioskAppMenuUpdater::~KioskAppMenuUpdater() = default; - -void KioskAppMenuUpdater::OnKioskAppDataChanged(const std::string& app_id) { - SendKioskApps(); -} - -void KioskAppMenuUpdater::OnKioskAppDataLoadFailure(const std::string& app_id) { - SendKioskApps(); -} - -void KioskAppMenuUpdater::OnKioskAppsSettingsChanged() { - SendKioskApps(); -} - -void KioskAppMenuUpdater::OnArcKioskAppsChanged() { - SendKioskApps(); -} - -void KioskAppMenuUpdater::OnKioskAppsSet(bool success) { - if (!success) - return; - - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_KIOSK_APPS_LOADED, - content::NotificationService::AllSources(), - content::NotificationService::NoDetails()); -} - -void KioskAppMenuUpdater::SendKioskApps() { - if (!LoginScreenClient::HasInstance()) - return; - - std::vector<ash::mojom::KioskAppInfoPtr> output; - - std::vector<KioskAppManager::App> apps; - KioskAppManager::Get()->GetApps(&apps); - for (const auto& app : apps) { - auto mojo_app = ash::mojom::KioskAppInfo::New(); - mojo_app->identifier = ash::mojom::KioskAppIdentifier::New(); - mojo_app->identifier->set_app_id(app.app_id); - mojo_app->name = base::UTF8ToUTF16(app.name); - if (app.icon.isNull()) { - mojo_app->icon = *ui::ResourceBundle::GetSharedInstance() - .GetImageNamed(IDR_APP_DEFAULT_ICON) - .ToImageSkia(); - } else { - mojo_app->icon = gfx::ImageSkia(app.icon); - } - output.push_back(std::move(mojo_app)); - } - - std::vector<ArcKioskAppData*> arc_apps; - ArcKioskAppManager::Get()->GetAllApps(&arc_apps); - for (ArcKioskAppData* app : arc_apps) { - auto mojo_app = ash::mojom::KioskAppInfo::New(); - mojo_app->identifier = ash::mojom::KioskAppIdentifier::New(); - mojo_app->identifier->set_account_id(app->account_id()); - mojo_app->name = base::UTF8ToUTF16(app->name()); - if (app->icon().isNull()) { - mojo_app->icon = *ui::ResourceBundle::GetSharedInstance() - .GetImageNamed(IDR_APP_DEFAULT_ICON) - .ToImageSkia(); - } else { - mojo_app->icon = gfx::ImageSkia(app->icon()); - } - output.push_back(std::move(mojo_app)); - } - LoginScreenClient::Get()->login_screen()->SetKioskApps( - std::move(output), base::BindOnce(&KioskAppMenuUpdater::OnKioskAppsSet, - weak_factory_.GetWeakPtr())); - - KioskAppLaunchError::Error error = KioskAppLaunchError::Get(); - if (error == KioskAppLaunchError::NONE) - return; - - // Clear any old pending Kiosk launch errors - KioskAppLaunchError::RecordMetricAndClear(); - - LoginScreenClient::Get()->login_screen()->ShowKioskAppError( - KioskAppLaunchError::GetErrorMessage(error)); -} - -} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.cc b/chrome/browser/chromeos/login/ui/login_display_host_common.cc index 7c57155a..fa094b4b 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_common.cc +++ b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
@@ -269,7 +269,7 @@ } void LoginDisplayHostCommon::OnStartSignInScreenCommon() { - kiosk_updater_.SendKioskApps(); + kiosk_app_menu_controller_.SendKioskApps(); } void LoginDisplayHostCommon::ShowGaiaDialogCommon(
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.h b/chrome/browser/chromeos/login/ui/login_display_host_common.h index 0fa53d7..f734830 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_common.h +++ b/chrome/browser/chromeos/login/ui/login_display_host_common.h
@@ -9,7 +9,7 @@ #include <string> #include <vector> -#include "chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h" +#include "chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h" #include "chrome/browser/chromeos/login/ui/login_display_host.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -113,7 +113,7 @@ // Called after host deletion. std::vector<base::OnceClosure> completion_callbacks_; - KioskAppMenuUpdater kiosk_updater_; + KioskAppMenuController kiosk_app_menu_controller_; base::WeakPtrFactory<LoginDisplayHostCommon> weak_factory_;
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc index dbabd6a..d6a3fde8 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc +++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -28,7 +28,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" -#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/base/locale_util.h" #include "chrome/browser/chromeos/boot_times_recorder.h" #include "chrome/browser/chromeos/first_run/drive_first_run_controller.h"
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.h b/chrome/browser/chromeos/login/ui/login_display_host_webui.h index 2fd0833..9af129f 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_webui.h +++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.h
@@ -17,7 +17,6 @@ #include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/oobe_configuration.h" #include "chrome/browser/chromeos/login/signin_screen_controller.h" -#include "chrome/browser/chromeos/login/ui/kiosk_app_menu_updater.h" #include "chrome/browser/chromeos/login/ui/login_display.h" #include "chrome/browser/chromeos/login/ui/login_display_host_common.h" #include "chrome/browser/chromeos/login/wizard_controller.h" @@ -273,9 +272,6 @@ // True if we need to play startup sound when audio device becomes available. bool need_to_play_startup_sound_ = false; - // Updates shelf kiosk app list. - KioskAppMenuUpdater kiosk_updater_; - base::WeakPtrFactory<LoginDisplayHostWebUI> weak_factory_; DISALLOW_COPY_AND_ASSIGN(LoginDisplayHostWebUI);
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc index f0caee55..421708a 100644 --- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc +++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -275,7 +275,7 @@ } void OobeUIDialogDelegate::ShowFullScreen() { - const gfx::Size& size = + const gfx::Size size = display::Screen::GetScreen() ->GetDisplayNearestWindow(dialog_widget_->GetNativeWindow()) .size();
diff --git a/chrome/browser/chromeos/policy/device_native_printers_handler.cc b/chrome/browser/chromeos/policy/device_native_printers_handler.cc index 423e1c80..f158ab5 100644 --- a/chrome/browser/chromeos/policy/device_native_printers_handler.cc +++ b/chrome/browser/chromeos/policy/device_native_printers_handler.cc
@@ -7,6 +7,7 @@ #include <utility> #include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h" #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h" #include "components/policy/policy_constants.h"
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc index 15ccecbd..d4a7397 100644 --- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc +++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc
@@ -176,13 +176,6 @@ chromeos::power::auto_screen_brightness::MetricsReporter:: RegisterLocalStatePrefs(registry.get()); - // Same default values as used in the actual pref store. - registry->RegisterIntegerPref(ash::prefs::kPowerAcScreenBrightnessPercent, - -1, PrefRegistry::PUBLIC); - registry->RegisterIntegerPref( - ash::prefs::kPowerBatteryScreenBrightnessPercent, -1, - PrefRegistry::PUBLIC); - sync_preferences::PrefServiceSyncable* regular_prefs = factory.CreateSyncable(registry.get()).release();
diff --git a/chrome/browser/chromeos/prefs/OWNERS b/chrome/browser/chromeos/prefs/OWNERS deleted file mode 100644 index ba87d8d6..0000000 --- a/chrome/browser/chromeos/prefs/OWNERS +++ /dev/null
@@ -1,4 +0,0 @@ -per-file ash_pref_connector_manifest.cc=set noparent -per-file ash_pref_connector_manifest.cc=file://ipc/SECURITY_OWNERS -per-file ash_pref_connector_manifest.h=set noparent -per-file ash_pref_connector_manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.cc b/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.cc deleted file mode 100644 index cea064d..0000000 --- a/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chromeos/prefs/ash_pref_connector_manifest.h" - -#include "ash/public/interfaces/pref_connector.mojom.h" -#include "base/no_destructor.h" -#include "services/preferences/public/mojom/preferences.mojom.h" -#include "services/service_manager/public/cpp/manifest_builder.h" - -const service_manager::Manifest& GetAshPrefConnectorManifest() { - static base::NoDestructor<service_manager::Manifest> manifest{ - service_manager::ManifestBuilder() - .WithServiceName(ash::mojom::kPrefConnectorServiceName) - .WithDisplayName("Ash Pref Connector") - .ExposeCapability("pref_connector", - service_manager::Manifest::InterfaceList< - ash::mojom::PrefConnector>()) - .RequireCapability(prefs::mojom::kServiceName, "pref_client") - .Build()}; - return *manifest; -}
diff --git a/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.h b/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.h deleted file mode 100644 index 924d7cb..0000000 --- a/chrome/browser/chromeos/prefs/ash_pref_connector_manifest.h +++ /dev/null
@@ -1,12 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_CHROMEOS_PREFS_ASH_PREF_CONNECTOR_MANIFEST_H_ -#define CHROME_BROWSER_CHROMEOS_PREFS_ASH_PREF_CONNECTOR_MANIFEST_H_ - -#include "services/service_manager/public/cpp/manifest.h" - -const service_manager::Manifest& GetAshPrefConnectorManifest(); - -#endif // CHROME_BROWSER_CHROMEOS_PREFS_ASH_PREF_CONNECTOR_MANIFEST_H_
diff --git a/chrome/browser/chromeos/prefs/pref_connector_service.cc b/chrome/browser/chromeos/prefs/pref_connector_service.cc deleted file mode 100644 index 488f2a8..0000000 --- a/chrome/browser/chromeos/prefs/pref_connector_service.cc +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2017 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/prefs/pref_connector_service.h" - -#include "base/bind.h" -#include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "components/user_manager/user_manager.h" -#include "content/public/browser/browser_context.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "services/service_manager/public/cpp/connector.h" - -AshPrefConnector::AshPrefConnector( - service_manager::mojom::ServiceRequest request) - : service_binding_(this, std::move(request)) { - registry_.AddInterface<ash::mojom::PrefConnector>(base::Bind( - &AshPrefConnector::BindConnectorRequest, base::Unretained(this))); -} - -AshPrefConnector::~AshPrefConnector() = default; - -void AshPrefConnector::GetPrefStoreConnectorForSigninScreen( - prefs::mojom::PrefStoreConnectorRequest request) { - // The signin screen profile is incognito and is not associated with a - // specific user. - Profile* profile = chromeos::ProfileHelper::Get()->GetSigninProfile(); - DCHECK(profile->IsOffTheRecord()); - content::BrowserContext::GetConnectorFor(profile)->BindInterface( - prefs::mojom::kServiceName, std::move(request)); -} - -void AshPrefConnector::GetPrefStoreConnectorForUser( - const AccountId& account_id, - prefs::mojom::PrefStoreConnectorRequest request) { - user_manager::User* user = - user_manager::UserManager::Get()->FindUserAndModify(account_id); - if (!user) - return; - - Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user); - if (!profile) { - user->AddProfileCreatedObserver(base::BindOnce( - &AshPrefConnector::GetPrefStoreConnectorForUser, - weak_factory_.GetWeakPtr(), account_id, std::move(request))); - return; - } - - content::BrowserContext::GetConnectorFor(profile)->BindInterface( - prefs::mojom::kServiceName, std::move(request)); -} - -void AshPrefConnector::BindConnectorRequest( - ash::mojom::PrefConnectorRequest request) { - connector_bindings_.AddBinding(this, std::move(request)); -} - -void AshPrefConnector::OnStart() {} - -void AshPrefConnector::OnBindInterface( - const service_manager::BindSourceInfo& source_info, - const std::string& interface_name, - mojo::ScopedMessagePipeHandle interface_pipe) { - registry_.BindInterface(interface_name, std::move(interface_pipe)); -}
diff --git a/chrome/browser/chromeos/prefs/pref_connector_service.h b/chrome/browser/chromeos/prefs/pref_connector_service.h deleted file mode 100644 index 3f5a0ac..0000000 --- a/chrome/browser/chromeos/prefs/pref_connector_service.h +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright 2017 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_PREFS_PREF_CONNECTOR_SERVICE_H_ -#define CHROME_BROWSER_CHROMEOS_PREFS_PREF_CONNECTOR_SERVICE_H_ - -#include <vector> - -#include "ash/public/interfaces/pref_connector.mojom.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "components/prefs/pref_value_store.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "services/preferences/public/mojom/preferences.mojom.h" -#include "services/service_manager/public/cpp/binder_registry.h" -#include "services/service_manager/public/cpp/service.h" -#include "services/service_manager/public/cpp/service_binding.h" -#include "services/service_manager/public/mojom/service.mojom.h" - -// A |ash::mojom::PrefConnector| implementation that provides ash with access to -// a |prefs::mojom::PrefStoreConnector| for a requested profile. -// -// TODO(http://crbug.com/705347): Once mash can connect to per-profile services -// via service manager, remove this class and the ash_pref_connector service. -class AshPrefConnector : public ash::mojom::PrefConnector, - public service_manager::Service { - public: - explicit AshPrefConnector(service_manager::mojom::ServiceRequest request); - ~AshPrefConnector() override; - - private: - // ash::mojom::PrefConnector: - void GetPrefStoreConnectorForSigninScreen( - prefs::mojom::PrefStoreConnectorRequest request) override; - void GetPrefStoreConnectorForUser( - const AccountId& account_id, - prefs::mojom::PrefStoreConnectorRequest request) override; - - // service_manager::Service: - void OnStart() override; - void OnBindInterface(const service_manager::BindSourceInfo& source_info, - const std::string& interface_name, - mojo::ScopedMessagePipeHandle interface_pipe) override; - - void BindConnectorRequest(ash::mojom::PrefConnectorRequest request); - - prefs::mojom::PrefStoreConnector& GetPrefStoreConnector(); - - service_manager::ServiceBinding service_binding_; - service_manager::BinderRegistry registry_; - mojo::BindingSet<ash::mojom::PrefConnector> connector_bindings_; - - base::WeakPtrFactory<AshPrefConnector> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(AshPrefConnector); -}; - -#endif // CHROME_BROWSER_CHROMEOS_PREFS_PREF_CONNECTOR_SERVICE_H_
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator.h b/chrome/browser/chromeos/printing/bulk_printers_calculator.h index 3bf1bc9..eda44d8 100644 --- a/chrome/browser/chromeos/printing/bulk_printers_calculator.h +++ b/chrome/browser/chromeos/printing/bulk_printers_calculator.h
@@ -12,10 +12,11 @@ #include <vector> #include "base/memory/weak_ptr.h" -#include "chromeos/printing/printer_configuration.h" namespace chromeos { +class Printer; + // Calculates a list of available printers from four policies: Data (json with // all printers), AccessMode (see below), Whitelist and Blacklist (lists with // ids). All methods must be called from the same sequence and all observers'
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc index 110b4db..d8197fb 100644 --- a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc +++ b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.cc
@@ -4,12 +4,11 @@ #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h" -#include <memory> - #include "base/lazy_instance.h" #include "chrome/browser/chromeos/printing/bulk_printers_calculator.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" +#include "components/account_id/account_id.h" #include "components/user_manager/user.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h index 2755ac7c..5018162 100644 --- a/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h +++ b/chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h
@@ -12,13 +12,14 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" -#include "chrome/browser/chromeos/printing/bulk_printers_calculator.h" -#include "components/account_id/account_id.h" +class AccountId; class Profile; namespace chromeos { +class BulkPrintersCalculator; + // Dispenses BulkPrintersCalculator objects based on account id. Access to this // object should be sequenced. class BulkPrintersCalculatorFactory {
diff --git a/chrome/browser/chromeos/printing/calculators_policies_binder.cc b/chrome/browser/chromeos/printing/calculators_policies_binder.cc index f21f408..8a5f474 100644 --- a/chrome/browser/chromeos/printing/calculators_policies_binder.cc +++ b/chrome/browser/chromeos/printing/calculators_policies_binder.cc
@@ -11,8 +11,11 @@ #include "chrome/browser/chromeos/printing/bulk_printers_calculator.h" #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "chromeos/settings/cros_settings_names.h" +#include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/chromeos/printing/calculators_policies_binder.h b/chrome/browser/chromeos/printing/calculators_policies_binder.h index 926e840a0..8cade172 100644 --- a/chrome/browser/chromeos/printing/calculators_policies_binder.h +++ b/chrome/browser/chromeos/printing/calculators_policies_binder.h
@@ -8,12 +8,17 @@ #include <memory> #include "base/macros.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/profiles/profile.h" -#include "components/pref_registry/pref_registry_syncable.h" + +class Profile; + +namespace user_prefs { +class PrefRegistrySyncable; +} namespace chromeos { +class CrosSettings; + // Observes device settings & user profile modifications and propagates them to // BulkPrintersCalculator objects associated with given device context and user // profile. All methods must be called from the same sequence (UI).
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager.cc b/chrome/browser/chromeos/printing/cups_print_job_manager.cc index 138cccda..ddde6455 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_manager.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_manager.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include "base/metrics/histogram_macros.h" +#include "chrome/browser/chromeos/printing/cups_print_job.h" #include "chrome/browser/chromeos/printing/cups_print_job_notification_manager.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager.h b/chrome/browser/chromeos/printing/cups_print_job_manager.h index 90a1750..f9ad6cf5 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_manager.h +++ b/chrome/browser/chromeos/printing/cups_print_job_manager.h
@@ -12,13 +12,13 @@ #include "base/observer_list.h" #include "base/time/time.h" -#include "chrome/browser/chromeos/printing/cups_print_job.h" #include "components/keyed_service/core/keyed_service.h" class Profile; namespace chromeos { +class CupsPrintJob; class CupsPrintJobNotificationManager; class CupsPrintJobManager : public KeyedService {
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc index efa5cac..81f663a 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/chromeos/printing/cups_print_job_manager.h" #include <cups/cups.h> -#include <algorithm> #include <set> #include <string> #include <utility> @@ -15,7 +14,6 @@ #include "base/compiler_specific.h" #include "base/metrics/histogram_macros.h" #include "base/sequenced_task_runner.h" -#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" #include "base/task_runner_util.h"
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.cc b/chrome/browser/chromeos/printing/cups_print_job_notification.cc index 16af6f2..d132c75 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_notification.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
@@ -4,10 +4,6 @@ #include "chrome/browser/chromeos/printing/cups_print_job_notification.h" -#include <memory> -#include <string> -#include <vector> - #include "ash/public/cpp/notification_utils.h" #include "ash/public/cpp/vector_icons/vector_icons.h" #include "base/strings/string16.h"
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification_manager.cc b/chrome/browser/chromeos/printing/cups_print_job_notification_manager.cc index 98cee8c..101c5cd 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_notification_manager.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_notification_manager.cc
@@ -5,6 +5,8 @@ #include "chrome/browser/chromeos/printing/cups_print_job_notification_manager.h" #include "chrome/browser/chromeos/printing/cups_print_job.h" +#include "chrome/browser/chromeos/printing/cups_print_job_manager.h" +#include "chrome/browser/chromeos/printing/cups_print_job_notification.h" #include "chrome/browser/profiles/profile.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification_manager.h b/chrome/browser/chromeos/printing/cups_print_job_notification_manager.h index 15fe14f..d467441 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_notification_manager.h +++ b/chrome/browser/chromeos/printing/cups_print_job_notification_manager.h
@@ -6,18 +6,17 @@ #define CHROME_BROWSER_CHROMEOS_PRINTING_CUPS_PRINT_JOB_NOTIFICATION_MANAGER_H_ #include <memory> -#include <string> #include <unordered_map> #include "base/macros.h" #include "chrome/browser/chromeos/printing/cups_print_job_manager.h" -#include "chrome/browser/chromeos/printing/cups_print_job_notification.h" class Profile; namespace chromeos { class CupsPrintJob; +class CupsPrintJobNotification; class CupsPrintJobNotificationManager : public CupsPrintJobManager::Observer { public:
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc index 5164803..f8b517d 100644 --- a/chrome/browser/chromeos/printing/cups_printers_manager.cc +++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -4,15 +4,10 @@ #include "chrome/browser/chromeos/printing/cups_printers_manager.h" -#include <algorithm> -#include <string> -#include <unordered_map> -#include <unordered_set> +#include <map> #include <utility> -#include <vector> #include "base/bind.h" -#include "base/memory/singleton.h" #include "base/observer_list.h" #include "base/optional.h" #include "base/scoped_observer.h" @@ -22,6 +17,7 @@ #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" #include "chrome/browser/chromeos/printing/ppd_resolution_tracker.h" #include "chrome/browser/chromeos/printing/printer_configurer.h" +#include "chrome/browser/chromeos/printing/printer_event_tracker.h" #include "chrome/browser/chromeos/printing/printer_event_tracker_factory.h" #include "chrome/browser/chromeos/printing/printers_map.h" #include "chrome/browser/chromeos/printing/synced_printers_manager.h"
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.h b/chrome/browser/chromeos/printing/cups_printers_manager.h index 8a45cb8..55768bcf 100644 --- a/chrome/browser/chromeos/printing/cups_printers_manager.h +++ b/chrome/browser/chromeos/printing/cups_printers_manager.h
@@ -9,12 +9,11 @@ #include <vector> #include "base/memory/ref_counted.h" -#include "chrome/browser/chromeos/printing/printer_event_tracker.h" #include "chrome/browser/chromeos/printing/printer_installation_manager.h" #include "chromeos/printing/printer_configuration.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/prefs/pref_service.h" +class PrefService; class Profile; namespace user_prefs {
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc index b4ef9e4..796549e8 100644 --- a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc +++ b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
@@ -19,12 +19,14 @@ #include "base/test/scoped_task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" #include "chrome/browser/chromeos/printing/printer_configurer.h" +#include "chrome/browser/chromeos/printing/printer_event_tracker.h" #include "chrome/browser/chromeos/printing/printers_map.h" #include "chrome/browser/chromeos/printing/synced_printers_manager.h" #include "chrome/browser/chromeos/printing/usb_printer_detector.h" #include "chrome/browser/chromeos/printing/usb_printer_notification_controller.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" #include "components/sync_preferences/testing_pref_service_syncable.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc index 5f42b97..27a4ba57 100644 --- a/chrome/browser/chromeos/printing/enterprise_printers_provider.cc +++ b/chrome/browser/chromeos/printing/enterprise_printers_provider.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/chromeos/printing/enterprise_printers_provider.h" -#include <list> #include <vector> #include "base/feature_list.h" @@ -13,12 +12,15 @@ #include "chrome/browser/chromeos/printing/bulk_printers_calculator.h" #include "chrome/browser/chromeos/printing/bulk_printers_calculator_factory.h" #include "chrome/browser/chromeos/printing/calculators_policies_binder.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" +#include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_translator.h" #include "components/policy/core/common/policy_service.h" #include "components/policy/policy_constants.h" +#include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/chromeos/printing/enterprise_printers_provider.h b/chrome/browser/chromeos/printing/enterprise_printers_provider.h index cdd4210f..af50e16 100644 --- a/chrome/browser/chromeos/printing/enterprise_printers_provider.h +++ b/chrome/browser/chromeos/printing/enterprise_printers_provider.h
@@ -10,13 +10,17 @@ #include <unordered_map> #include "base/macros.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/profiles/profile.h" -#include "chromeos/printing/printer_configuration.h" -#include "components/pref_registry/pref_registry_syncable.h" + +namespace user_prefs { +class PrefRegistrySyncable; +} namespace chromeos { +class CrosSettings; +class Printer; + // Uses classes BulkPrintersCalculator and CalculatorsPoliciesBinder to track // device settings & user profile modifications and to calculates resultant // list of available enterprise printers. The final list of available enterprise
diff --git a/chrome/browser/chromeos/printing/fake_cups_print_job_manager.cc b/chrome/browser/chromeos/printing/fake_cups_print_job_manager.cc index e416176..cfe28410 100644 --- a/chrome/browser/chromeos/printing/fake_cups_print_job_manager.cc +++ b/chrome/browser/chromeos/printing/fake_cups_print_job_manager.cc
@@ -4,8 +4,6 @@ #include "chrome/browser/chromeos/printing/fake_cups_print_job_manager.h" -#include <memory> -#include <string> #include <utility> #include "base/bind.h"
diff --git a/chrome/browser/chromeos/printing/fake_cups_print_job_manager.h b/chrome/browser/chromeos/printing/fake_cups_print_job_manager.h index 1f6ef43..daaefcd 100644 --- a/chrome/browser/chromeos/printing/fake_cups_print_job_manager.h +++ b/chrome/browser/chromeos/printing/fake_cups_print_job_manager.h
@@ -7,7 +7,6 @@ #include <memory> #include <string> -#include <utility> #include <vector> #include "base/memory/weak_ptr.h"
diff --git a/chrome/browser/chromeos/printing/ppd_provider_factory.cc b/chrome/browser/chromeos/printing/ppd_provider_factory.cc index b6c5dee3..b1ce7bc 100644 --- a/chrome/browser/chromeos/printing/ppd_provider_factory.cc +++ b/chrome/browser/chromeos/printing/ppd_provider_factory.cc
@@ -4,8 +4,6 @@ #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" -#include <memory> - #include "base/files/file_path.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/net/system_network_context_manager.h"
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_state.cc b/chrome/browser/chromeos/printing/ppd_resolution_state.cc index 69c1259c..310838c4 100644 --- a/chrome/browser/chromeos/printing/ppd_resolution_state.cc +++ b/chrome/browser/chromeos/printing/ppd_resolution_state.cc
@@ -4,8 +4,6 @@ #include "chrome/browser/chromeos/printing/ppd_resolution_state.h" -#include <string> - #include "base/logging.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_tracker.cc b/chrome/browser/chromeos/printing/ppd_resolution_tracker.cc index e264108..ef24e46 100644 --- a/chrome/browser/chromeos/printing/ppd_resolution_tracker.cc +++ b/chrome/browser/chromeos/printing/ppd_resolution_tracker.cc
@@ -4,12 +4,8 @@ #include "chrome/browser/chromeos/printing/ppd_resolution_tracker.h" -#include <memory> -#include <string> -#include <unordered_map> -#include <vector> - #include "base/stl_util.h" +#include "chrome/browser/chromeos/printing/ppd_resolution_state.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/ppd_resolution_tracker.h b/chrome/browser/chromeos/printing/ppd_resolution_tracker.h index 6133213..ccdd00980 100644 --- a/chrome/browser/chromeos/printing/ppd_resolution_tracker.h +++ b/chrome/browser/chromeos/printing/ppd_resolution_tracker.h
@@ -5,17 +5,16 @@ #ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PPD_RESOLUTION_TRACKER_H_ #define CHROME_BROWSER_CHROMEOS_PRINTING_PPD_RESOLUTION_TRACKER_H_ -#include <memory> #include <string> #include <unordered_map> -#include <vector> #include "base/macros.h" -#include "chrome/browser/chromeos/printing/ppd_resolution_state.h" #include "chromeos/printing/printer_configuration.h" namespace chromeos { +class PpdResolutionState; + class PpdResolutionTracker { public: PpdResolutionTracker();
diff --git a/chrome/browser/chromeos/printing/printer_configurer.cc b/chrome/browser/chromeos/printing/printer_configurer.cc index bd3710c..4d16cdf 100644 --- a/chrome/browser/chromeos/printing/printer_configurer.cc +++ b/chrome/browser/chromeos/printing/printer_configurer.cc
@@ -5,9 +5,7 @@ #include "chrome/browser/chromeos/printing/printer_configurer.h" #include <map> -#include <memory> #include <set> -#include <string> #include <utility> #include <vector>
diff --git a/chrome/browser/chromeos/printing/printer_configurer.h b/chrome/browser/chromeos/printing/printer_configurer.h index c625edb..13f7f3b 100644 --- a/chrome/browser/chromeos/printing/printer_configurer.h +++ b/chrome/browser/chromeos/printing/printer_configurer.h
@@ -10,12 +10,13 @@ #include "base/callback_forward.h" #include "base/macros.h" -#include "chromeos/printing/printer_configuration.h" class Profile; namespace chromeos { +class Printer; + // These values are written to logs. New enum values can be added, but existing // enums must never be renumbered or deleted and reused. enum PrinterSetupResult {
diff --git a/chrome/browser/chromeos/printing/printer_event_tracker.cc b/chrome/browser/chromeos/printing/printer_event_tracker.cc index 0b85173..ac39f843 100644 --- a/chrome/browser/chromeos/printing/printer_event_tracker.cc +++ b/chrome/browser/chromeos/printing/printer_event_tracker.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/printing/printer_event_tracker.h" +#include "chromeos/printing/printer_configuration.h" + namespace chromeos { namespace {
diff --git a/chrome/browser/chromeos/printing/printer_event_tracker.h b/chrome/browser/chromeos/printing/printer_event_tracker.h index a3e7f25..e9cd8bf5 100644 --- a/chrome/browser/chromeos/printing/printer_event_tracker.h +++ b/chrome/browser/chromeos/printing/printer_event_tracker.h
@@ -5,20 +5,19 @@ #ifndef CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_EVENT_TRACKER_H_ #define CHROME_BROWSER_CHROMEOS_PRINTING_PRINTER_EVENT_TRACKER_H_ -#include <stdint.h> - #include <string> #include <vector> #include "base/macros.h" #include "base/synchronization/lock.h" #include "chrome/browser/chromeos/printing/printer_detector.h" -#include "chromeos/printing/printer_configuration.h" #include "components/keyed_service/core/keyed_service.h" #include "third_party/metrics_proto/printer_event.pb.h" namespace chromeos { +class Printer; + // Aggregates printer events for logging. This class is thread-safe. class PrinterEventTracker : public KeyedService { public:
diff --git a/chrome/browser/chromeos/printing/printer_event_tracker_factory.cc b/chrome/browser/chromeos/printing/printer_event_tracker_factory.cc index e822044..059ed40 100644 --- a/chrome/browser/chromeos/printing/printer_event_tracker_factory.cc +++ b/chrome/browser/chromeos/printing/printer_event_tracker_factory.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" -#include "components/keyed_service/content/browser_context_keyed_service_factory.h" namespace chromeos { namespace {
diff --git a/chrome/browser/chromeos/printing/printers_map.h b/chrome/browser/chromeos/printing/printers_map.h index 5eba874..2247da8 100644 --- a/chrome/browser/chromeos/printing/printers_map.h +++ b/chrome/browser/chromeos/printing/printers_map.h
@@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/optional.h" -#include "chrome/browser/chromeos/printing/cups_printers_manager.h" #include "chromeos/printing/printer_configuration.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/printers_sync_bridge.cc b/chrome/browser/chromeos/printing/printers_sync_bridge.cc index 98b37db..6daf6bd 100644 --- a/chrome/browser/chromeos/printing/printers_sync_bridge.cc +++ b/chrome/browser/chromeos/printing/printers_sync_bridge.cc
@@ -4,20 +4,16 @@ #include "chrome/browser/chromeos/printing/printers_sync_bridge.h" -#include <memory> #include <set> -#include <string> #include <utility> -#include <vector> #include "base/bind.h" -#include "base/optional.h" #include "base/stl_util.h" #include "base/task/post_task.h" #include "chrome/browser/chromeos/printing/specifics_translation.h" +#include "chromeos/printing/printer_configuration.h" #include "components/sync/base/report_unrecoverable_error.h" #include "components/sync/model/model_type_change_processor.h" -#include "components/sync/model/model_type_store.h" #include "components/sync/model/mutable_data_batch.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/protocol/model_type_state.pb.h"
diff --git a/chrome/browser/chromeos/printing/specifics_translation.cc b/chrome/browser/chromeos/printing/specifics_translation.cc index a4e55d2..f0bffc6 100644 --- a/chrome/browser/chromeos/printing/specifics_translation.cc +++ b/chrome/browser/chromeos/printing/specifics_translation.cc
@@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <memory> +#include "chrome/browser/chromeos/printing/specifics_translation.h" + #include <string> #include <vector> @@ -10,9 +11,7 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/time/time.h" -#include "chrome/browser/chromeos/printing/specifics_translation.h" #include "chromeos/printing/printer_configuration.h" -#include "components/sync/protocol/printer_specifics.pb.h" namespace chromeos {
diff --git a/chrome/browser/chromeos/printing/specifics_translation.h b/chrome/browser/chromeos/printing/specifics_translation.h index ede9a1707..6f8a129 100644 --- a/chrome/browser/chromeos/printing/specifics_translation.h +++ b/chrome/browser/chromeos/printing/specifics_translation.h
@@ -7,11 +7,12 @@ #include <memory> -#include "chromeos/printing/printer_configuration.h" #include "components/sync/protocol/printer_specifics.pb.h" namespace chromeos { +class Printer; + // Convert |printer| into its local representation. Enforces that only one // field in PpdReference is filled in. In order of preference, we populate // autoconf, user_supplied_ppd_url, or effective_make_and_model.
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.cc b/chrome/browser/chromeos/printing/synced_printers_manager.cc index 39f80370..f9beb54 100644 --- a/chrome/browser/chromeos/printing/synced_printers_manager.cc +++ b/chrome/browser/chromeos/printing/synced_printers_manager.cc
@@ -4,11 +4,8 @@ #include "chrome/browser/chromeos/printing/synced_printers_manager.h" -#include <memory> -#include <string> #include <unordered_map> #include <utility> -#include <vector> #include "base/guid.h" #include "base/observer_list_threadsafe.h" @@ -20,6 +17,7 @@ #include "chrome/browser/chromeos/printing/enterprise_printers_provider.h" #include "chrome/browser/chromeos/printing/printers_sync_bridge.h" #include "chrome/browser/chromeos/printing/specifics_translation.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager.h b/chrome/browser/chromeos/printing/synced_printers_manager.h index 926e7a9c..f33605d 100644 --- a/chrome/browser/chromeos/printing/synced_printers_manager.h +++ b/chrome/browser/chromeos/printing/synced_printers_manager.h
@@ -5,18 +5,15 @@ #ifndef CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_H_ #define CHROME_BROWSER_CHROMEOS_PRINTING_SYNCED_PRINTERS_MANAGER_H_ -#include <map> #include <memory> #include <string> #include <vector> #include "base/memory/weak_ptr.h" #include "base/observer_list.h" -#include "chrome/browser/chromeos/printing/printers_sync_bridge.h" #include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_translator.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/prefs/pref_change_registrar.h" class Profile; @@ -26,6 +23,8 @@ namespace chromeos { +class PrintersSyncBridge; + // Manages information about synced local printers classes (SAVED // and ENTERPRISE). Provides an interface to a user's printers and // printers provided by policy. User printers are backed by the
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc index be87b5b..86bbd0e 100644 --- a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc +++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
@@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/debug/dump_without_crashing.h" #include "chrome/browser/chromeos/printing/printers_sync_bridge.h" +#include "chrome/browser/chromeos/printing/synced_printers_manager.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/model_type_store_service_factory.h"
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_factory.h b/chrome/browser/chromeos/printing/synced_printers_manager_factory.h index c9bd895..60dbc4bf 100644 --- a/chrome/browser/chromeos/printing/synced_printers_manager_factory.h +++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.h
@@ -16,6 +16,8 @@ namespace chromeos { +class SyncedPrintersManager; + class SyncedPrintersManagerFactory : public BrowserContextKeyedServiceFactory { public: static SyncedPrintersManager* GetForBrowserContext(
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc index 4d78384..7362ee2 100644 --- a/chrome/browser/chromeos/printing/usb_printer_detector.cc +++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stdint.h> +#include "chrome/browser/chromeos/printing/usb_printer_detector.h" #include <map> -#include <memory> #include <set> +#include <string> #include <utility> #include <vector> @@ -23,7 +23,6 @@ #include "chrome/browser/chromeos/printing/printer_event_tracker.h" #include "chrome/browser/chromeos/printing/printer_event_tracker_factory.h" #include "chrome/browser/chromeos/printing/synced_printers_manager_factory.h" -#include "chrome/browser/chromeos/printing/usb_printer_detector.h" #include "chrome/browser/chromeos/printing/usb_printer_util.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/debug_daemon_client.h"
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.h b/chrome/browser/chromeos/printing/usb_printer_detector.h index 3d18659..27ce72e 100644 --- a/chrome/browser/chromeos/printing/usb_printer_detector.h +++ b/chrome/browser/chromeos/printing/usb_printer_detector.h
@@ -6,8 +6,6 @@ #define CHROME_BROWSER_CHROMEOS_PRINTING_USB_PRINTER_DETECTOR_H_ #include <memory> -#include <string> -#include <vector> #include "base/macros.h" #include "chrome/browser/chromeos/printing/printer_detector.h"
diff --git a/chrome/browser/chromeos/printing/usb_printer_notification.h b/chrome/browser/chromeos/printing/usb_printer_notification.h index 9d1b4c5..9c5eab45 100644 --- a/chrome/browser/chromeos/printing/usb_printer_notification.h +++ b/chrome/browser/chromeos/printing/usb_printer_notification.h
@@ -11,6 +11,7 @@ #include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/printing/printer_configurer.h" +#include "chromeos/printing/printer_configuration.h" #include "ui/message_center/public/cpp/notification_delegate.h" class Profile;
diff --git a/chrome/browser/chromeos/printing/zeroconf_printer_detector.cc b/chrome/browser/chromeos/printing/zeroconf_printer_detector.cc index efb2322..2abe2ea 100644 --- a/chrome/browser/chromeos/printing/zeroconf_printer_detector.cc +++ b/chrome/browser/chromeos/printing/zeroconf_printer_detector.cc
@@ -4,8 +4,6 @@ #include "chrome/browser/chromeos/printing/zeroconf_printer_detector.h" -#include <map> -#include <string> #include <utility> #include <vector>
diff --git a/chrome/browser/chromeos/shutdown_policy_browsertest.cc b/chrome/browser/chromeos/shutdown_policy_browsertest.cc index 47abdec..b498042 100644 --- a/chrome/browser/chromeos/shutdown_policy_browsertest.cc +++ b/chrome/browser/chromeos/shutdown_policy_browsertest.cc
@@ -191,6 +191,15 @@ ShutdownPolicyBaseTest::TearDownOnMainThread(); } + void WaitForShutdownButtonVisibility(bool visible) { + ScreenLockerTester tester; + int ui_update_count = tester.GetUiUpdateCount(); + while (tester.IsLockShutdownButtonShown() != visible) { + tester.WaitForUiUpdate(ui_update_count); + ui_update_count = tester.GetUiUpdateCount(); + } + } + private: std::unique_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_; @@ -205,19 +214,15 @@ IN_PROC_BROWSER_TEST_F(ShutdownPolicyLockerTest, PolicyChange) { ScreenLockerTester tester; - int ui_update_count = tester.GetUiUpdateCount(); UpdateRebootOnShutdownPolicy(true); RefreshDevicePolicy(); - tester.WaitForUiUpdate(ui_update_count); + WaitForShutdownButtonVisibility(false); EXPECT_TRUE(tester.IsLockRestartButtonShown()); - EXPECT_FALSE(tester.IsLockShutdownButtonShown()); - ui_update_count = tester.GetUiUpdateCount(); UpdateRebootOnShutdownPolicy(false); RefreshDevicePolicy(); - tester.WaitForUiUpdate(ui_update_count); + WaitForShutdownButtonVisibility(true); EXPECT_FALSE(tester.IsLockRestartButtonShown()); - EXPECT_TRUE(tester.IsLockShutdownButtonShown()); } class ShutdownPolicyLoginTest : public ShutdownPolicyBaseTest { @@ -254,6 +259,15 @@ } } + void WaitForShutdownButtonVisibility(bool visible) { + test::LoginScreenTester tester; + int ui_update_count = tester.GetUiUpdateCount(); + while (tester.IsShutdownButtonShown() != visible) { + tester.WaitForUiUpdate(ui_update_count); + ui_update_count = tester.GetUiUpdateCount(); + } + } + private: DISALLOW_COPY_AND_ASSIGN(ShutdownPolicyLoginTest); }; @@ -266,19 +280,15 @@ IN_PROC_BROWSER_TEST_F(ShutdownPolicyLoginTest, PolicyChange) { test::LoginScreenTester tester; - int ui_update_count = tester.GetUiUpdateCount(); UpdateRebootOnShutdownPolicy(true); RefreshDevicePolicy(); - tester.WaitForUiUpdate(ui_update_count); + WaitForShutdownButtonVisibility(false); EXPECT_TRUE(tester.IsRestartButtonShown()); - EXPECT_FALSE(tester.IsShutdownButtonShown()); - ui_update_count = tester.GetUiUpdateCount(); UpdateRebootOnShutdownPolicy(false); RefreshDevicePolicy(); - tester.WaitForUiUpdate(ui_update_count); + WaitForShutdownButtonVisibility(true); EXPECT_FALSE(tester.IsRestartButtonShown()); - EXPECT_TRUE(tester.IsShutdownButtonShown()); } } // namespace chromeos
diff --git a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc index 445fd21..d7e515e6 100644 --- a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc +++ b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
@@ -194,16 +194,17 @@ return is_open; } - void ClickAutoclickOnDetailMenu() { + void ClickVirtualKeyboardOnDetailMenu() { ash::mojom::SystemTrayTestApiAsyncWaiter wait_for(tray_test_api_.get()); - wait_for.ClickBubbleView(ash::VIEW_ID_ACCESSIBILITY_AUTOCLICK); + wait_for.ClickBubbleView(ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD); } - bool IsAutoclickEnabledOnDetailMenu() const { + bool IsVirtualKeyboardEnabledOnDetailMenu() const { ash::mojom::SystemTrayTestApiAsyncWaiter wait_for(tray_test_api_.get()); bool visible = false; - wait_for.IsBubbleViewVisible(ash::VIEW_ID_ACCESSIBILITY_AUTOCLICK_ENABLED, - false /* open_tray */, &visible); + wait_for.IsBubbleViewVisible( + ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED, + false /* open_tray */, &visible); return visible; } @@ -497,19 +498,21 @@ // Verify that the accessiblity system detailed menu remains open when an item // is selected or deselected. +// Do not use a feature which requires an enable/disable confirmation dialog +// here, as the dialogs change focus and close the detail menu. IN_PROC_BROWSER_TEST_P(TrayAccessibilityTest, DetailMenuRemainsOpen) { CreateDetailedMenu(); - ClickAutoclickOnDetailMenu(); - EXPECT_TRUE(IsAutoclickEnabledOnDetailMenu()); + ClickVirtualKeyboardOnDetailMenu(); + EXPECT_TRUE(IsVirtualKeyboardEnabledOnDetailMenu()); { base::RunLoop run_loop; run_loop.RunUntilIdle(); } EXPECT_TRUE(IsBubbleOpen()); - ClickAutoclickOnDetailMenu(); - EXPECT_FALSE(IsAutoclickEnabledOnDetailMenu()); + ClickVirtualKeyboardOnDetailMenu(); + EXPECT_FALSE(IsVirtualKeyboardEnabledOnDetailMenu()); { base::RunLoop run_loop; run_loop.RunUntilIdle();
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc index 1683491..5213cdf4 100644 --- a/chrome/browser/conflicts/module_database_win.cc +++ b/chrome/browser/conflicts/module_database_win.cc
@@ -89,7 +89,9 @@ // static constexpr base::TimeDelta ModuleDatabase::kIdleTimeout; -ModuleDatabase::ModuleDatabase(bool third_party_blocking_policy_enabled) +ModuleDatabase::ModuleDatabase( + std::unique_ptr<service_manager::Connector> connector, + bool third_party_blocking_policy_enabled) : idle_timer_( FROM_HERE, kIdleTimeout, @@ -103,7 +105,8 @@ // ModuleDatabase owns |module_inspector_|, so it is safe to use // base::Unretained(). module_inspector_(base::Bind(&ModuleDatabase::OnModuleInspected, - base::Unretained(this))) { + base::Unretained(this)), + std::move(connector)) { AddObserver(&module_inspector_); AddObserver(&third_party_metrics_);
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h index f2f4d7b..a81bc9e 100644 --- a/chrome/browser/conflicts/module_database_win.h +++ b/chrome/browser/conflicts/module_database_win.h
@@ -17,6 +17,7 @@ #include "chrome/browser/conflicts/module_inspector_win.h" #include "chrome/browser/conflicts/third_party_metrics_recorder_win.h" #include "content/public/common/process_type.h" +#include "services/service_manager/public/cpp/connector.h" class ModuleDatabaseObserver; @@ -58,7 +59,8 @@ // Creates the ModuleDatabase. Must be created and set on the sequence // returned by GetTaskRunner(). - explicit ModuleDatabase(bool third_party_blocking_policy_enabled); + explicit ModuleDatabase(std::unique_ptr<service_manager::Connector> connector, + bool third_party_blocking_policy_enabled); ~ModuleDatabase() override; // Returns the SequencedTaskRunner on which the ModuleDatabase lives. Can be
diff --git a/chrome/browser/conflicts/module_database_win_unittest.cc b/chrome/browser/conflicts/module_database_win_unittest.cc index 8b9b812..574cce2 100644 --- a/chrome/browser/conflicts/module_database_win_unittest.cc +++ b/chrome/browser/conflicts/module_database_win_unittest.cc
@@ -41,6 +41,7 @@ base::test::ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME), scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()), module_database_(std::make_unique<ModuleDatabase>( + nullptr, /* third_party_blocking_policy_enabled = */ false)) {} ~ModuleDatabaseTest() override {
diff --git a/chrome/browser/conflicts/module_inspector_win.cc b/chrome/browser/conflicts/module_inspector_win.cc index 969efba..68ff760 100644 --- a/chrome/browser/conflicts/module_inspector_win.cc +++ b/chrome/browser/conflicts/module_inspector_win.cc
@@ -17,7 +17,6 @@ #include "chrome/common/chrome_paths.h" #include "chrome/services/util_win/public/mojom/constants.mojom.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/service_manager_connection.h" #include "services/service_manager/public/cpp/connector.h" namespace { @@ -76,9 +75,11 @@ constexpr base::TimeDelta ModuleInspector::kFlushInspectionResultsTimerTimeout; ModuleInspector::ModuleInspector( - const OnModuleInspectedCallback& on_module_inspected_callback) + const OnModuleInspectedCallback& on_module_inspected_callback, + std::unique_ptr<service_manager::Connector> connector) : on_module_inspected_callback_(on_module_inspected_callback), is_after_startup_(false), + connector_(std::move(connector)), inspection_task_runner_(base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), @@ -169,16 +170,8 @@ DCHECK(base::FeatureList::IsEnabled(kWinOOPInspectModuleFeature)); // Use the |test_connector_| if set. - service_manager::Connector* connector = test_connector_; - if (!connector) { - // The ServiceManagerConnection can be null during shutdown. - content::ServiceManagerConnection* service_manager_connection = - content::ServiceManagerConnection::GetForProcess(); - if (!service_manager_connection) - return; - - connector = service_manager_connection->GetConnector(); - } + service_manager::Connector* connector = + test_connector_ ? test_connector_ : connector_.get(); connector->BindInterface(chrome::mojom::kUtilWinServiceName, &util_win_ptr_); util_win_ptr_.set_connection_error_handler(
diff --git a/chrome/browser/conflicts/module_inspector_win.h b/chrome/browser/conflicts/module_inspector_win.h index 5a414f5f..181a347 100644 --- a/chrome/browser/conflicts/module_inspector_win.h +++ b/chrome/browser/conflicts/module_inspector_win.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_CONFLICTS_MODULE_INSPECTOR_WIN_H_ #include <map> +#include <memory> #include "base/callback.h" #include "base/containers/queue.h" @@ -61,8 +62,8 @@ base::Callback<void(const ModuleInfoKey& module_key, ModuleInspectionResult inspection_result)>; - explicit ModuleInspector( - const OnModuleInspectedCallback& on_module_inspected_callback); + ModuleInspector(const OnModuleInspectedCallback& on_module_inspected_callback, + std::unique_ptr<service_manager::Connector> connector); ~ModuleInspector() override; // Adds the module to the queue of modules to inspect. Starts the inspection @@ -132,6 +133,9 @@ // inspection tasks in order to not negatively impact startup performance. bool is_after_startup_; + // Allows this class to connect to the UtilWin service. + std::unique_ptr<service_manager::Connector> connector_; + // A pointer to the UtilWin service. Only used if the WinOOPInspectModule // feature is enabled. It is created when inspection is ongoing, and freed // when no longer needed.
diff --git a/chrome/browser/conflicts/module_inspector_win_unittest.cc b/chrome/browser/conflicts/module_inspector_win_unittest.cc index a6a402d..ed1afc5 100644 --- a/chrome/browser/conflicts/module_inspector_win_unittest.cc +++ b/chrome/browser/conflicts/module_inspector_win_unittest.cc
@@ -90,8 +90,10 @@ } // namespace TEST_F(ModuleInspectorTest, OneModule) { - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); module_inspector.AddModule({GetKernel32DllFilePath(), 0, 0}); @@ -107,8 +109,10 @@ {base::FilePath(), 0, 0}, }; - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); for (const auto& module : kTestCases) module_inspector.AddModule(module); @@ -128,8 +132,10 @@ {base::FilePath(), 0, 0}, }; - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); for (const auto& module : kTestCases) module_inspector.AddModule(module); @@ -161,8 +167,10 @@ {base::FilePath(), 0, 0}, }; - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); module_inspector.SetConnectorForTesting( test_connector_factory_.GetDefaultConnector()); @@ -192,8 +200,10 @@ ASSERT_TRUE( CreateInspectionResultsCacheWithEntry(module_key, inspection_result)); - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); module_inspector.AddModule(module_key); @@ -219,8 +229,10 @@ base::ScopedPathOverride scoped_user_data_dir_override( chrome::DIR_USER_DATA, scoped_temp_dir.GetPath()); - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); ModuleInfoKey module_key(GetKernel32DllFilePath(), 0, 0); module_inspector.AddModule(module_key); @@ -258,8 +270,10 @@ base::ScopedPathOverride scoped_user_data_dir_override( chrome::DIR_USER_DATA, scoped_temp_dir.GetPath()); - ModuleInspector module_inspector(base::Bind( - &ModuleInspectorTest::OnModuleInspected, base::Unretained(this))); + ModuleInspector module_inspector( + base::Bind(&ModuleInspectorTest::OnModuleInspected, + base::Unretained(this)), + nullptr); ModuleInfoKey module_key(GetKernel32DllFilePath(), 0, 0); module_inspector.AddModule(module_key);
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc index f850d1b..3596fe5 100644 --- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -26,36 +26,6 @@ const char kFakeClientId[] = "fake-client-id"; const char kFakeMachineNameReport[] = "{\"computername\":\"name\"}"; -class MockCloudPolicyClient : public policy::MockCloudPolicyClient { - public: - explicit MockCloudPolicyClient( - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) - : policy::MockCloudPolicyClient(std::move(url_loader_factory)) {} - - void UploadChromeDesktopReport( - std::unique_ptr<enterprise_management::ChromeDesktopReportRequest> - request, - const StatusCallback& callback) override { - UploadChromeDesktopReportProxy(request.get(), callback); - } - MOCK_METHOD2(UploadChromeDesktopReportProxy, - void(enterprise_management::ChromeDesktopReportRequest*, - const StatusCallback&)); - - void OnReportUploadedFailed(const StatusCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, false)); - } - - void OnReportUploadedSucceeded(const StatusCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, true)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MockCloudPolicyClient); -}; - class FakeBrowserDMTokenStorage : public policy::BrowserDMTokenStorage { public: FakeBrowserDMTokenStorage() = default; @@ -89,7 +59,7 @@ EnterpriseReportingPrivateUploadChromeDesktopReportFunction* function = EnterpriseReportingPrivateUploadChromeDesktopReportFunction:: CreateForTesting(test_url_loader_factory_.GetSafeWeakWrapper()); - auto client = std::make_unique<MockCloudPolicyClient>( + auto client = std::make_unique<policy::MockCloudPolicyClient>( test_url_loader_factory_.GetSafeWeakWrapper()); client_ = client.get(); function->SetCloudPolicyClientForTesting(std::move(client)); @@ -109,7 +79,7 @@ "{\"chromeUserProfileReport\":[{\"chromeSignInUser\":\"Name\"}]}}]"); } - MockCloudPolicyClient* client_; + policy::MockCloudPolicyClient* client_; private: network::TestURLLoaderFactory test_url_loader_factory_; @@ -140,8 +110,7 @@ EXPECT_CALL(*client_, SetupRegistration(kFakeDMToken, kFakeClientId, _)) .Times(1); EXPECT_CALL(*client_, UploadChromeDesktopReportProxy(_, _)) - .WillOnce(WithArgs<1>( - Invoke(client_, &MockCloudPolicyClient::OnReportUploadedFailed))); + .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(false))); ASSERT_EQ(enterprise_reporting::kUploadFailed, RunFunctionAndReturnError(function, GenerateArgs(kFakeMachineNameReport))); @@ -155,8 +124,7 @@ EXPECT_CALL(*client_, SetupRegistration(kFakeDMToken, kFakeClientId, _)) .Times(1); EXPECT_CALL(*client_, UploadChromeDesktopReportProxy(_, _)) - .WillOnce(WithArgs<1>( - Invoke(client_, &MockCloudPolicyClient::OnReportUploadedSucceeded))); + .WillOnce(WithArgs<1>(policy::ScheduleStatusCallback(true))); ASSERT_EQ(nullptr, RunFunctionAndReturnValue( function, GenerateArgs(kFakeMachineNameReport))); ::testing::Mock::VerifyAndClearExpectations(client_);
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc index c14f565..e48990e 100644 --- a/chrome/browser/extensions/updater/extension_updater_unittest.cc +++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -270,7 +270,7 @@ return identity_test_env_.get(); } - const std::string& account_id() { return account_info_.account_id; } + const std::string& account_id() { return account_info_.account_id.id; } // Creates test extensions and inserts them into list. The name and // version are all based on their index. If |update_url| is non-null, it
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h index bc732c4d..496f1e1 100644 --- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h +++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h
@@ -16,7 +16,7 @@ // The FrozenFrameAggregator is responsible for tracking frame frozen states, // and aggregating this property to the page and process nodes. -class FrozenFrameAggregator : public GraphObserver { +class FrozenFrameAggregator : public GraphObserverDefaultImpl { public: struct Data;
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc index 3207e1c..df27098 100644 --- a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc +++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
@@ -20,7 +20,7 @@ using LifecycleState = PageNodeImpl::LifecycleState; -class LenientMockGraphObserver : public GraphObserver { +class LenientMockGraphObserver : public GraphObserverDefaultImpl { public: LenientMockGraphObserver() = default; ~LenientMockGraphObserver() override = default;
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h index 91c17a4..67dfcdc 100644 --- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h +++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h
@@ -19,7 +19,7 @@ // reached an "almost idle" state after initial load, based on CPU and network // quiescence, as well as an absolute timeout. This state is then updated on // PageNodes in a graph. -class PageAlmostIdleDecorator : public GraphObserver { +class PageAlmostIdleDecorator : public GraphObserverDefaultImpl { public: class Data;
diff --git a/chrome/browser/performance_manager/graph/graph_impl.cc b/chrome/browser/performance_manager/graph/graph_impl.cc index 7bb9437..7065250 100644 --- a/chrome/browser/performance_manager/graph/graph_impl.cc +++ b/chrome/browser/performance_manager/graph/graph_impl.cc
@@ -45,7 +45,7 @@ void GraphImpl::RegisterObserver(GraphObserver* observer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - observer->set_node_graph(this); + observer->SetNodeGraph(this); observers_.push_back(observer); observer->OnRegistered(); }
diff --git a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc index 8a638d3f..900c1b46 100644 --- a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc +++ b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc
@@ -20,7 +20,7 @@ namespace { // Observer used to make sure that signals are dispatched correctly. -class SystemAndProcessObserver : public GraphObserver { +class SystemAndProcessObserver : public GraphObserverDefaultImpl { public: // GraphObserver implementation: bool ShouldObserve(const NodeBase* node) override {
diff --git a/chrome/browser/performance_manager/observers/graph_observer.cc b/chrome/browser/performance_manager/observers/graph_observer.cc index b83cf800..a4417a615 100644 --- a/chrome/browser/performance_manager/observers/graph_observer.cc +++ b/chrome/browser/performance_manager/observers/graph_observer.cc
@@ -6,8 +6,14 @@ namespace performance_manager { -GraphObserver::GraphObserver() = default; - GraphObserver::~GraphObserver() = default; +GraphObserverDefaultImpl::GraphObserverDefaultImpl() = default; + +GraphObserverDefaultImpl::~GraphObserverDefaultImpl() = default; + +void GraphObserverDefaultImpl::SetNodeGraph(GraphImpl* graph) { + node_graph_ = graph; +} + } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/observers/graph_observer.h b/chrome/browser/performance_manager/observers/graph_observer.h index 88f0f99..765fa72 100644 --- a/chrome/browser/performance_manager/observers/graph_observer.h +++ b/chrome/browser/performance_manager/observers/graph_observer.h
@@ -33,59 +33,106 @@ // const Node* rather then mutable NodeImpl* types for external consumers. class GraphObserver { public: - GraphObserver(); - virtual ~GraphObserver(); + virtual ~GraphObserver() = 0; // Invoked when an observer is added to or removed from the graph. This is a // convenient place for observers to initialize any necessary state, validate // graph invariants, etc. - virtual void OnRegistered() {} - virtual void OnUnregistered() {} + virtual void OnRegistered() = 0; + virtual void OnUnregistered() = 0; // Determines whether or not the observer should be registered with, and // invoked for, the |node|. virtual bool ShouldObserve(const NodeBase* node) = 0; // Called whenever a node has been added to the graph. - virtual void OnNodeAdded(NodeBase* node) {} + virtual void OnNodeAdded(NodeBase* node) = 0; // Called when the |node| is about to be removed from the graph. - virtual void OnBeforeNodeRemoved(NodeBase* node) {} + virtual void OnBeforeNodeRemoved(NodeBase* node) = 0; // FrameNodeImpl notifications. - virtual void OnIsCurrentChanged(FrameNodeImpl* frame_node) {} - virtual void OnNetworkAlmostIdleChanged(FrameNodeImpl* frame_node) {} - virtual void OnLifecycleStateChanged(FrameNodeImpl* frame_node) {} - virtual void OnNonPersistentNotificationCreated(FrameNodeImpl* frame_node) {} + virtual void OnIsCurrentChanged(FrameNodeImpl* frame_node) = 0; + virtual void OnNetworkAlmostIdleChanged(FrameNodeImpl* frame_node) = 0; + virtual void OnLifecycleStateChanged(FrameNodeImpl* frame_node) = 0; + virtual void OnNonPersistentNotificationCreated( + FrameNodeImpl* frame_node) = 0; // PageNodeImpl notifications. - virtual void OnIsVisibleChanged(PageNodeImpl* page_node) {} - virtual void OnIsLoadingChanged(PageNodeImpl* page_node) {} - virtual void OnUkmSourceIdChanged(PageNodeImpl* page_node) {} - virtual void OnLifecycleStateChanged(PageNodeImpl* page_node) {} - virtual void OnPageAlmostIdleChanged(PageNodeImpl* page_node) {} - virtual void OnFaviconUpdated(PageNodeImpl* page_node) {} - virtual void OnTitleUpdated(PageNodeImpl* page_node) {} - virtual void OnMainFrameNavigationCommitted(PageNodeImpl* page_node) {} + virtual void OnIsVisibleChanged(PageNodeImpl* page_node) = 0; + virtual void OnIsLoadingChanged(PageNodeImpl* page_node) = 0; + virtual void OnUkmSourceIdChanged(PageNodeImpl* page_node) = 0; + virtual void OnLifecycleStateChanged(PageNodeImpl* page_node) = 0; + virtual void OnPageAlmostIdleChanged(PageNodeImpl* page_node) = 0; + virtual void OnFaviconUpdated(PageNodeImpl* page_node) = 0; + virtual void OnTitleUpdated(PageNodeImpl* page_node) = 0; + virtual void OnMainFrameNavigationCommitted(PageNodeImpl* page_node) = 0; // ProcessNodeImpl notifications. virtual void OnExpectedTaskQueueingDurationSample( - ProcessNodeImpl* process_node) {} - virtual void OnMainThreadTaskLoadIsLow(ProcessNodeImpl* process_node) {} - virtual void OnRendererIsBloated(ProcessNodeImpl* process_node) {} - virtual void OnAllFramesInProcessFrozen(ProcessNodeImpl* process_node) {} + ProcessNodeImpl* process_node) = 0; + virtual void OnMainThreadTaskLoadIsLow(ProcessNodeImpl* process_node) = 0; + virtual void OnRendererIsBloated(ProcessNodeImpl* process_node) = 0; + virtual void OnAllFramesInProcessFrozen(ProcessNodeImpl* process_node) = 0; // SystemNodeImpl notifications. - virtual void OnProcessCPUUsageReady(SystemNodeImpl* system_node) {} + virtual void OnProcessCPUUsageReady(SystemNodeImpl* system_node) = 0; - void set_node_graph(GraphImpl* graph) { node_graph_ = graph; } + virtual void SetNodeGraph(GraphImpl* graph) = 0; +}; + +// An empty implementation of the interface. +class GraphObserverDefaultImpl : public GraphObserver { + public: + GraphObserverDefaultImpl(); + ~GraphObserverDefaultImpl() override; + + // Invoked when an observer is added to or removed from the graph. This is a + // convenient place for observers to initialize any necessary state, validate + // graph invariants, etc. + void OnRegistered() override {} + void OnUnregistered() override {} + + // Called whenever a node has been added to the graph. + void OnNodeAdded(NodeBase* node) override {} + + // Called when the |node| is about to be removed from the graph. + void OnBeforeNodeRemoved(NodeBase* node) override {} + + // FrameNodeImpl notifications. + void OnIsCurrentChanged(FrameNodeImpl* frame_node) override {} + void OnNetworkAlmostIdleChanged(FrameNodeImpl* frame_node) override {} + void OnLifecycleStateChanged(FrameNodeImpl* frame_node) override {} + void OnNonPersistentNotificationCreated(FrameNodeImpl* frame_node) override {} + + // PageNodeImpl notifications. + void OnIsVisibleChanged(PageNodeImpl* page_node) override {} + void OnIsLoadingChanged(PageNodeImpl* page_node) override {} + void OnUkmSourceIdChanged(PageNodeImpl* page_node) override {} + void OnLifecycleStateChanged(PageNodeImpl* page_node) override {} + void OnPageAlmostIdleChanged(PageNodeImpl* page_node) override {} + void OnFaviconUpdated(PageNodeImpl* page_node) override {} + void OnTitleUpdated(PageNodeImpl* page_node) override {} + void OnMainFrameNavigationCommitted(PageNodeImpl* page_node) override {} + + // ProcessNodeImpl notifications. + void OnExpectedTaskQueueingDurationSample( + ProcessNodeImpl* process_node) override {} + void OnMainThreadTaskLoadIsLow(ProcessNodeImpl* process_node) override {} + void OnRendererIsBloated(ProcessNodeImpl* process_node) override {} + void OnAllFramesInProcessFrozen(ProcessNodeImpl* process_node) override {} + + // SystemNodeImpl notifications. + void OnProcessCPUUsageReady(SystemNodeImpl* system_node) override {} + + void SetNodeGraph(GraphImpl* graph) override; GraphImpl* graph() const { return node_graph_; } private: GraphImpl* node_graph_ = nullptr; - DISALLOW_COPY_AND_ASSIGN(GraphObserver); + DISALLOW_COPY_AND_ASSIGN(GraphObserverDefaultImpl); }; } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc index a5221b1..cd4db49 100644 --- a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc +++ b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc
@@ -20,7 +20,7 @@ class GraphObserverTest : public GraphTestHarness {}; -class TestGraphObserver : public GraphObserver { +class TestGraphObserver : public GraphObserverDefaultImpl { public: TestGraphObserver() {} ~TestGraphObserver() override {}
diff --git a/chrome/browser/performance_manager/observers/metrics_collector.h b/chrome/browser/performance_manager/observers/metrics_collector.h index 8ead22b..25eabb70 100644 --- a/chrome/browser/performance_manager/observers/metrics_collector.h +++ b/chrome/browser/performance_manager/observers/metrics_collector.h
@@ -29,7 +29,7 @@ extern const int kDefaultFrequencyUkmEQTReported; // The MetricsCollector is a graph observer that reports UMA/UKM. -class MetricsCollector : public GraphObserver { +class MetricsCollector : public GraphObserverDefaultImpl { public: MetricsCollector(); ~MetricsCollector() override;
diff --git a/chrome/browser/performance_manager/observers/working_set_trimmer_win.h b/chrome/browser/performance_manager/observers/working_set_trimmer_win.h index a45eca6..56f246bb4 100644 --- a/chrome/browser/performance_manager/observers/working_set_trimmer_win.h +++ b/chrome/browser/performance_manager/observers/working_set_trimmer_win.h
@@ -26,7 +26,7 @@ // to be compressed and/or written to disk preemptively, which makes more // memory available quickly for foreground processes and improves global // browser performance. -class WorkingSetTrimmer : public GraphObserver { +class WorkingSetTrimmer : public GraphObserverDefaultImpl { public: WorkingSetTrimmer(); ~WorkingSetTrimmer() override;
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_store.h b/chrome/browser/performance_manager/persistence/site_data/site_data_store.h new file mode 100644 index 0000000..b91a0909 --- /dev/null +++ b/chrome/browser/performance_manager/persistence/site_data/site_data_store.h
@@ -0,0 +1,59 @@ +// 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. + +#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_STORE_H_ +#define CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_STORE_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/optional.h" +#include "chrome/browser/performance_manager/persistence/site_data/site_data.pb.h" +#include "url/origin.h" + +class SiteDataProto; + +namespace performance_manager { + +// Interface for an on-disk SiteData store. +class SiteDataStore { + public: + // Callback to call once the initialization from the store has completed, + // |site_data_proto| should be equal to base::nullopt if the initialization + // has failed. + using ReadSiteDataFromStoreCallback = + base::OnceCallback<void(base::Optional<SiteDataProto> site_data_proto)>; + using GetStoreSizeCallback = + base::OnceCallback<void(base::Optional<int64_t> num_rows, + base::Optional<int64_t> on_disk_size_kb)>; + + SiteDataStore() = default; + virtual ~SiteDataStore() {} + + // Checks the if there's an entry with the key |origin| and if pass the + // corresponding proto to |callback|. + virtual void ReadSiteDataFromStore( + const url::Origin& origin, + ReadSiteDataFromStoreCallback callback) = 0; + + // Store an entry in the store, create it if it doesn't exist and update it it + // it does. + virtual void WriteSiteDataIntoStore(const url::Origin& origin, + const SiteDataProto& site_data_proto) = 0; + + // Removes some entries from the store. + virtual void RemoveSiteDataFromStore( + const std::vector<url::Origin>& site_origins) = 0; + + // Clear the store, removes every entries that it contains. + virtual void ClearStore() = 0; + + // Retrieve the size of the store. + virtual void GetStoreSize(GetStoreSizeCallback callback) = 0; +}; + +} // namespace performance_manager + +#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_STORE_H_
diff --git a/chrome/browser/performance_manager/webui_graph_dump_impl.cc b/chrome/browser/performance_manager/webui_graph_dump_impl.cc index a34dc4b..674aba5 100644 --- a/chrome/browser/performance_manager/webui_graph_dump_impl.cc +++ b/chrome/browser/performance_manager/webui_graph_dump_impl.cc
@@ -147,6 +147,10 @@ SendProcessNotification(process_node, false); } +void WebUIGraphDumpImpl::SetNodeGraph(GraphImpl* graph) { + DCHECK_EQ(graph_, graph); +} + void WebUIGraphDumpImpl::SendFrameNotification(FrameNodeImpl* frame, bool created) { // TODO(https://crbug.com/961785): Add more frame properties.
diff --git a/chrome/browser/performance_manager/webui_graph_dump_impl.h b/chrome/browser/performance_manager/webui_graph_dump_impl.h index bc8cdc7..fb336783 100644 --- a/chrome/browser/performance_manager/webui_graph_dump_impl.h +++ b/chrome/browser/performance_manager/webui_graph_dump_impl.h
@@ -27,6 +27,8 @@ mojom::WebUIGraphChangeStreamPtr change_subscriber) override; // GraphObserver implementation. + void OnRegistered() override {} + void OnUnregistered() override {} bool ShouldObserve(const NodeBase* node) override; void OnNodeAdded(NodeBase* node) override; void OnBeforeNodeRemoved(NodeBase* node) override; @@ -55,6 +57,11 @@ // Event notification. void OnAllFramesInProcessFrozen(ProcessNodeImpl* process_node) override {} + // Ignored + void OnProcessCPUUsageReady(SystemNodeImpl* system_node) override {} + + void SetNodeGraph(GraphImpl* graph) override; + private: void SendFrameNotification(FrameNodeImpl* frame, bool created); void SendPageNotification(PageNodeImpl* page, bool created);
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc index 78266c4..eb6e8680 100644 --- a/chrome/browser/policy/policy_conversions.cc +++ b/chrome/browser/policy/policy_conversions.cc
@@ -351,7 +351,8 @@ {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT}, {"sourceCloud", IDS_POLICY_SOURCE_CLOUD}, {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY}, - {"sourcePublicSessionOverride", IDS_POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE}, + {"sourceDeviceLocalAccountOverride", + IDS_POLICY_SOURCE_DEVICE_LOCAL_ACCOUNT_OVERRIDE}, {"sourcePlatform", IDS_POLICY_SOURCE_PLATFORM}, {"sourcePriorityCloud", IDS_POLICY_SOURCE_CLOUD}, {"sourceMerged", IDS_POLICY_SOURCE_MERGED},
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 242dfac8..56fb0cc7d 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -228,6 +228,7 @@ #endif // defined(OS_ANDROID) #if defined(OS_CHROMEOS) +#include "ash/public/cpp/ash_prefs.h" #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/arc/arc_session_manager.h" @@ -918,6 +919,9 @@ #if defined(OS_ANDROID) ::android::RegisterUserProfilePrefs(registry); #endif +#if defined(OS_CHROMEOS) + ash::RegisterUserProfilePrefs(registry); +#endif } void RegisterScreenshotPrefs(PrefRegistrySimple* registry) { @@ -925,8 +929,9 @@ } #if defined(OS_CHROMEOS) -void RegisterLoginProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { +void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { RegisterProfilePrefs(registry, g_browser_process->GetApplicationLocale()); + ash::RegisterSigninProfilePrefs(registry); } #if BUILDFLAG(ENABLE_CROS_ASSISTANT)
diff --git a/chrome/browser/prefs/browser_prefs.h b/chrome/browser/prefs/browser_prefs.h index 69a5162..05f6c20 100644 --- a/chrome/browser/prefs/browser_prefs.h +++ b/chrome/browser/prefs/browser_prefs.h
@@ -33,9 +33,9 @@ const std::string& locale); #if defined(OS_CHROMEOS) -// Register all prefs that will be used via a PrefService attached to the login -// Profile using the locale of |g_browser_process|. -void RegisterLoginProfilePrefs(user_prefs::PrefRegistrySyncable* registry); +// Register all prefs that will be used via a PrefService attached to the +// sign-in profile using the locale of |g_browser_process|. +void RegisterSigninProfilePrefs(user_prefs::PrefRegistrySyncable* registry); #endif // Migrate/cleanup deprecated prefs in |local_state|. Over time, long deprecated
diff --git a/chrome/browser/profiles/pref_service_builder_utils.cc b/chrome/browser/profiles/pref_service_builder_utils.cc index 87f373a..9909604 100644 --- a/chrome/browser/profiles/pref_service_builder_utils.cc +++ b/chrome/browser/profiles/pref_service_builder_utils.cc
@@ -73,7 +73,7 @@ user_prefs::PrefRegistrySyncable* pref_registry) { #if defined(OS_CHROMEOS) if (is_signin_profile) - RegisterLoginProfilePrefs(pref_registry); + RegisterSigninProfilePrefs(pref_registry); else #endif RegisterUserProfilePrefs(pref_registry, locale);
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc b/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc index f487c83..51cbd13 100644 --- a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc +++ b/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc
@@ -35,7 +35,7 @@ } // namespace class LocalSiteCharacteristicsWebContentsObserver::GraphObserver - : public performance_manager::GraphObserver { + : public performance_manager::GraphObserverDefaultImpl { public: using NodeBase = performance_manager::NodeBase; using FrameNodeImpl = performance_manager::FrameNodeImpl;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc index d771522..daf4a72 100644 --- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc +++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -64,7 +64,8 @@ // A very simple graph observer that forwards events over to the // TabLifecycleUnitSource on the UI thread. This is created on the UI thread // and ownership passed to the performance manager. -class TabLifecycleStateObserver : public performance_manager::GraphObserver { +class TabLifecycleStateObserver + : public performance_manager::GraphObserverDefaultImpl { public: using NodeBase = performance_manager::NodeBase; using PageNodeImpl = performance_manager::PageNodeImpl;
diff --git a/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h b/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h index 83caecc0..4488dce 100644 --- a/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h +++ b/chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h
@@ -17,7 +17,7 @@ // TODO(chrisha): Kill this thing entirely and move all of tab manager into the // performance manager. class TabManager::ResourceCoordinatorSignalObserver - : public performance_manager::GraphObserver { + : public performance_manager::GraphObserverDefaultImpl { public: using NodeBase = performance_manager::NodeBase; using PageNodeImpl = performance_manager::PageNodeImpl;
diff --git a/chrome/browser/resources/chromeos/camera/BUILD.gn b/chrome/browser/resources/chromeos/camera/BUILD.gn index 019ea1c7..bca5a3d 100644 --- a/chrome/browser/resources/chromeos/camera/BUILD.gn +++ b/chrome/browser/resources/chromeos/camera/BUILD.gn
@@ -82,6 +82,7 @@ "src/images/camera_shutter_video_start_hover.svg", "src/images/camera_shutter_video_stop.svg", "src/images/camera_shutter_video_stop_hover.svg", + "src/images/dialog_intro_icon.svg", "src/images/settings_button_back.svg", "src/images/settings_button_expand.svg", "src/images/settings_feedback.svg",
diff --git a/chrome/browser/resources/chromeos/camera/Makefile b/chrome/browser/resources/chromeos/camera/Makefile index bbb7938..25b82ad57 100644 --- a/chrome/browser/resources/chromeos/camera/Makefile +++ b/chrome/browser/resources/chromeos/camera/Makefile
@@ -104,6 +104,7 @@ src/images/camera_shutter_video_start_hover.svg \ src/images/camera_shutter_video_stop.svg \ src/images/camera_shutter_video_stop_hover.svg \ + src/images/dialog_intro_icon.svg \ src/images/settings_button_back.svg \ src/images/settings_button_expand.svg \ src/images/settings_feedback.svg \
diff --git a/chrome/browser/resources/chromeos/camera/src/_locales/en/messages.json b/chrome/browser/resources/chromeos/camera/src/_locales/en/messages.json index d0e8d21..a5c31c9 100644 --- a/chrome/browser/resources/chromeos/camera/src/_locales/en/messages.json +++ b/chrome/browser/resources/chromeos/camera/src/_locales/en/messages.json
@@ -230,5 +230,17 @@ "MIGRATE_PICTURES_MSG": { "message": "Photos and videos taken with the camera will be moved to the Downloads folder. You can access them in Files.\n\nApps with storage permissions will have access to your photos and videos.", "description": "Message shown before moving all photos and videos stored in the Camera App to the Downloads folder." + }, + "DIALOG_INTRO_TITLE": { + "message": "A whole new look", + "description": "Title of dialog shown for introducing new camera App UI." + }, + "DIALOG_INTRO_MSG": { + "message": "Your camera now supports new modes and your photos and videos will be available under your Downloads folders.", + "description": "Message in the dialog shown for introducing new camera App UI." + }, + "DIALOG_GOT_IT_BUTTON": { + "message": "Got it", + "description": "Label for the got it button in the dialog for introducing new camera App UI." } } \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css index 2855954..fa068304 100644 --- a/chrome/browser/resources/chromeos/camera/src/css/main.css +++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -444,7 +444,8 @@ #gridsettings, #timersettings, #browser, -#dialog, +#message-dialog, +#intro-dialog, #warning { bottom: 0; left: 0; @@ -461,7 +462,8 @@ body.gridsettings #gridsettings, body.timersettings #timersettings, body.browser #browser, -body.dialog #dialog, +body.message-dialog #message-dialog, +body.intro-dialog #intro-dialog, body.warning #warning { opacity: 1; transition: opacity 100ms; @@ -1005,7 +1007,7 @@ } #warning, -#dialog { +.dialog { align-items: center; display: flex; justify-content: center; @@ -1025,13 +1027,14 @@ white-space: pre-wrap; } -#dialog { - background: rgba(0, 0, 0, 0.8); +.dialog { + background: rgba(0, 0, 0, 0.6); } -#dialog-popup { +.dialog-popup { background: white; - border-radius: 4px; + border-radius: 8px; + box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15); display: flex; flex-direction: column; padding: 20px; @@ -1039,11 +1042,11 @@ transition: transform 200ms; } -body.dialog #dialog #dialog-popup { +.dialog .dialog-popup { transform: translateY(0); } -#dialog #dialog-msg { +.dialog .dialog-msg { color: rgb(32, 33, 36); cursor: text; font-family: 'Roboto', sans-serif; @@ -1056,29 +1059,29 @@ white-space: pre-wrap; } -#dialog #dialog-msg::-webkit-scrollbar { +.dialog .dialog-msg::-webkit-scrollbar { height: 6px; width: 6px; } -#dialog #dialog-msg::-webkit-scrollbar-track { +.dialog .dialog-msg::-webkit-scrollbar-track { background: transparent; } -#dialog #dialog-msg::-webkit-scrollbar-thumb { +.dialog .dialog-msg::-webkit-scrollbar-thumb { background: gray; height: 6px; width: 6px; } -#dialog-buttons { +.dialog-buttons { align-items: center; display: flex; justify-content: flex-end; margin: 0 -2px; } -#dialog-buttons button { +.dialog-buttons button { background-color: white; border-style: solid; color: rgb(37, 129, 223); @@ -1088,16 +1091,59 @@ padding: 6px 18px; } -#dialog-buttons button:focus { +.dialog-buttons button:focus { background-color: rgb(37, 129, 223); border-color: rgb(37, 129, 223); color: white; } -#dialog-buttons button:focus::after { +.dialog-buttons button:focus::after { border: none; } +#intro-dialog .dialog-popup { + align-items: center; + justify-content: center; + padding: 32px 50px; + text-align: center; + width: 360px; +} + +#intro-dialog .dialog-title-icon { + background-image: url(../images/dialog_new_cca_icon.svg); + background-size: cover; + height: 32px; + width: 32px; +} + +#intro-dialog .dialog-title { + color: rgb(32, 33, 36); + font: normal 28px/36px 'Google Sans', sans-serif; + margin-top: 20px; +} + +#intro-dialog .dialog-msg { + color: rgb(95, 99, 104); + font: normal 13px/20px 'Roboto'; + margin-top: 12px; + padding: 0; +} + +#intro-dialog .dialog-buttons { + justify-content: center; + margin-top: 32px; +} + +#intro-dialog .dialog-positive-button { + background: rgb(26, 115, 232); + border: none; + color: white; + font: 500 13px/20px 'Roboto', sans-serif; + height: 32px; + margin: 0; + padding: 0 16px; +} + #spinner { background-image: url(../images/spinner.svg); height: 32px;
diff --git a/chrome/browser/resources/chromeos/camera/src/images/dialog_intro_icon.svg b/chrome/browser/resources/chromeos/camera/src/images/dialog_intro_icon.svg new file mode 100644 index 0000000..c1f4410 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/images/dialog_intro_icon.svg
@@ -0,0 +1 @@ +<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M5 6l2-2h5l2 2h3a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h3zM3 8v9h13V8H3zm14.373-5.373L16 2l1.373-.627L18 0l.627 1.373L20 2l-1.373.627L18 4l-.627-1.373zM9.5 15.5a3 3 0 1 1 0-6 3 3 0 0 1 0 6z" fill="#1A73E8" fill-rule="nonzero"/></svg> \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/js/main.js b/chrome/browser/resources/chromeos/camera/src/js/main.js index d799717f..398c9062 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/main.js +++ b/chrome/browser/resources/chromeos/camera/src/js/main.js
@@ -49,7 +49,8 @@ new cca.views.TimerSettings(), this.browserView_, new cca.views.Warning(), - new cca.views.Dialog(), + new cca.views.Dialog('#message-dialog'), + new cca.views.Dialog('#intro-dialog'), ]); }; @@ -133,12 +134,13 @@ cca.models.FileSystem.initialize(() => { // Prompt to migrate pictures if needed. var message = chrome.i18n.getMessage('migrate_pictures_msg'); - return cca.nav.open('dialog', message, false).then((acked) => { - if (!acked) { - throw new Error('no-migrate'); - } - ackMigrate = true; - }); + return cca.nav.open('message-dialog', {message, cancellable: false}) + .then((acked) => { + if (!acked) { + throw new Error('no-migrate'); + } + ackMigrate = true; + }); }).then((external) => { cca.state.set('ext-fs', external); this.model_.addObserver(this.galleryButton_); @@ -147,6 +149,7 @@ } this.model_.load(); cca.nav.open('camera'); + this.openIntroDialog_(); }).catch((error) => { console.error(error); if (error && error.message == 'no-migrate') { @@ -170,6 +173,19 @@ }; /** + * Tries to open dialog for introducing new CCA. + * @private + */ +cca.App.prototype.openIntroDialog_ = function() { + chrome.storage.local.get(['isIntroDialogShown'], (values) => { + if (!values['isIntroDialogShown']) { + cca.nav.open('intro-dialog'); + chrome.storage.local.set({isIntroDialogShown: true}); + } + }); +}; + +/** * @type {cca.App} Singleton of the App object. * @private */
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/dialog.js b/chrome/browser/resources/chromeos/camera/src/js/views/dialog.js index e50b6a9..5e4d9e6 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/dialog.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/dialog.js
@@ -18,33 +18,38 @@ * Creates the Dialog view controller. * @extends {cca.views.View} * @constructor + * @param {string} viewId Root element id of dialog view. */ -cca.views.Dialog = function() { - cca.views.View.call(this, '#dialog', true); +cca.views.Dialog = function(viewId) { + cca.views.View.call(this, viewId, true); /** * @type {HTMLButtonElement} * @private */ - this.positiveButton_ = document.querySelector('#dialog-positive-button'); + this.positiveButton_ = + document.querySelector(`${viewId} .dialog-positive-button`); /** - * @type {HTMLButtonElement} + * @type {!HTMLButtonElement} * @private */ - this.negativeButton_ = document.querySelector('#dialog-negative-button'); + this.negativeButton_ = + document.querySelector(`${viewId} .dialog-negative-button`); /** - * @type {HTMLElement} + * @type {!HTMLElement} * @private */ - this.messageElement_ = document.querySelector('#dialog-msg'); + this.messageHolder_ = document.querySelector(`${viewId} .dialog-msg-holder`); // End of properties, seal the object. Object.seal(this); this.positiveButton_.addEventListener('click', () => this.leave(true)); - this.negativeButton_.addEventListener('click', () => this.leave()); + if (this.negativeButton_) { + this.negativeButton_.addEventListener('click', () => this.leave()); + } }; cca.views.Dialog.prototype = { @@ -56,9 +61,13 @@ * @param {boolean} cancellable Whether the dialog is cancellable. * @override */ -cca.views.Dialog.prototype.entering = function(message, cancellable) { - this.messageElement_.textContent = message; - this.negativeButton_.hidden = !cancellable; +cca.views.Dialog.prototype.entering = function({message, cancellable} = {}) { + if (this.messageHolder_ && message) { + this.messageHolder_.textContent = message; + } + if (this.negativeButton_) { + this.negativeButton_.hidden = !cancellable; + } }; /**
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/gallery_base.js b/chrome/browser/resources/chromeos/camera/src/js/views/gallery_base.js index 454511f..8ac451c8 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/gallery_base.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/gallery_base.js
@@ -128,18 +128,20 @@ var message = chrome.i18n.getMessage( multi ? 'delete_multi_confirmation_msg' : 'delete_confirmation_msg', param); - cca.nav.open('dialog', message, true).then((confirmed) => { - if (!confirmed) { - return; - } - var selectedPictures = this.selectedPictures(); - for (var i = selectedPictures.length - 1; i >= 0; i--) { - this.model_.deletePicture(selectedPictures[i].picture).catch((error) => { - console.error(error); - // TODO(yuli): Show a toast message here. + cca.nav.open('message-dialog', {message, cancellable: true}) + .then((confirmed) => { + if (!confirmed) { + return; + } + var selectedPictures = this.selectedPictures(); + for (var i = selectedPictures.length - 1; i >= 0; i--) { + this.model_.deletePicture(selectedPictures[i].picture) + .catch((error) => { + console.error(error); + // TODO(yuli): Show a toast message here. + }); + } }); - } - }); }; /**
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd index a5eac502..fb32abe 100644 --- a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd +++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd
@@ -273,6 +273,15 @@ <message desc="Label for switch to take portrait photo mode button." name="IDS_LABEL_SWITCH_TAKE_PORTRAIT_PHOTO_BUTTON"> Portrait </message> + <message desc="Title of dialog shown for introducing new camera App UI." name="IDS_DIALOG_INTRO_TITLE"> + A whole new look + </message> + <message desc="Message in the dialog shown for introducing new camera App UI." name="IDS_DIALOG_INTRO_MSG"> + Your camera now supports new modes and your photos and videos will be available under your Downloads folders. + </message> + <message desc="Label for the got it button in the dialog for introducing new camera App UI." name="IDS_DIALOG_GOT_IT_BUTTON"> + Got it + </message> </messages> </release> </grit>
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html index 8c36c92..ddc633d 100644 --- a/chrome/browser/resources/chromeos/camera/src/views/main.html +++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -234,17 +234,28 @@ <div id="warning"> <div id="error-msg" aria-live="polite"></div> </div> - <div id="dialog"> - <div id="dialog-popup" role="dialog" aria-labelledby="dialog-msg"> - <div id="dialog-msg"></div> - <div id="dialog-buttons"> - <button id="dialog-negative-button" tabindex="0" + <div id="message-dialog" class="dialog"> + <div class="dialog-popup" role="dialog" aria-labelledby="dialog-msg"> + <div class="dialog-msg dialog-msg-holder"></div> + <div class="dialog-buttons"> + <button class="dialog-negative-button" tabindex="0" i18n-content="dialog_cancel_button"></button> - <button id="dialog-positive-button" tabindex="0" + <button class="dialog-positive-button" tabindex="0" i18n-content="dialog_ok_button"></button> </div> </div> </div> + <div id="intro-dialog" class="dialog"> + <div class="dialog-popup" role="dialog" aria-labelledby="dialog-msg"> + <div class="dialog-title-icon"></div> + <div class="dialog-title" i18n-content="dialog_intro_title"></div> + <div class="dialog-msg" i18n-content="dialog_intro_msg"></div> + <div class="dialog-buttons"> + <button class="dialog-positive-button" tabindex="0" + i18n-content="dialog_got_it_button"></button> + </div> + </div> + </div> <div class="centered-overlay" id="toast" aria-live="polite"></div> <div id="tooltip" aria-hidden="true"></div> <audio id="sound-tick" src="../sounds/tick.ogg" data-timeout="200">
diff --git a/chrome/browser/resources/chromeos/login/network_select_login.js b/chrome/browser/resources/chromeos/login/network_select_login.js index b39700c..c29ea8a 100644 --- a/chrome/browser/resources/chromeos/login/network_select_login.js +++ b/chrome/browser/resources/chromeos/login/network_select_login.js
@@ -205,13 +205,13 @@ }, /** - * This is called when network setup is done. - * + * Called when network setup is done. Notifies parent that network setup is + * done. * @private */ onSelectedNetworkConnected_: function() { this.networkLastSelectedGuid_ = ''; - chrome.send('login.NetworkScreen.userActed', ['continue']); + this.fire('selected-network-connected'); }, /**
diff --git a/chrome/browser/resources/chromeos/login/oobe_network.html b/chrome/browser/resources/chromeos/login/oobe_network.html index 67f7bdf..03f133d 100644 --- a/chrome/browser/resources/chromeos/login/oobe_network.html +++ b/chrome/browser/resources/chromeos/login/oobe_network.html
@@ -19,6 +19,7 @@ </div> <div slot="footer" class="layout vertical"> <network-select-login id="networkSelectLogin" + on-selected-network-connected="onNetworkConnected_" is-connected="{{isConnected_}}"> </network-select-login> </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_network.js b/chrome/browser/resources/chromeos/login/oobe_network.js index 0e7f484..597e8f6 100644 --- a/chrome/browser/resources/chromeos/login/oobe_network.js +++ b/chrome/browser/resources/chromeos/login/oobe_network.js
@@ -121,4 +121,12 @@ this.$.networkSelectLogin.isOfflineDemoModeSetup = this.isDemoModeSetup && this.offlineDemoModeEnabled; }, + + /** + * This is called when network setup is done. + * @private + */ + onNetworkConnected_: function() { + chrome.send('login.NetworkScreen.userActed', ['continue']); + }, });
diff --git a/chrome/browser/resources/chromeos/login/screen_error_message.js b/chrome/browser/resources/chromeos/login/screen_error_message.js index b621fb0..a80680c 100644 --- a/chrome/browser/resources/chromeos/login/screen_error_message.js +++ b/chrome/browser/resources/chromeos/login/screen_error_message.js
@@ -13,6 +13,7 @@ var USER_ACTION_LOCAL_STATE_POWERWASH = 'local-state-error-powerwash'; var USER_ACTION_REBOOT = 'reboot'; var USER_ACTION_SHOW_CAPTIVE_PORTAL = 'show-captive-portal'; + var USER_ACTION_NETWORK_CONNECTED = 'network-connected'; // Link which starts guest session for captive portal fixing. /** @const */ var FIX_CAPTIVE_PORTAL_ID = 'captive-portal-fix-link'; @@ -137,6 +138,12 @@ USER_ACTION_LOCAL_STATE_POWERWASH); e.stopPropagation(); }); + $('offline-network-control') + .addEventListener('selected-network-connected', function(e) { + self.send( + login.Screen.CALLBACK_USER_ACTED, + USER_ACTION_NETWORK_CONNECTED); + }); }, /**
diff --git a/chrome/browser/resources/history/history_item.html b/chrome/browser/resources/history/history_item.html index e658f1c0..7d5c39c3 100644 --- a/chrome/browser/resources/history/history_item.html +++ b/chrome/browser/resources/history/history_item.html
@@ -172,7 +172,7 @@ <div id="background-clip"> <div id="background"></div> </div> - <div id="date-accessed" class="card-title"> + <div id="date-accessed" class="card-title" role="heading" aria-level="2"> [[cardTitle_(numberOfItems, item.dateRelativeDay, searchTerm)]] </div> <div id="item-container" focus-row-container
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc index e1713a43..d4a3351 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
@@ -650,7 +650,7 @@ // Tests where the process gets past the startup phase and finds UwS to clean. INSTANTIATE_TEST_SUITE_P( - CleanerFindsUwS, + CleanerFindsUwSAndRuns, ChromeCleanerControllerTest, Combine(Values(CleanerProcessStatus::kFetchSuccessValidProcess), ValuesIn(kCrashPointsAfterStartup), @@ -665,9 +665,21 @@ ItemsReporting::kNotReported, ItemsReporting::kReported), Values(UserResponse::kAcceptedWithLogs, - UserResponse::kAcceptedWithoutLogs, - UserResponse::kDenied, - UserResponse::kDismissed)), + UserResponse::kAcceptedWithoutLogs)), + chrome_cleaner::GetParamNameForTest()); + +// Tests where the process gets past the startup phase and finds UwS to clean, +// but is dismissed by the user. +INSTANTIATE_TEST_SUITE_P( + CleanerIsDismissed, + ChromeCleanerControllerTest, + Combine(Values(CleanerProcessStatus::kFetchSuccessValidProcess), + ValuesIn(kCrashPointsAfterStartup), + Values(UwsFoundStatus::kUwsFoundRebootRequired), + Values(ExtensionCleaningFeatureStatus::kEnabled), + Values(ItemsReporting::kReported), + Values(ItemsReporting::kReported), + Values(UserResponse::kDenied, UserResponse::kDismissed)), chrome_cleaner::GetParamNameForTest()); // Tests where the process gets past the startup phase but finds nothing to
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc index f24ddc03..41c8b39 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -460,18 +460,29 @@ INSTANTIATE_TEST_SUITE_P( NoUwsFound, ChromeCleanerRunnerTest, + Combine(Values(UwsFoundStatus::kNoUwsFound), + // When no UwS is found we don't care about extension removel. + Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, + MockChromeCleanerProcess::ItemsReporting::kNotReported, + MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, + MockChromeCleanerProcess::ItemsReporting::kNotReported, + MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::CrashPoint::kNone), + Values(PromptAcceptance::DENIED)), + chrome_cleaner::GetParamNameForTest()); + +INSTANTIATE_TEST_SUITE_P( + NoUwsFoundAndCrashes, + ChromeCleanerRunnerTest, Combine( Values(UwsFoundStatus::kNoUwsFound), // When no UwS is found we don't care about extension removel. Values(ExtensionCleaningFeatureStatus::kDisabled), - Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, - MockChromeCleanerProcess::ItemsReporting::kNotReported, - MockChromeCleanerProcess::ItemsReporting::kReported), - Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, - MockChromeCleanerProcess::ItemsReporting::kNotReported, - MockChromeCleanerProcess::ItemsReporting::kReported), - Values(MockChromeCleanerProcess::CrashPoint::kNone, - MockChromeCleanerProcess::CrashPoint::kOnStartup, + Values(MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::CrashPoint::kOnStartup, MockChromeCleanerProcess::CrashPoint::kAfterConnection, MockChromeCleanerProcess::CrashPoint::kAfterRequestSent, MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived), @@ -481,25 +492,35 @@ INSTANTIATE_TEST_SUITE_P( UwsFound, ChromeCleanerRunnerTest, + Combine(Values(UwsFoundStatus::kUwsFoundRebootRequired, + UwsFoundStatus::kUwsFoundNoRebootRequired), + Values(ExtensionCleaningFeatureStatus::kEnabled, + ExtensionCleaningFeatureStatus::kDisabled), + Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, + MockChromeCleanerProcess::ItemsReporting::kNotReported, + MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, + MockChromeCleanerProcess::ItemsReporting::kNotReported, + MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::CrashPoint::kNone), + Values(PromptAcceptance::DENIED, + PromptAcceptance::ACCEPTED_WITH_LOGS, + PromptAcceptance::ACCEPTED_WITHOUT_LOGS)), + chrome_cleaner::GetParamNameForTest()); + +INSTANTIATE_TEST_SUITE_P( + UwsFoundAndCrashes, + ChromeCleanerRunnerTest, Combine( - Values(UwsFoundStatus::kUwsFoundRebootRequired, - UwsFoundStatus::kUwsFoundNoRebootRequired), - Values(ExtensionCleaningFeatureStatus::kEnabled, - ExtensionCleaningFeatureStatus::kDisabled), - Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, - MockChromeCleanerProcess::ItemsReporting::kNotReported, - MockChromeCleanerProcess::ItemsReporting::kReported), - Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, - MockChromeCleanerProcess::ItemsReporting::kNotReported, - MockChromeCleanerProcess::ItemsReporting::kReported), - Values(MockChromeCleanerProcess::CrashPoint::kNone, - MockChromeCleanerProcess::CrashPoint::kOnStartup, + Values(UwsFoundStatus::kUwsFoundRebootRequired), + Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::ItemsReporting::kReported), + Values(MockChromeCleanerProcess::CrashPoint::kOnStartup, MockChromeCleanerProcess::CrashPoint::kAfterConnection, MockChromeCleanerProcess::CrashPoint::kAfterRequestSent, MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived), - Values(PromptAcceptance::DENIED, - PromptAcceptance::ACCEPTED_WITH_LOGS, - PromptAcceptance::ACCEPTED_WITHOUT_LOGS)), + Values(PromptAcceptance::ACCEPTED_WITH_LOGS)), chrome_cleaner::GetParamNameForTest()); } // namespace
diff --git a/chrome/browser/ui/ash/login_screen_client.cc b/chrome/browser/ui/ash/login_screen_client.cc index 03eded8..83a4d42 100644 --- a/chrome/browser/ui/ash/login_screen_client.cc +++ b/chrome/browser/ui/ash/login_screen_client.cc
@@ -218,15 +218,6 @@ chromeos::LoginDisplayHost::default_host()->ShowFeedback(); } -void LoginScreenClient::LaunchKioskApp(const std::string& app_id) { - chromeos::LoginDisplayHost::default_host()->StartAppLaunch(app_id, false, - false); -} - -void LoginScreenClient::LaunchArcKioskApp(const AccountId& account_id) { - chromeos::LoginDisplayHost::default_host()->StartArcKiosk(account_id); -} - void LoginScreenClient::ShowResetScreen() { chromeos::LoginDisplayHost::default_host()->ShowResetScreen(); }
diff --git a/chrome/browser/ui/ash/login_screen_client.h b/chrome/browser/ui/ash/login_screen_client.h index ab184798..6c9bad1 100644 --- a/chrome/browser/ui/ash/login_screen_client.h +++ b/chrome/browser/ui/ash/login_screen_client.h
@@ -125,8 +125,6 @@ void RequestPublicSessionKeyboardLayouts(const AccountId& account_id, const std::string& locale) override; void ShowFeedback() override; - void LaunchKioskApp(const std::string& app_id) override; - void LaunchArcKioskApp(const AccountId& account_id) override; void ShowResetScreen() override; void ShowAccountAccessHelpApp() override; void OnFocusLeavingSystemTray(bool reverse) override;
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.cc b/chrome/browser/ui/ash/session_controller_client_impl.cc index df53ce8..eace4e2 100644 --- a/chrome/browser/ui/ash/session_controller_client_impl.cc +++ b/chrome/browser/ui/ash/session_controller_client_impl.cc
@@ -201,6 +201,10 @@ SessionControllerClientImpl::~SessionControllerClientImpl() { DCHECK_EQ(this, g_session_controller_client_instance); g_session_controller_client_instance = nullptr; + if (session_controller_ && + session_controller_ == ash::SessionController::Get()) { + session_controller_->SetClient(nullptr); + } if (supervised_user_profile_) { SupervisedUserServiceFactory::GetForProfile(supervised_user_profile_) @@ -321,6 +325,20 @@ chromeos::SessionManagerClient::Get()->EmitAshInitialized(); } +PrefService* SessionControllerClientImpl::GetSigninScreenPrefService() { + return chromeos::ProfileHelper::Get()->GetSigninProfile()->GetPrefs(); +} + +PrefService* SessionControllerClientImpl::GetUserPrefService( + const AccountId& account_id) { + Profile* const user_profile = + multi_user_util::GetProfileFromAccountId(account_id); + if (!user_profile) + return nullptr; + + return user_profile->GetPrefs(); +} + // static bool SessionControllerClientImpl::IsMultiProfileAvailable() { if (!profiles::IsMultipleProfilesEnabled() || !UserManager::IsInitialized())
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.h b/chrome/browser/ui/ash/session_controller_client_impl.h index 111f950b..2a95b5c 100644 --- a/chrome/browser/ui/ash/session_controller_client_impl.h +++ b/chrome/browser/ui/ash/session_controller_client_impl.h
@@ -80,6 +80,8 @@ void CycleActiveUser(ash::CycleUserDirection direction) override; void ShowMultiProfileLogin() override; void EmitAshInitialized() override; + PrefService* GetSigninScreenPrefService() override; + PrefService* GetUserPrefService(const AccountId& account_id) override; // Returns true if a multi-profile user can be added to the session or if // multiple users are already signed in.
diff --git a/chrome/browser/ui/ash/test_login_screen.cc b/chrome/browser/ui/ash/test_login_screen.cc index a4d4b24..f1cba50f 100644 --- a/chrome/browser/ui/ash/test_login_screen.cc +++ b/chrome/browser/ui/ash/test_login_screen.cc
@@ -113,10 +113,6 @@ void TestLoginScreen::SetPublicSessionShowFullManagementDisclosure( bool show_full_management_disclosure) {} -void TestLoginScreen::SetKioskApps( - std::vector<::ash::mojom::KioskAppInfoPtr> kiosk_apps, - SetKioskAppsCallback callback) {} - void TestLoginScreen::ShowKioskAppError(const std::string& message) {} void TestLoginScreen::NotifyOobeDialogState(ash::mojom::OobeDialogState state) {
diff --git a/chrome/browser/ui/ash/test_login_screen.h b/chrome/browser/ui/ash/test_login_screen.h index 6d4c9ba..00c0dd0 100644 --- a/chrome/browser/ui/ash/test_login_screen.h +++ b/chrome/browser/ui/ash/test_login_screen.h
@@ -74,8 +74,6 @@ std::vector<::ash::mojom::InputMethodItemPtr> keyboard_layouts) override; void SetPublicSessionShowFullManagementDisclosure( bool show_full_management_disclosure) override; - void SetKioskApps(std::vector<::ash::mojom::KioskAppInfoPtr> kiosk_apps, - SetKioskAppsCallback callback) override; void ShowKioskAppError(const std::string& message) override; void NotifyOobeDialogState(ash::mojom::OobeDialogState state) override; void SetAddUserButtonEnabled(bool enable) override;
diff --git a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm index 9192773a..61c9527a 100644 --- a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm +++ b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm
@@ -154,6 +154,7 @@ // Returns the menu item for the Quit menu item, or a thrown-together default // one if no Quit menu item exists. + (NSMenuItem*)quitMenuItem; +- (void)sendAccessibilityAnnouncement; @end ConfirmQuitPanelController* g_confirmQuitPanelController = nil; @@ -236,6 +237,13 @@ // Show the info panel that explains what the user must to do confirm quit. [self showWindow:self]; + // Explicitly announce the hold-to-quit message. For an ordinary modal dialog + // VoiceOver would announce it and read its message, but VoiceOver does not do + // this for windows whose styleMask is NSBorderlessWindowMask, so do it + // manually here. Without this screenreader users have no way to know why + // their quit hotkey seems not to work. + [self sendAccessibilityAnnouncement]; + // Spin a nested run loop until the |targetDate| is reached or a KeyUp event // is sent. NSDate* targetDate = [NSDate @@ -396,4 +404,16 @@ return string; } +- (void)sendAccessibilityAnnouncement { + NSString* message = l10n_util::GetNSStringF( + IDS_CONFIRM_TO_QUIT_DESCRIPTION, + base::SysNSStringToUTF16([[self class] keyCommandString])); + + NSAccessibilityPostNotificationWithUserInfo( + [NSApp mainWindow], NSAccessibilityAnnouncementRequestedNotification, @{ + NSAccessibilityAnnouncementKey : message, + NSAccessibilityPriorityKey : @(NSAccessibilityPriorityHigh), + }); +} + @end
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc index be3817b..d3833ba 100644 --- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc +++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -61,29 +61,28 @@ void AppInfoFooterPanel::CreateButtons() { if (CanCreateShortcuts(app_)) { - create_shortcuts_button_ = AddChildView(base::WrapUnique<views::View>( - views::MdTextButton::CreateSecondaryUiButton( + create_shortcuts_button_ = + AddChildView(views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16( - IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT)))); + IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT))); } #if defined(OS_CHROMEOS) if (CanSetPinnedToShelf(profile_, app_)) { - pin_to_shelf_button_ = AddChildView(base::WrapUnique<views::View>( - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN)))); - unpin_from_shelf_button_ = AddChildView(base::WrapUnique<views::View>( - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN)))); + pin_to_shelf_button_ = + AddChildView(views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN))); + unpin_from_shelf_button_ = + AddChildView(views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN))); UpdatePinButtons(false); } #endif if (CanUninstallApp(profile_, app_)) { - remove_button_ = AddChildView(base::WrapUnique<views::View>( - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16( - IDS_APPLICATION_INFO_UNINSTALL_BUTTON_TEXT)))); + remove_button_ = AddChildView(views::MdTextButton::CreateSecondaryUiButton( + this, + l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_UNINSTALL_BUTTON_TEXT))); } }
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc index f2a1544..56435d5 100644 --- a/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc +++ b/chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.cc
@@ -66,11 +66,10 @@ } views::View* SaveCardManageCardsBubbleViews::CreateExtraView() { - views::View* manage_cards_button = - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_CARDS)); + auto manage_cards_button = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_CARDS)); manage_cards_button->SetID(DialogViewId::MANAGE_CARDS_BUTTON); - return manage_cards_button; + return manage_cards_button.release(); } int SaveCardManageCardsBubbleViews::GetDialogButtons() const {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc index 0a0468f..309ded0 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -144,9 +144,10 @@ } views::View* BookmarkBubbleView::CreateExtraView() { - edit_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto edit_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS)); - edit_button_->AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN)); + edit_button->AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN)); + edit_button_ = edit_button.release(); return edit_button_; }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc index 39bf76e..d7839d3 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -327,9 +327,8 @@ tree_view->SetRootShown(false); tree_view->set_context_menu_controller(this); - new_folder_button_.reset(views::MdTextButton::CreateSecondaryUiButton( - this, - l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_BUTTON))); + new_folder_button_ = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_BUTTON)); new_folder_button_->set_owned_by_client(); new_folder_button_->SetEnabled(false); }
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc index 568b714..9b6e4bf 100644 --- a/chrome/browser/ui/views/certificate_selector.cc +++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -283,8 +283,9 @@ views::View* CertificateSelector::CreateExtraView() { DCHECK(!view_cert_button_); - view_cert_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto view_cert_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_PAGE_INFO_CERT_INFO_BUTTON)); + view_cert_button_ = view_cert_button.release(); return view_cert_button_; }
diff --git a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc index ac42e02..6936443 100644 --- a/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc +++ b/chrome/browser/ui/views/chrome_cleaner_dialog_win.cc
@@ -153,9 +153,10 @@ views::View* ChromeCleanerDialog::CreateExtraView() { DCHECK(!details_button_); - details_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto details_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16( IDS_CHROME_CLEANUP_PROMPT_DETAILS_BUTTON_LABEL)); + details_button_ = details_button.release(); return details_button_; }
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc index e1b1dee..ed0c7c7 100644 --- a/chrome/browser/ui/views/collected_cookies_views.cc +++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -575,16 +575,19 @@ views::GridLayout* layout = allowed->SetLayoutManager( std::make_unique<views::GridLayout>(allowed.get())); - block_allowed_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_BLOCK_BUTTON)); - delete_allowed_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_COOKIES_REMOVE_LABEL)); + block_allowed_button_ = + views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_BLOCK_BUTTON)) + .release(); + delete_allowed_button_ = + views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_COOKIES_REMOVE_LABEL)) + .release(); StartNewButtonColumnSet(layout, 0); layout->AddView(block_allowed_button_); layout->AddView(delete_allowed_button_); - allowed_buttons_pane_ = allowed.get(); - view->AddChildView(allowed.release()); + allowed_buttons_pane_ = view->AddChildView(std::move(allowed)); } { @@ -593,17 +596,20 @@ std::make_unique<views::GridLayout>(blocked.get())); blocked->SetVisible(false); - allow_blocked_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_ALLOW_BUTTON)); - for_session_blocked_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, - l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_SESSION_ONLY_BUTTON)); + allow_blocked_button_ = + views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_COLLECTED_COOKIES_ALLOW_BUTTON)) + .release(); + for_session_blocked_button_ = + views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16( + IDS_COLLECTED_COOKIES_SESSION_ONLY_BUTTON)) + .release(); StartNewButtonColumnSet(layout, 0); layout->AddView(allow_blocked_button_); layout->AddView(for_session_blocked_button_); - blocked_buttons_pane_ = blocked.get(); - view->AddChildView(blocked.release()); + blocked_buttons_pane_ = view->AddChildView(std::move(blocked)); } return view;
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc index a764a98f..177267c6 100644 --- a/chrome/browser/ui/views/content_setting_bubble_contents.cc +++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -562,11 +562,13 @@ base::string16 title = bubble_content.manage_text; if (title.empty()) title = l10n_util::GetStringUTF16(IDS_MANAGE); - manage_button_ = views::MdTextButton::CreateSecondaryUiButton(this, title); - manage_button_->SetMinSize(gfx::Size( + auto manage_button = + views::MdTextButton::CreateSecondaryUiButton(this, title); + manage_button->SetMinSize(gfx::Size( layout->GetDistanceMetric(views::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH), 0)); - extra_views.push_back(base::WrapUnique(manage_button_)); + manage_button_ = manage_button.get(); + extra_views.push_back(std::move(manage_button)); } if (extra_views.empty()) return nullptr;
diff --git a/chrome/browser/ui/views/device_chooser_content_view.cc b/chrome/browser/ui/views/device_chooser_content_view.cc index 1ee6af2..dce7b9b 100644 --- a/chrome/browser/ui/views/device_chooser_content_view.cc +++ b/chrome/browser/ui/views/device_chooser_content_view.cc
@@ -45,24 +45,23 @@ DeviceChooserContentView::BluetoothStatusContainer::BluetoothStatusContainer( views::ButtonListener* listener) { - re_scan_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto re_scan_button = views::MdTextButton::CreateSecondaryUiButton( listener, l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_RE_SCAN)); - re_scan_button_->SetTooltipText( + re_scan_button->SetTooltipText( l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_RE_SCAN_TOOLTIP)); - re_scan_button_->SetFocusForPlatform(); - re_scan_button_->set_tag(kReScanButtonTag); - AddChildView(re_scan_button_); + re_scan_button->SetFocusForPlatform(); + re_scan_button->set_tag(kReScanButtonTag); + re_scan_button_ = AddChildView(std::move(re_scan_button)); - throbber_ = new views::Throbber(); - AddChildView(throbber_); + throbber_ = AddChildView(std::make_unique<views::Throbber>()); - scanning_label_ = new views::Label( + auto scanning_label = std::make_unique<views::Label>( l10n_util::GetStringUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_SCANNING_LABEL), views::style::CONTEXT_LABEL, views::style::STYLE_DISABLED); - scanning_label_->SetTooltipText(l10n_util::GetStringUTF16( + scanning_label->SetTooltipText(l10n_util::GetStringUTF16( IDS_BLUETOOTH_DEVICE_CHOOSER_SCANNING_LABEL_TOOLTIP)); - AddChildView(scanning_label_); + scanning_label_ = AddChildView(std::move(scanning_label)); } gfx::Size
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc index 89e4516..c3b16cb 100644 --- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc +++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -254,8 +254,9 @@ DCHECK(!auxiliary_button_); base::string16 button_label = controller_->GetAuxiliaryButtonText(); if (!button_label.empty()) { - auxiliary_button_ = + auto auxiliary_button = views::MdTextButton::CreateSecondaryUiButton(this, button_label); + auxiliary_button_ = auxiliary_button.release(); } return auxiliary_button_; }
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc index bf6e518b..f8f054f9 100644 --- a/chrome/browser/ui/views/global_error_bubble_view.cc +++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -196,8 +196,9 @@ if (!error_ || error_->GetBubbleViewCancelButtonLabel().empty() || !error_->ShouldUseExtraView()) return nullptr; - return views::MdTextButton::CreateSecondaryUiButton( + auto view = views::MdTextButton::CreateSecondaryUiButton( this, error_->GetBubbleViewCancelButtonLabel()); + return view.release(); } bool GlobalErrorBubbleView::Cancel() {
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc index 91c9d4c..b0dcd46 100644 --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -448,11 +448,11 @@ AddChildView(zoom_in_button.release()); // Add "Reset" button. - reset_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto reset_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_ZOOM_SET_DEFAULT)); - reset_button_->SetTooltipText( + reset_button->SetTooltipText( l10n_util::GetStringUTF16(IDS_ACCNAME_ZOOM_SET_DEFAULT)); - AddChildView(reset_button_); + reset_button_ = AddChildView(std::move(reset_button)); StartTimerIfNecessary(); }
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc index 742e353..8e8b62b 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -188,19 +188,16 @@ // The label that displays the status of the identity check for this site. // Includes a link to open the Chrome Help Center article about connection // security. - views::StyledLabel* security_details_label_; + views::StyledLabel* security_details_label_ = nullptr; // A container for the styled label with a link for resetting cert decisions. // This is only shown sometimes, so we use a container to keep track of // where to place it (if needed). - views::View* reset_decisions_label_container_; - views::StyledLabel* reset_cert_decisions_label_; + views::View* reset_decisions_label_container_ = nullptr; // A container for the label buttons used to change password or mark the site // as safe. - views::View* password_reuse_button_container_; - views::LabelButton* change_password_button_; - views::LabelButton* whitelist_password_reuse_button_; + views::View* password_reuse_button_container_ = nullptr; DISALLOW_COPY_AND_ASSIGN(BubbleHeaderView); }; @@ -231,13 +228,7 @@ views::StyledLabelListener* styled_label_listener, int side_margin) : button_listener_(button_listener), - styled_label_listener_(styled_label_listener), - security_details_label_(nullptr), - reset_decisions_label_container_(nullptr), - reset_cert_decisions_label_(nullptr), - password_reuse_button_container_(nullptr), - change_password_button_(nullptr), - whitelist_password_reuse_button_(nullptr) { + styled_label_listener_(styled_label_listener) { views::GridLayout* layout = SetLayoutManager(std::make_unique<views::GridLayout>(this)); @@ -303,9 +294,9 @@ base::string16 text = base::ReplaceStringPlaceholders( base::ASCIIToUTF16("$1 $2"), subst, &offsets); - reset_cert_decisions_label_ = - new views::StyledLabel(text, styled_label_listener_); - reset_cert_decisions_label_->SetID( + auto reset_cert_decisions_label = + std::make_unique<views::StyledLabel>(text, styled_label_listener_); + reset_cert_decisions_label->SetID( PageInfoBubbleView::VIEW_ID_PAGE_INFO_LABEL_RESET_CERTIFICATE_DECISIONS); gfx::Range link_range(offsets[1], text.length()); @@ -313,10 +304,11 @@ views::StyledLabel::RangeStyleInfo::CreateForLink(); link_style.disable_line_wrapping = false; - reset_cert_decisions_label_->AddStyleRange(link_range, link_style); + reset_cert_decisions_label->AddStyleRange(link_range, link_style); // Fit the styled label to occupy available width. - reset_cert_decisions_label_->SizeToFit(0); - reset_decisions_label_container_->AddChildView(reset_cert_decisions_label_); + reset_cert_decisions_label->SizeToFit(0); + reset_decisions_label_container_->AddChildView( + std::move(reset_cert_decisions_label)); // Now that it contains a label, the container needs padding at the top. reset_decisions_label_container_->SetBorder(views::CreateEmptyBorder( @@ -331,16 +323,17 @@ password_reuse_button_container_->RemoveAllChildViews(true /* delete */); } - change_password_button_ = views::MdTextButton::CreateSecondaryUiBlueButton( - button_listener_, - l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_BUTTON)); - change_password_button_->SetID( + auto change_password_button = + views::MdTextButton::CreateSecondaryUiBlueButton( + button_listener_, + l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_BUTTON)); + change_password_button->SetID( PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_CHANGE_PASSWORD); - whitelist_password_reuse_button_ = + auto whitelist_password_reuse_button = views::MdTextButton::CreateSecondaryUiButton( button_listener_, l10n_util::GetStringUTF16( IDS_PAGE_INFO_WHITELIST_PASSWORD_REUSE_BUTTON)); - whitelist_password_reuse_button_->SetID( + whitelist_password_reuse_button->SetID( PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_WHITELIST_PASSWORD_REUSE); int kSpacingBetweenButtons = 8; @@ -348,8 +341,8 @@ // If these two buttons cannot fit into a single line, stack them vertically. bool can_fit_in_one_line = (password_reuse_button_container_->width() - kSpacingBetweenButtons) >= - (change_password_button_->CalculatePreferredSize().width() + - whitelist_password_reuse_button_->CalculatePreferredSize().width()); + (change_password_button->CalculatePreferredSize().width() + + whitelist_password_reuse_button->CalculatePreferredSize().width()); auto layout = std::make_unique<views::BoxLayout>( can_fit_in_one_line ? views::BoxLayout::kHorizontal : views::BoxLayout::kVertical, @@ -360,13 +353,15 @@ password_reuse_button_container_->SetLayoutManager(std::move(layout)); #if defined(OS_WIN) || defined(OS_CHROMEOS) - password_reuse_button_container_->AddChildView(change_password_button_); password_reuse_button_container_->AddChildView( - whitelist_password_reuse_button_); + std::move(change_password_button)); + password_reuse_button_container_->AddChildView( + std::move(whitelist_password_reuse_button)); #else password_reuse_button_container_->AddChildView( - whitelist_password_reuse_button_); - password_reuse_button_container_->AddChildView(change_password_button_); + std::move(whitelist_password_reuse_button)); + password_reuse_button_container_->AddChildView( + std::move(change_password_button)); #endif // Add padding at the top.
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc index c44cf6e..cec1f4f 100644 --- a/chrome/browser/ui/views/passwords/password_items_view.cc +++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -30,6 +30,7 @@ #include "ui/views/controls/link_listener.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" +#include "ui/views/style/typography.h" namespace { @@ -119,9 +120,15 @@ if (other_possible_username_pair.first != form.username_value) usernames.push_back(other_possible_username_pair.first); } + base::EraseIf(usernames, [](const base::string16& username) { + return username.empty(); + }); + bool display_arrow = !usernames.empty(); auto combobox = std::make_unique<views::EditableCombobox>( std::make_unique<ui::SimpleComboboxModel>(usernames), - /*filter_on_edit=*/false, /*show_on_empty=*/true); + /*filter_on_edit=*/false, /*show_on_empty=*/true, + views::EditableCombobox::Type::kRegular, views::style::CONTEXT_BUTTON, + views::style::STYLE_PRIMARY, display_arrow); combobox->SetText(form.username_value); combobox->SetAccessibleName( l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_USERNAME_LABEL)); @@ -283,9 +290,11 @@ } views::View* PasswordItemsView::CreateExtraView() { - return views::MdTextButton::CreateSecondaryUiButton( + auto view = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS_BUTTON)); + + return view.release(); } int PasswordItemsView::GetDialogButtons() const {
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc index b341405..ba46281b 100644 --- a/chrome/browser/ui/views/passwords/password_pending_view.cc +++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -177,15 +177,19 @@ const autofill::PasswordForm& form, bool are_passwords_revealed) { DCHECK(!form.IsFederatedCredential()); - const std::vector<base::string16> passwords = + std::vector<base::string16> passwords = form.all_possible_passwords.empty() ? std::vector<base::string16>(/*n=*/1, form.password_value) : ToValues(form.all_possible_passwords); + base::EraseIf(passwords, [](const base::string16& password) { + return password.empty(); + }); + bool display_arrow = !passwords.empty(); auto combobox = std::make_unique<views::EditableCombobox>( std::make_unique<ui::SimpleComboboxModel>(passwords), /*filter_on_edit=*/false, /*show_on_empty=*/true, views::EditableCombobox::Type::kPassword, views::style::CONTEXT_BUTTON, - STYLE_PRIMARY_MONOSPACED); + STYLE_PRIMARY_MONOSPACED, display_arrow); combobox->SetText(form.password_value); combobox->RevealPasswords(are_passwords_revealed); combobox->SetAccessibleName(
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc index 01f0e1e..70f90c1 100644 --- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc +++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -516,9 +516,8 @@ void PaymentRequestSheetController::AddSecondaryButton(views::View* container) { if (ShouldShowSecondaryButton()) { - secondary_button_ = std::unique_ptr<views::Button>( - views::MdTextButton::CreateSecondaryUiButton( - this, GetSecondaryButtonLabel())); + secondary_button_ = views::MdTextButton::CreateSecondaryUiButton( + this, GetSecondaryButtonLabel()); secondary_button_->set_owned_by_client(); secondary_button_->set_tag(GetSecondaryButtonTag()); secondary_button_->SetID(GetSecondaryButtonId());
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc index 271cfd0..b752bbb0 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -260,9 +260,9 @@ std::unique_ptr<HoverButton> title_card = std::make_unique<HoverButton>( enabled ? this : nullptr, std::move(icon_view), title, subtitle); title_card->SetEnabled(enabled); - views::Button* pointer = title_card.get(); + views::Button* button_ptr = title_card.get(); AddMenuItemInternal(std::move(title_card), MenuItems::kTitleCard); - return pointer; + return button_ptr; } views::Button* ProfileMenuViewBase::CreateAndAddButton( @@ -278,9 +278,9 @@ views::Button* ProfileMenuViewBase::CreateAndAddBlueButton( const base::string16& text, bool md_style) { - std::unique_ptr<views::LabelButton> button = base::WrapUnique( + std::unique_ptr<views::LabelButton> button = md_style ? views::MdTextButton::CreateSecondaryUiBlueButton(this, text) - : views::MdTextButton::Create(this, text)); + : base::WrapUnique(views::MdTextButton::Create(this, text)); views::Button* pointer = button.get(); // Add margins.
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc index 0f8eb98..a99b6a1 100644 --- a/chrome/browser/ui/views/sad_tab_view.cc +++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -124,7 +124,8 @@ } action_button_ = views::MdTextButton::CreateSecondaryUiBlueButton( - this, l10n_util::GetStringUTF16(GetButtonTitle())); + this, l10n_util::GetStringUTF16(GetButtonTitle())) + .release(); help_link_ = new views::Link(l10n_util::GetStringUTF16(GetHelpLinkTitle())); help_link_->set_listener(this); layout->StartRowWithPadding(views::GridLayout::kFixedSize, column_set_id,
diff --git a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc index c7edef7..3e38bc1 100644 --- a/chrome/browser/ui/views/screen_capture_notification_ui_views.cc +++ b/chrome/browser/ui/views/screen_capture_notification_ui_views.cc
@@ -140,34 +140,32 @@ SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kHorizontal, gfx::Insets(), kHorizontalMargin)); - gripper_ = new views::ImageView(); - gripper_->SetImage( - ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP)); - AddChildView(gripper_); + auto gripper = std::make_unique<views::ImageView>(); + gripper->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP)); + gripper_ = AddChildView(std::move(gripper)); - label_ = new views::Label(); - AddChildView(label_); + label_ = AddChildView(std::make_unique<views::Label>()); base::string16 source_text = l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_SOURCE); - source_button_ = + auto source_button = views::MdTextButton::CreateSecondaryUiButton(this, source_text); - AddChildView(source_button_); + source_button_ = AddChildView(std::move(source_button)); base::string16 stop_text = l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP); - stop_button_ = + auto stop_button = views::MdTextButton::CreateSecondaryUiBlueButton(this, stop_text); - AddChildView(stop_button_); + stop_button_ = AddChildView(std::move(stop_button)); // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to // merge to M34. Change it to a new IDS_ after the merge. - hide_link_ = new views::Link( + auto hide_link = std::make_unique<views::Link>( l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON)); - hide_link_->set_listener(this); - hide_link_->SetUnderline(false); - AddChildView(hide_link_); + hide_link->set_listener(this); + hide_link->SetUnderline(false); + hide_link_ = AddChildView(std::move(hide_link)); } ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc index 9551dc9c..65e87848 100644 --- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc +++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -119,8 +119,10 @@ const base::string16 continue_signin_text = l10n_util::GetStringUTF16(IDS_ENTERPRISE_SIGNIN_CONTINUE); + return views::MdTextButton::CreateSecondaryUiButton(this, - continue_signin_text); + continue_signin_text) + .release(); } bool ProfileSigninConfirmationDialogViews::Accept() {
diff --git a/chrome/browser/ui/views/test/view_event_test_platform_part_chromeos.cc b/chrome/browser/ui/views/test/view_event_test_platform_part_chromeos.cc index 36a8e45e..51c39e7 100644 --- a/chrome/browser/ui/views/test/view_event_test_platform_part_chromeos.cc +++ b/chrome/browser/ui/views/test/view_event_test_platform_part_chromeos.cc
@@ -71,7 +71,7 @@ switches::kHostWindowBounds, "0+0-1280x800"); ash::Shell::CreateInstance(std::move(init_params)); ash::TestSessionControllerClient session_controller_client( - ash::Shell::Get()->session_controller()); + ash::Shell::Get()->session_controller(), /*prefs_provider=*/nullptr); session_controller_client.CreatePredefinedUserSessions(1); GetContext()->GetHost()->Show(); }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc index 49920c94..f883cde 100644 --- a/chrome/browser/ui/views/translate/translate_bubble_view.cc +++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -602,11 +602,10 @@ views::GridLayout::kFixedSize, kButtonColumnSetId, views::GridLayout::kFixedSize, provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); - views::LabelButton* accept_button = - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT)); - accept_button->SetID(BUTTON_ID_TRANSLATE); + auto accept_button = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT)); + accept_button->SetID(BUTTON_ID_TRANSLATE); accept_button->SetIsDefault(true); before_translate_options_button_ = new views::MdTextButtonWithDownArrow( this, @@ -615,11 +614,11 @@ before_translate_options_button_->set_request_focus_on_press(true); if (views::PlatformStyle::kIsOkButtonLeading) { - layout->AddView(accept_button); + layout->AddView(accept_button.release()); layout->AddView(before_translate_options_button_); } else { layout->AddView(before_translate_options_button_); - layout->AddView(accept_button); + layout->AddView(accept_button.release()); } return view; @@ -654,12 +653,12 @@ provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId); - views::LabelButton* revert_button = - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT)); + + auto revert_button = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT)); revert_button->SetID(BUTTON_ID_SHOW_ORIGINAL); revert_button->SetEnabled(false); - layout->AddView(revert_button); + layout->AddView(revert_button.release()); return view; } @@ -690,10 +689,10 @@ provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId); - views::LabelButton* button = views::MdTextButton::CreateSecondaryUiButton( + auto button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT)); button->SetID(BUTTON_ID_SHOW_ORIGINAL); - layout->AddView(button); + layout->AddView(button.release()); views::Button* options_menu_button = new views::MdTextButtonWithDownArrow( this, @@ -732,18 +731,16 @@ provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId); - views::LabelButton* try_again_button = - views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN)); - try_again_button->SetID(BUTTON_ID_TRY_AGAIN); - layout->AddView(try_again_button); - views::LabelButton* advanced_button = - views::MdTextButton::CreateSecondaryUiButton( - this, - l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ADVANCED_BUTTON)); + auto try_again_button = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN)); + try_again_button->SetID(BUTTON_ID_TRY_AGAIN); + layout->AddView(try_again_button.release()); + + auto advanced_button = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ADVANCED_BUTTON)); advanced_button->SetID(BUTTON_ID_ADVANCED); - layout->AddView(advanced_button); + layout->AddView(advanced_button.release()); return view; } @@ -853,13 +850,16 @@ layout->StartRow(views::GridLayout::kFixedSize, COLUMN_SET_ID_BUTTONS); layout->SkipColumns(1); - advanced_done_button_ = views::MdTextButton::CreateSecondaryUiButton( + auto advanced_done_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_DONE)); - advanced_done_button_->SetID(BUTTON_ID_DONE); - advanced_done_button_->SetIsDefault(true); - advanced_cancel_button_ = views::MdTextButton::CreateSecondaryUiButton( + advanced_done_button->SetID(BUTTON_ID_DONE); + advanced_done_button->SetIsDefault(true); + auto advanced_cancel_button = views::MdTextButton::CreateSecondaryUiButton( this, l10n_util::GetStringUTF16(IDS_CANCEL)); - advanced_cancel_button_->SetID(BUTTON_ID_CANCEL); + advanced_cancel_button->SetID(BUTTON_ID_CANCEL); + advanced_done_button_ = advanced_done_button.release(); + advanced_cancel_button_ = advanced_cancel_button.release(); + layout->AddView(advanced_done_button_); layout->AddView(advanced_cancel_button_);
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc index eb1d7d6..bc4371d0 100644 --- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc +++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -29,6 +29,7 @@ #include "chromeos/dbus/debug_daemon_client.h" #include "chromeos/printing/ppd_provider.h" #include "chromeos/printing/printer_configuration.h" +#include "components/prefs/pref_service.h" #include "components/printing/browser/printer_capabilities.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc index f5f0fe7..27a76ee 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -124,6 +124,7 @@ params.SetKey("constrained", base::Value("1")); params.SetKey("flow", base::Value("crosAddAccount")); + params.SetBoolean("dontResizeNonEmbeddedPages", true); } void InlineLoginHandlerChromeOS::CompleteLogin(const std::string& email,
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc index 2187907..43395bf 100644 --- a/chrome/common/webui_url_constants.cc +++ b/chrome/common/webui_url_constants.cc
@@ -371,6 +371,7 @@ bool IsOSSettingsSubPage(const std::string& sub_page) { static const char* const kSubPages[] = {kAccessibilitySubPage, + kAccountManagerSubPage, kAndroidAppsDetailsSubPage, kAssistantSubPage, kBluetoothSubPage,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h index 37a9944e..0615e93 100644 --- a/chrome/common/webui_url_constants.h +++ b/chrome/common/webui_url_constants.h
@@ -9,6 +9,7 @@ #define CHROME_COMMON_WEBUI_URL_CONSTANTS_H_ #include <stddef.h> +#include <string> #include "base/strings/string_piece_forward.h" #include "build/build_config.h"
diff --git a/chrome/credential_provider/setup/BUILD.gn b/chrome/credential_provider/setup/BUILD.gn index 12cb384fb..b2d089ff 100644 --- a/chrome/credential_provider/setup/BUILD.gn +++ b/chrome/credential_provider/setup/BUILD.gn
@@ -27,7 +27,6 @@ "//base", ] deps = [ - ":version", "../gaiacp:common", "//chrome/installer/util:with_no_strings", ] @@ -69,6 +68,7 @@ deps = [ ":common", ":setup_resources", + ":version", "../eventlog:gcp_eventlog_messages", "../gaiacp:common", "//chrome/common:version_header",
diff --git a/chrome/credential_provider/test/BUILD.gn b/chrome/credential_provider/test/BUILD.gn index 45b2cc9..7aae9892 100644 --- a/chrome/credential_provider/test/BUILD.gn +++ b/chrome/credential_provider/test/BUILD.gn
@@ -51,10 +51,4 @@ "//third_party/pywebsocket/src/mod_pywebsocket/", "//third_party/tlslite/", ] - - if (is_win) { - # TODO(https://crbug.com/958955): Fix dupes and remove this flag. - configs -= [ "//build/config/compiler:fatal_linker_warnings_win" ] - ldflags = [ "/FORCE:MultipleRes" ] - } }
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn index 9c30d7f..6a0e266 100644 --- a/chrome/renderer/BUILD.gn +++ b/chrome/renderer/BUILD.gn
@@ -277,10 +277,6 @@ sources += [ "extensions/app_hooks_delegate.cc", "extensions/app_hooks_delegate.h", - "extensions/automation_ax_tree_wrapper.cc", - "extensions/automation_ax_tree_wrapper.h", - "extensions/automation_internal_custom_bindings.cc", - "extensions/automation_internal_custom_bindings.h", "extensions/chrome_extensions_dispatcher_delegate.cc", "extensions/chrome_extensions_dispatcher_delegate.h", "extensions/chrome_extensions_renderer_client.cc", @@ -327,7 +323,6 @@ "media/cast_transport_ipc.h", "media/cast_udp_transport.cc", "media/cast_udp_transport.h", - "resources/extensions/automation_custom_bindings.js", "resources/extensions/browser_action_custom_bindings.js", "resources/extensions/declarative_content_custom_bindings.js", "resources/extensions/enterprise_platform_keys_custom_bindings.js",
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc index b0b1a6e..d0f6fc72 100644 --- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc +++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -13,7 +13,6 @@ #include "chrome/common/crash_keys.h" #include "chrome/grit/renderer_resources.h" #include "chrome/renderer/extensions/app_hooks_delegate.h" -#include "chrome/renderer/extensions/automation_internal_custom_bindings.h" #include "chrome/renderer/extensions/cast_streaming_native_handler.h" #include "chrome/renderer/extensions/extension_hooks_delegate.h" #include "chrome/renderer/extensions/media_galleries_custom_bindings.h" @@ -52,11 +51,9 @@ using extensions::NativeHandler; -ChromeExtensionsDispatcherDelegate::ChromeExtensionsDispatcherDelegate() { -} +ChromeExtensionsDispatcherDelegate::ChromeExtensionsDispatcherDelegate() {} -ChromeExtensionsDispatcherDelegate::~ChromeExtensionsDispatcherDelegate() { -} +ChromeExtensionsDispatcherDelegate::~ChromeExtensionsDispatcherDelegate() {} void ChromeExtensionsDispatcherDelegate::RegisterNativeHandlers( extensions::Dispatcher* dispatcher, @@ -96,10 +93,6 @@ "cast_streaming_natives", std::make_unique<extensions::CastStreamingNativeHandler>( context, bindings_system)); - module_system->RegisterNativeHandler( - "automationInternal", - std::make_unique<extensions::AutomationInternalCustomBindings>( - context, bindings_system)); // The following are native handlers that are defined in //extensions, but // are only used for APIs defined in Chrome. @@ -118,9 +111,6 @@ void ChromeExtensionsDispatcherDelegate::PopulateSourceMap( extensions::ResourceBundleSourceMap* source_map) { // Custom bindings. - source_map->RegisterSource("automation", IDR_AUTOMATION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("automationEvent", IDR_AUTOMATION_EVENT_JS); - source_map->RegisterSource("automationNode", IDR_AUTOMATION_NODE_JS); source_map->RegisterSource("browserAction", IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS); source_map->RegisterSource("declarativeContent",
diff --git a/chrome/renderer/resources/extensions/automation/OWNERS b/chrome/renderer/resources/extensions/automation/OWNERS deleted file mode 100644 index e6cc90e..0000000 --- a/chrome/renderer/resources/extensions/automation/OWNERS +++ /dev/null
@@ -1,4 +0,0 @@ -aboxhall@chromium.org -dtseng@chromium.org - -# COMPONENT: UI>Accessibility
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd index d3c8dae..058e668 100644 --- a/chrome/renderer/resources/renderer_resources.grd +++ b/chrome/renderer/resources/renderer_resources.grd
@@ -23,9 +23,6 @@ <!-- Extension libraries. --> <if expr="enable_extensions"> <!-- Custom bindings for extension APIs. --> - <include name="IDR_AUTOMATION_CUSTOM_BINDINGS_JS" file="extensions\automation_custom_bindings.js" type="BINDATA" /> - <include name="IDR_AUTOMATION_EVENT_JS" file="extensions\automation\automation_event.js" type="BINDATA" /> - <include name="IDR_AUTOMATION_NODE_JS" file="extensions\automation\automation_node.js" type="BINDATA" /> <include name="IDR_BROWSER_ACTION_CUSTOM_BINDINGS_JS" file="extensions\browser_action_custom_bindings.js" type="BINDATA" /> <include name="IDR_CAST_STREAMING_RTP_STREAM_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_rtp_stream_custom_bindings.js" type="BINDATA" /> <include name="IDR_CAST_STREAMING_SESSION_CUSTOM_BINDINGS_JS" file="extensions\cast_streaming_session_custom_bindings.js" type="BINDATA" />
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 2e5e9123..f289b28 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -432,6 +432,10 @@ } else { sources += [ "base/browser_tests_main.cc" ] } + + if (is_win) { + deps += [ "//chrome/installer/util:strings" ] + } } if (is_win) { @@ -1846,6 +1850,7 @@ "../browser/chromeos/login/enrollment/mock_auto_enrollment_check_screen.cc", "../browser/chromeos/login/enrollment/mock_auto_enrollment_check_screen.h", "../browser/chromeos/login/enterprise_enrollment_browsertest.cc", + "../browser/chromeos/login/error_screen_browsertest.cc", "../browser/chromeos/login/eula_browsertest.cc", "../browser/chromeos/login/existing_user_controller_browsertest.cc", "../browser/chromeos/login/guest_login_browsertest.cc", @@ -4583,10 +4588,6 @@ "/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll", ] - # TODO(https://crbug.com/958955): Fix dupes and remove this flag. - configs -= [ "//build/config/compiler:fatal_linker_warnings_win" ] - ldflags += [ "/FORCE:MultipleRes" ] - if (is_chrome_branded) { sources += [ "../browser/conflicts/incompatible_applications_updater_win_unittest.cc", @@ -5347,6 +5348,7 @@ "//chrome:other_version", "//chrome/app:chrome_dll_resources", "//chrome/install_static:install_static_util", + "//chrome/installer/util:strings", "//third_party/isimpledom", "//third_party/webrtc/modules/desktop_capture", "//third_party/wtl", @@ -5812,10 +5814,6 @@ configs -= [ "//build/config/win:default_incremental_linking" ] configs += [ "//build/config/win:default_large_module_incremental_linking" ] - - # TODO(https://crbug.com/958955): Fix dupes and remove this flag. - configs -= [ "//build/config/compiler:fatal_linker_warnings_win" ] - ldflags = [ "/FORCE:MultipleRes" ] } else { sources -= [ "../app/chrome_version.rc.version" ] } @@ -5984,6 +5982,7 @@ if (is_win) { deps += [ "//chrome:other_version", + "//chrome/installer/util:strings", "//third_party/webrtc/modules/desktop_capture", "//third_party/wtl", "//ui/resources",
diff --git a/chrome/test/data/accessibility/image_annotation_link.html b/chrome/test/data/accessibility/image_annotation_link.html index eb55a07..72e1dbac 100644 --- a/chrome/test/data/accessibility/image_annotation_link.html +++ b/chrome/test/data/accessibility/image_annotation_link.html
@@ -27,4 +27,9 @@ </div> </a> + <!-- A positioned image should still annotate the link. --> + <a href="#"> + <img src="green.png" width=16 height=16 style="position: absolute"> + </a> + </body>
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn index 0f2ec3ec..1b6e8ba5 100644 --- a/chromecast/renderer/BUILD.gn +++ b/chromecast/renderer/BUILD.gn
@@ -92,10 +92,6 @@ "cast_extensions_dispatcher_delegate.g", "cast_extensions_renderer_client.cc", "cast_extensions_renderer_client.h", - "extensions/automation_ax_tree_wrapper.cc", - "extensions/automation_ax_tree_wrapper.h", - "extensions/automation_internal_custom_bindings.cc", - "extensions/automation_internal_custom_bindings.h", "extensions/extension_hooks_delegate.cc", "extensions/extension_hooks_delegate.h", "extensions/tabs_hooks_delegate.cc",
diff --git a/chromecast/renderer/cast_extensions_dispatcher_delegate.cc b/chromecast/renderer/cast_extensions_dispatcher_delegate.cc index 2bb6bd5..bb07b56 100644 --- a/chromecast/renderer/cast_extensions_dispatcher_delegate.cc +++ b/chromecast/renderer/cast_extensions_dispatcher_delegate.cc
@@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/strings/string_number_conversions.h" -#include "chromecast/renderer/extensions/automation_internal_custom_bindings.h" #include "chromecast/renderer/extensions/extension_hooks_delegate.h" #include "chromecast/renderer/extensions/tabs_hooks_delegate.h" #include "chromecast/renderer/grit/extensions_renderer_resources.h" @@ -39,11 +38,9 @@ using extensions::NativeHandler; -CastExtensionsDispatcherDelegate::CastExtensionsDispatcherDelegate() { -} +CastExtensionsDispatcherDelegate::CastExtensionsDispatcherDelegate() {} -CastExtensionsDispatcherDelegate::~CastExtensionsDispatcherDelegate() { -} +CastExtensionsDispatcherDelegate::~CastExtensionsDispatcherDelegate() {} void CastExtensionsDispatcherDelegate::RegisterNativeHandlers( extensions::Dispatcher* dispatcher, @@ -51,10 +48,6 @@ extensions::NativeExtensionBindingsSystem* bindings_system, extensions::ScriptContext* context) { module_system->RegisterNativeHandler( - "automationInternal", - std::make_unique<extensions::cast::AutomationInternalCustomBindings>( - context, bindings_system)); - module_system->RegisterNativeHandler( "lazy_background_page", std::make_unique<extensions::LazyBackgroundPageNativeHandler>(context)); } @@ -62,9 +55,6 @@ void CastExtensionsDispatcherDelegate::PopulateSourceMap( extensions::ResourceBundleSourceMap* source_map) { // Custom bindings. - source_map->RegisterSource("automation", IDR_AUTOMATION_CUSTOM_BINDINGS_JS); - source_map->RegisterSource("automationEvent", IDR_AUTOMATION_EVENT_JS); - source_map->RegisterSource("automationNode", IDR_AUTOMATION_NODE_JS); source_map->RegisterSource("tts", IDR_TTS_CUSTOM_BINDINGS_JS); }
diff --git a/chromecast/renderer/extensions/automation_ax_tree_wrapper.cc b/chromecast/renderer/extensions/automation_ax_tree_wrapper.cc deleted file mode 100644 index 99f7693..0000000 --- a/chromecast/renderer/extensions/automation_ax_tree_wrapper.cc +++ /dev/null
@@ -1,489 +0,0 @@ -// Copyright 2017 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 "base/no_destructor.h" -#include "chromecast/common/extensions_api/cast_extension_messages.h" -#include "chromecast/renderer/extensions/automation_internal_custom_bindings.h" -#include "extensions/common/extension_messages.h" -#include "ui/accessibility/ax_node.h" - -namespace extensions { -namespace cast { - -namespace { - -std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& GetChildTreeIDReverseMap() { - static base::NoDestructor<std::map<ui::AXTreeID, AutomationAXTreeWrapper*>> - child_tree_id_reverse_map; - return *child_tree_id_reverse_map; -} - -// Convert from ax::mojom::Event to api::automation::EventType. -api::automation::EventType ToAutomationEvent(ax::mojom::Event event_type) { - switch (event_type) { - case ax::mojom::Event::kNone: - return api::automation::EVENT_TYPE_NONE; - case ax::mojom::Event::kActiveDescendantChanged: - return api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED; - case ax::mojom::Event::kAlert: - return api::automation::EVENT_TYPE_ALERT; - case ax::mojom::Event::kAriaAttributeChanged: - return api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED; - case ax::mojom::Event::kAutocorrectionOccured: - return api::automation::EVENT_TYPE_AUTOCORRECTIONOCCURED; - case ax::mojom::Event::kBlur: - return api::automation::EVENT_TYPE_BLUR; - case ax::mojom::Event::kCheckedStateChanged: - return api::automation::EVENT_TYPE_CHECKEDSTATECHANGED; - case ax::mojom::Event::kChildrenChanged: - return api::automation::EVENT_TYPE_CHILDRENCHANGED; - case ax::mojom::Event::kClicked: - return api::automation::EVENT_TYPE_CLICKED; - case ax::mojom::Event::kDocumentSelectionChanged: - return api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED; - case ax::mojom::Event::kDocumentTitleChanged: - return api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED; - case ax::mojom::Event::kEndOfTest: - return api::automation::EVENT_TYPE_ENDOFTEST; - case ax::mojom::Event::kExpandedChanged: - return api::automation::EVENT_TYPE_EXPANDEDCHANGED; - case ax::mojom::Event::kFocus: - case ax::mojom::Event::kFocusContext: - return api::automation::EVENT_TYPE_FOCUS; - case ax::mojom::Event::kHide: - return api::automation::EVENT_TYPE_HIDE; - case ax::mojom::Event::kHitTestResult: - return api::automation::EVENT_TYPE_HITTESTRESULT; - case ax::mojom::Event::kHover: - return api::automation::EVENT_TYPE_HOVER; - case ax::mojom::Event::kImageFrameUpdated: - return api::automation::EVENT_TYPE_IMAGEFRAMEUPDATED; - case ax::mojom::Event::kInvalidStatusChanged: - return api::automation::EVENT_TYPE_INVALIDSTATUSCHANGED; - case ax::mojom::Event::kLayoutComplete: - return api::automation::EVENT_TYPE_LAYOUTCOMPLETE; - case ax::mojom::Event::kLiveRegionCreated: - return api::automation::EVENT_TYPE_LIVEREGIONCREATED; - case ax::mojom::Event::kLiveRegionChanged: - return api::automation::EVENT_TYPE_LIVEREGIONCHANGED; - case ax::mojom::Event::kLoadComplete: - return api::automation::EVENT_TYPE_LOADCOMPLETE; - case ax::mojom::Event::kLoadStart: - return api::automation::EVENT_TYPE_LOADSTART; - case ax::mojom::Event::kLocationChanged: - return api::automation::EVENT_TYPE_LOCATIONCHANGED; - case ax::mojom::Event::kMediaStartedPlaying: - return api::automation::EVENT_TYPE_MEDIASTARTEDPLAYING; - case ax::mojom::Event::kMediaStoppedPlaying: - return api::automation::EVENT_TYPE_MEDIASTOPPEDPLAYING; - case ax::mojom::Event::kMenuEnd: - return api::automation::EVENT_TYPE_MENUEND; - case ax::mojom::Event::kMenuListItemSelected: - return api::automation::EVENT_TYPE_MENULISTITEMSELECTED; - case ax::mojom::Event::kMenuListValueChanged: - return api::automation::EVENT_TYPE_MENULISTVALUECHANGED; - case ax::mojom::Event::kMenuPopupEnd: - return api::automation::EVENT_TYPE_MENUPOPUPEND; - case ax::mojom::Event::kMenuPopupHide: - return api::automation::EVENT_TYPE_MENUPOPUPHIDE; - case ax::mojom::Event::kMenuPopupStart: - return api::automation::EVENT_TYPE_MENUPOPUPSTART; - case ax::mojom::Event::kMenuStart: - return api::automation::EVENT_TYPE_MENUSTART; - case ax::mojom::Event::kMouseCanceled: - return api::automation::EVENT_TYPE_MOUSECANCELED; - case ax::mojom::Event::kMouseDragged: - return api::automation::EVENT_TYPE_MOUSEDRAGGED; - case ax::mojom::Event::kMouseMoved: - return api::automation::EVENT_TYPE_MOUSEMOVED; - case ax::mojom::Event::kMousePressed: - return api::automation::EVENT_TYPE_MOUSEPRESSED; - case ax::mojom::Event::kMouseReleased: - return api::automation::EVENT_TYPE_MOUSERELEASED; - case ax::mojom::Event::kRowCollapsed: - return api::automation::EVENT_TYPE_ROWCOLLAPSED; - case ax::mojom::Event::kRowCountChanged: - return api::automation::EVENT_TYPE_ROWCOUNTCHANGED; - case ax::mojom::Event::kRowExpanded: - return api::automation::EVENT_TYPE_ROWEXPANDED; - case ax::mojom::Event::kScrollPositionChanged: - return api::automation::EVENT_TYPE_SCROLLPOSITIONCHANGED; - case ax::mojom::Event::kScrolledToAnchor: - return api::automation::EVENT_TYPE_SCROLLEDTOANCHOR; - case ax::mojom::Event::kSelectedChildrenChanged: - return api::automation::EVENT_TYPE_SELECTEDCHILDRENCHANGED; - case ax::mojom::Event::kSelection: - return api::automation::EVENT_TYPE_SELECTION; - case ax::mojom::Event::kSelectionAdd: - return api::automation::EVENT_TYPE_SELECTIONADD; - case ax::mojom::Event::kSelectionRemove: - return api::automation::EVENT_TYPE_SELECTIONREMOVE; - case ax::mojom::Event::kShow: - return api::automation::EVENT_TYPE_SHOW; - case ax::mojom::Event::kStateChanged: - return api::automation::EVENT_TYPE_NONE; - case ax::mojom::Event::kTextChanged: - return api::automation::EVENT_TYPE_TEXTCHANGED; - case ax::mojom::Event::kTextSelectionChanged: - return api::automation::EVENT_TYPE_TEXTSELECTIONCHANGED; - case ax::mojom::Event::kWindowActivated: - return api::automation::EVENT_TYPE_WINDOWACTIVATED; - case ax::mojom::Event::kWindowDeactivated: - return api::automation::EVENT_TYPE_WINDOWDEACTIVATED; - case ax::mojom::Event::kTreeChanged: - return api::automation::EVENT_TYPE_TREECHANGED; - case ax::mojom::Event::kValueChanged: - return api::automation::EVENT_TYPE_VALUECHANGED; - } - - NOTREACHED(); - return api::automation::EVENT_TYPE_NONE; -} - -// Convert from ui::AXEventGenerator::Event to api::automation::EventType. -api::automation::EventType ToAutomationEvent( - ui::AXEventGenerator::Event event_type) { - switch (event_type) { - case ui::AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED: - return api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED; - case ui::AXEventGenerator::Event::ALERT: - return api::automation::EVENT_TYPE_ALERT; - case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED: - return api::automation::EVENT_TYPE_CHECKEDSTATECHANGED; - case ui::AXEventGenerator::Event::CHILDREN_CHANGED: - return api::automation::EVENT_TYPE_CHILDRENCHANGED; - case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: - return api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED; - case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED: - return api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED; - case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED: - return api::automation::EVENT_TYPE_INVALIDSTATUSCHANGED; - case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: - return api::automation::EVENT_TYPE_LIVEREGIONCHANGED; - case ui::AXEventGenerator::Event::LIVE_REGION_CREATED: - return api::automation::EVENT_TYPE_LIVEREGIONCREATED; - case ui::AXEventGenerator::Event::LOAD_COMPLETE: - return api::automation::EVENT_TYPE_LOADCOMPLETE; - case ui::AXEventGenerator::Event::LOAD_START: - return api::automation::EVENT_TYPE_LOADSTART; - case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED: - return api::automation::EVENT_TYPE_MENULISTITEMSELECTED; - case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED: - return api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED; - case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED: - return api::automation::EVENT_TYPE_ROWCOUNTCHANGED; - case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED: - case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED: - return api::automation::EVENT_TYPE_SCROLLPOSITIONCHANGED; - case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED: - return api::automation::EVENT_TYPE_SELECTEDCHILDRENCHANGED; - case ui::AXEventGenerator::Event::VALUE_CHANGED: - return api::automation::EVENT_TYPE_VALUECHANGED; - - // Map these into generic attribute changes (not necessarily aria related, - // but mapping for backward compat). - case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED: - case ui::AXEventGenerator::Event::COLLAPSED: - case ui::AXEventGenerator::Event::EXPANDED: - case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED: - case ui::AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED: - case ui::AXEventGenerator::Event::NAME_CHANGED: - case ui::AXEventGenerator::Event::ROLE_CHANGED: - case ui::AXEventGenerator::Event::SELECTED_CHANGED: - case ui::AXEventGenerator::Event::STATE_CHANGED: - return api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED; - - case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED: - case ui::AXEventGenerator::Event::ATOMIC_CHANGED: - case ui::AXEventGenerator::Event::BUSY_CHANGED: - case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED: - case ui::AXEventGenerator::Event::CONTROLS_CHANGED: - case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED: - case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED: - case ui::AXEventGenerator::Event::ENABLED_CHANGED: - case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED: - case ui::AXEventGenerator::Event::FLOW_TO_CHANGED: - case ui::AXEventGenerator::Event::HASPOPUP_CHANGED: - case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED: - case ui::AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED: - case ui::AXEventGenerator::Event::LABELED_BY_CHANGED: - case ui::AXEventGenerator::Event::LANGUAGE_CHANGED: - case ui::AXEventGenerator::Event::LAYOUT_INVALIDATED: - case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED: - case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED: - case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED: - case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED: - case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED: - case ui::AXEventGenerator::Event::POSITION_IN_SET_CHANGED: - case ui::AXEventGenerator::Event::READONLY_CHANGED: - case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED: - case ui::AXEventGenerator::Event::SET_SIZE_CHANGED: - case ui::AXEventGenerator::Event::SUBTREE_CREATED: - case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED: - case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED: - case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED: - return api::automation::EVENT_TYPE_NONE; - } - - NOTREACHED(); - return api::automation::EVENT_TYPE_NONE; -} - -} // namespace - -AutomationAXTreeWrapper::AutomationAXTreeWrapper( - ui::AXTreeID tree_id, - AutomationInternalCustomBindings* owner) - : tree_id_(tree_id), owner_(owner), event_generator_(&tree_) { - tree_.AddObserver(this); -} - -AutomationAXTreeWrapper::~AutomationAXTreeWrapper() { - // Stop observing so we don't get a callback for every node being deleted. - event_generator_.SetTree(nullptr); - tree_.RemoveObserver(this); -} - -// static -AutomationAXTreeWrapper* AutomationAXTreeWrapper::GetParentOfTreeId( - ui::AXTreeID tree_id) { - std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& child_tree_id_reverse_map = - GetChildTreeIDReverseMap(); - const auto& iter = child_tree_id_reverse_map.find(tree_id); - if (iter != child_tree_id_reverse_map.end()) - return iter->second; - - return nullptr; -} - -bool AutomationAXTreeWrapper::OnAccessibilityEvents( - const ExtensionMsg_AccessibilityEventBundleParams& event_bundle, - bool is_active_profile) { - std::map<ui::AXTreeID, AutomationAXTreeWrapper*>& child_tree_id_reverse_map = - GetChildTreeIDReverseMap(); - for (const ui::AXTreeID& tree_id : tree_.GetAllChildTreeIds()) { - DCHECK_EQ(child_tree_id_reverse_map[tree_id], this); - child_tree_id_reverse_map.erase(tree_id); - } - - for (const auto& update : event_bundle.updates) { - event_generator_.set_event_from(update.event_from); - deleted_node_ids_.clear(); - did_send_tree_change_during_unserialization_ = false; - - if (!tree_.Unserialize(update)) - return false; - - if (is_active_profile) { - owner_->SendNodesRemovedEvent(&tree_, deleted_node_ids_); - - if (update.nodes.size() && did_send_tree_change_during_unserialization_) { - ui::AXNode* target = tree_.GetFromId(update.nodes[0].id); - if (target) { - owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND, &tree_, - target); - } - } - } - } - - for (const ui::AXTreeID& tree_id : tree_.GetAllChildTreeIds()) { - DCHECK(!base::ContainsKey(child_tree_id_reverse_map, tree_id)); - child_tree_id_reverse_map.insert(std::make_pair(tree_id, this)); - } - - // Exit early if this isn't the active profile. - if (!is_active_profile) - return true; - - // Send all blur and focus events first. This ensures we correctly dispatch - // these events, which gets re-targetted in the js bindings and ensures it - // receives the correct value for |event_from|. - for (const auto& event : event_bundle.events) { - if (event.event_type != ax::mojom::Event::kFocus && - event.event_type != ax::mojom::Event::kBlur) - continue; - - api::automation::EventType automation_event_type = - ToAutomationEvent(event.event_type); - owner_->SendAutomationEvent(event_bundle.tree_id, - event_bundle.mouse_location, event, - automation_event_type); - } - - // Send auto-generated AXEventGenerator events. - for (const auto& targeted_event : event_generator_) { - api::automation::EventType event_type = - ToAutomationEvent(targeted_event.event_params.event); - if (IsEventTypeHandledByAXEventGenerator(event_type)) { - ui::AXEvent generated_event; - generated_event.id = targeted_event.node->id(); - generated_event.event_from = targeted_event.event_params.event_from; - owner_->SendAutomationEvent(event_bundle.tree_id, - event_bundle.mouse_location, generated_event, - event_type); - } - } - event_generator_.ClearEvents(); - - for (const auto& event : event_bundle.events) { - if (event.event_type == ax::mojom::Event::kFocus || - event.event_type == ax::mojom::Event::kBlur) - continue; - - api::automation::EventType automation_event_type = - ToAutomationEvent(event.event_type); - - // Send some events directly from the event message, if they're not - // handled by AXEventGenerator yet. - if (!IsEventTypeHandledByAXEventGenerator(automation_event_type)) { - owner_->SendAutomationEvent(event_bundle.tree_id, - event_bundle.mouse_location, event, - automation_event_type); - } - } - - return true; -} - -bool AutomationAXTreeWrapper::IsDesktopTree() const { - return tree_.root()->data().role == ax::mojom::Role::kDesktop; -} - -void AutomationAXTreeWrapper::OnNodeDataWillChange( - ui::AXTree* tree, - const ui::AXNodeData& old_node_data, - const ui::AXNodeData& new_node_data) { - if (old_node_data.GetStringAttribute(ax::mojom::StringAttribute::kName) != - new_node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)) - text_changed_node_ids_.push_back(new_node_data.id); -} - -void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree, - ui::AXNode* node) { - did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_NODEREMOVED, tree, node); - deleted_node_ids_.push_back(node->id()); -} - -void AutomationAXTreeWrapper::OnAtomicUpdateFinished( - ui::AXTree* tree, - bool root_changed, - const std::vector<ui::AXTreeObserver::Change>& changes) { - DCHECK_EQ(&tree_, tree); - for (const auto change : changes) { - ui::AXNode* node = change.node; - switch (change.type) { - case NODE_CREATED: - did_send_tree_change_during_unserialization_ |= - owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_NODECREATED, tree, node); - break; - case SUBTREE_CREATED: - did_send_tree_change_during_unserialization_ |= - owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, tree, node); - break; - case NODE_CHANGED: - did_send_tree_change_during_unserialization_ |= - owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_NODECHANGED, tree, node); - break; - // Unhandled. - case NODE_REPARENTED: - case SUBTREE_REPARENTED: - break; - } - } - - for (int id : text_changed_node_ids_) { - did_send_tree_change_during_unserialization_ |= owner_->SendTreeChangeEvent( - api::automation::TREE_CHANGE_TYPE_TEXTCHANGED, tree, - tree->GetFromId(id)); - } - text_changed_node_ids_.clear(); -} - -bool AutomationAXTreeWrapper::IsEventTypeHandledByAXEventGenerator( - api::automation::EventType event_type) const { - switch (event_type) { - // Generated by AXEventGenerator. - case api::automation::EVENT_TYPE_ACTIVEDESCENDANTCHANGED: - case api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED: - case api::automation::EVENT_TYPE_CHECKEDSTATECHANGED: - case api::automation::EVENT_TYPE_CHILDRENCHANGED: - case api::automation::EVENT_TYPE_DOCUMENTSELECTIONCHANGED: - case api::automation::EVENT_TYPE_DOCUMENTTITLECHANGED: - case api::automation::EVENT_TYPE_EXPANDEDCHANGED: - case api::automation::EVENT_TYPE_INVALIDSTATUSCHANGED: - case api::automation::EVENT_TYPE_LIVEREGIONCHANGED: - case api::automation::EVENT_TYPE_LIVEREGIONCREATED: - case api::automation::EVENT_TYPE_LOADCOMPLETE: - case api::automation::EVENT_TYPE_LOADSTART: - case api::automation::EVENT_TYPE_ROWCOLLAPSED: - case api::automation::EVENT_TYPE_ROWCOUNTCHANGED: - case api::automation::EVENT_TYPE_ROWEXPANDED: - case api::automation::EVENT_TYPE_SCROLLPOSITIONCHANGED: - case api::automation::EVENT_TYPE_SELECTEDCHILDRENCHANGED: - return true; - - // Not generated by AXEventGenerator and possible candidates - // for removal from the automation API entirely. - case api::automation::EVENT_TYPE_HIDE: - case api::automation::EVENT_TYPE_LAYOUTCOMPLETE: - case api::automation::EVENT_TYPE_MENULISTVALUECHANGED: - case api::automation::EVENT_TYPE_MENUPOPUPEND: - case api::automation::EVENT_TYPE_MENUPOPUPHIDE: - case api::automation::EVENT_TYPE_MENUPOPUPSTART: - case api::automation::EVENT_TYPE_SELECTIONADD: - case api::automation::EVENT_TYPE_SELECTIONREMOVE: - case api::automation::EVENT_TYPE_SHOW: - case api::automation::EVENT_TYPE_STATECHANGED: - case api::automation::EVENT_TYPE_TREECHANGED: - return false; - - // These events will never be generated by AXEventGenerator. - // These are all events that can't be inferred from a tree change. - case api::automation::EVENT_TYPE_NONE: - case api::automation::EVENT_TYPE_AUTOCORRECTIONOCCURED: - case api::automation::EVENT_TYPE_CLICKED: - case api::automation::EVENT_TYPE_ENDOFTEST: - case api::automation::EVENT_TYPE_FOCUSCONTEXT: - case api::automation::EVENT_TYPE_HITTESTRESULT: - case api::automation::EVENT_TYPE_HOVER: - case api::automation::EVENT_TYPE_MEDIASTARTEDPLAYING: - case api::automation::EVENT_TYPE_MEDIASTOPPEDPLAYING: - case api::automation::EVENT_TYPE_MOUSECANCELED: - case api::automation::EVENT_TYPE_MOUSEDRAGGED: - case api::automation::EVENT_TYPE_MOUSEMOVED: - case api::automation::EVENT_TYPE_MOUSEPRESSED: - case api::automation::EVENT_TYPE_MOUSERELEASED: - case api::automation::EVENT_TYPE_SCROLLEDTOANCHOR: - case api::automation::EVENT_TYPE_WINDOWACTIVATED: - case api::automation::EVENT_TYPE_WINDOWDEACTIVATED: - return false; - - // These events might need to be migrated to AXEventGenerator. - case api::automation::EVENT_TYPE_ALERT: - case api::automation::EVENT_TYPE_BLUR: - case api::automation::EVENT_TYPE_FOCUS: - case api::automation::EVENT_TYPE_IMAGEFRAMEUPDATED: - case api::automation::EVENT_TYPE_LOCATIONCHANGED: - case api::automation::EVENT_TYPE_MENUEND: - case api::automation::EVENT_TYPE_MENULISTITEMSELECTED: - case api::automation::EVENT_TYPE_MENUSTART: - case api::automation::EVENT_TYPE_SELECTION: - case api::automation::EVENT_TYPE_TEXTCHANGED: - case api::automation::EVENT_TYPE_TEXTSELECTIONCHANGED: - case api::automation::EVENT_TYPE_VALUECHANGED: - return false; - } - - NOTREACHED(); - return false; -} - -} // namespace cast -} // namespace extensions
diff --git a/chromecast/renderer/extensions/automation_ax_tree_wrapper.h b/chromecast/renderer/extensions/automation_ax_tree_wrapper.h deleted file mode 100644 index faefb07..0000000 --- a/chromecast/renderer/extensions/automation_ax_tree_wrapper.h +++ /dev/null
@@ -1,79 +0,0 @@ -// Copyright 2017 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 CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_ -#define CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_ - -#include "extensions/common/api/automation.h" -#include "ui/accessibility/ax_event_generator.h" -#include "ui/accessibility/ax_tree.h" - -struct ExtensionMsg_AccessibilityEventBundleParams; - -namespace extensions { -namespace cast { - -class AutomationInternalCustomBindings; - -// A class that wraps one AXTree and all of the additional state -// and helper methods needed to use it for the automation API. -class AutomationAXTreeWrapper : public ui::AXTreeObserver { - public: - AutomationAXTreeWrapper(ui::AXTreeID tree_id, - AutomationInternalCustomBindings* owner); - ~AutomationAXTreeWrapper() override; - - // Returns the AutomationAXTreeWrapper that lists |tree_id| as one of its - // child trees, if any. - static AutomationAXTreeWrapper* GetParentOfTreeId(ui::AXTreeID tree_id); - - ui::AXTreeID tree_id() const { return tree_id_; } - ui::AXTree* tree() { return &tree_; } - AutomationInternalCustomBindings* owner() { return owner_; } - - // Called by AutomationInternalCustomBindings::OnAccessibilityEvents on - // the AutomationAXTreeWrapper instance for the correct tree corresponding - // to this event. Unserializes the tree update and calls back to - // AutomationInternalCustomBindings to fire any automation events needed. - bool OnAccessibilityEvents( - const ExtensionMsg_AccessibilityEventBundleParams& events, - bool is_active_profile); - - // Returns true if this is the desktop tree. - bool IsDesktopTree() const; - - private: - // AXTreeObserver overrides. - void OnNodeDataWillChange(ui::AXTree* tree, - const ui::AXNodeData& old_node_data, - const ui::AXNodeData& new_node_data) override; - void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; - void OnAtomicUpdateFinished(ui::AXTree* tree, - bool root_changed, - const std::vector<Change>& changes) override; - - // Given an event, return true if the event is handled by - // AXEventGenerator, and false if it's not. Temporary, this will be - // removed with the AXEventGenerator refactoring is complete. - bool IsEventTypeHandledByAXEventGenerator( - extensions::api::automation::EventType) const; - - ui::AXTreeID tree_id_; - ui::AXTree tree_; - AutomationInternalCustomBindings* owner_; - std::vector<int> deleted_node_ids_; - std::vector<int> text_changed_node_ids_; - ui::AXEventGenerator event_generator_; - - // Tracks whether a tree change event was sent during unserialization. Tree - // changes outside of unserialization do not get reflected here. The value is - // reset after unserialization. - bool did_send_tree_change_during_unserialization_ = false; - DISALLOW_COPY_AND_ASSIGN(AutomationAXTreeWrapper); -}; - -} // namespace cast -} // namespace extensions - -#endif // CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_
diff --git a/chromecast/renderer/extensions/automation_internal_custom_bindings.cc b/chromecast/renderer/extensions/automation_internal_custom_bindings.cc deleted file mode 100644 index 540ae29..0000000 --- a/chromecast/renderer/extensions/automation_internal_custom_bindings.cc +++ /dev/null
@@ -1,1883 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chromecast/renderer/extensions/automation_internal_custom_bindings.h" - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <memory> -#include <set> -#include <string> -#include <utility> - -#include "base/bind.h" -#include "base/i18n/string_search.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/values.h" -#include "chromecast/common/extensions_api/cast_extension_messages.h" -#include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_thread.h" -#include "content/public/renderer/render_view.h" -#include "extensions/common/extension.h" -#include "extensions/common/extension_messages.h" -#include "extensions/common/manifest.h" -#include "extensions/common/manifest_handlers/automation.h" -#include "extensions/common/manifest_handlers/background_info.h" -#include "extensions/renderer/native_extension_bindings_system.h" -#include "extensions/renderer/script_context.h" -#include "gin/converter.h" -#include "gin/data_object_builder.h" -#include "ipc/message_filter.h" -#include "ui/accessibility/ax_enum_util.h" -#include "ui/accessibility/ax_enums.mojom.h" -#include "ui/accessibility/ax_event.h" -#include "ui/accessibility/ax_node.h" -#include "ui/accessibility/ax_role_properties.h" -#include "ui/gfx/geometry/rect_conversions.h" - -namespace extensions { -namespace cast { - -namespace { - -void ThrowInvalidArgumentsException( - AutomationInternalCustomBindings* automation_bindings) { - v8::Isolate* isolate = automation_bindings->GetIsolate(); - automation_bindings->GetIsolate()->ThrowException( - v8::String::NewFromUtf8( - isolate, - "Invalid arguments to AutomationInternalCustomBindings function", - v8::NewStringType::kNormal) - .ToLocalChecked()); - - LOG(FATAL) << "Invalid arguments to AutomationInternalCustomBindings function" - << automation_bindings->context()->GetStackTraceAsString(); -} - -v8::Local<v8::String> CreateV8String(v8::Isolate* isolate, - base::StringPiece str) { - return gin::StringToSymbol(isolate, str); -} - -v8::Local<v8::Object> RectToV8Object(v8::Isolate* isolate, - const gfx::Rect& rect) { - return gin::DataObjectBuilder(isolate) - .Set("left", rect.x()) - .Set("top", rect.y()) - .Set("width", rect.width()) - .Set("height", rect.height()) - .Build(); -} - -// Adjust the bounding box of a node from local to global coordinates, -// walking up the parent hierarchy to offset by frame offsets and -// scroll offsets. -// If |clip_bounds| is false, the bounds of the node will not be clipped -// to the ancestors bounding boxes if needed. Regardless of clipping, results -// are returned in global coordinates. -static gfx::Rect ComputeGlobalNodeBounds(AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - gfx::RectF local_bounds = gfx::RectF(), - bool* offscreen = nullptr, - bool clip_bounds = true) { - gfx::RectF bounds = local_bounds; - - while (node) { - bounds = tree_wrapper->tree()->RelativeToTreeBounds(node, bounds, offscreen, - clip_bounds); - - if (!tree_wrapper->owner()) - break; - - AutomationAXTreeWrapper* previous_tree_wrapper = tree_wrapper; - ui::AXNode* parent = tree_wrapper->owner()->GetParent( - tree_wrapper->tree()->root(), &tree_wrapper); - if (parent == node) - break; - - // All trees other than the desktop tree are scaled by the device - // scale factor. When crossing out of another tree into the desktop - // tree, unscale the bounds by the device scale factor. - if (!previous_tree_wrapper->IsDesktopTree() && - tree_wrapper->IsDesktopTree()) { - float scale_factor = tree_wrapper->owner()->GetDeviceScaleFactor(); - if (scale_factor > 0) - bounds.Scale(1.0 / scale_factor); - } - - node = parent; - } - - return gfx::ToEnclosingRect(bounds); -} - -// -// Helper class that helps implement bindings for a JavaScript function -// that takes a single input argument consisting of a Tree ID. Looks up -// the AutomationAXTreeWrapper and passes it to the function passed to the -// constructor. -// - -typedef void (*TreeIDFunction)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper); - -class TreeIDWrapper : public base::RefCountedThreadSafe<TreeIDWrapper> { - public: - TreeIDWrapper(AutomationInternalCustomBindings* automation_bindings, - TreeIDFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() != 1 || !args[0]->IsString()) - ThrowInvalidArgumentsException(automation_bindings_); - - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - // The root can be null if this is called from an onTreeChange callback. - if (!tree_wrapper->tree()->root()) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper); - } - - private: - virtual ~TreeIDWrapper() {} - - friend class base::RefCountedThreadSafe<TreeIDWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - TreeIDFunction function_; -}; - -// -// Helper class that helps implement bindings for a JavaScript function -// that takes two input arguments: a tree ID and node ID. Looks up the -// AutomationAXTreeWrapper and the AXNode and passes them to the function passed -// to the constructor. -// -typedef std::function<void(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node)> - NodeIDFunction; - -class NodeIDWrapper : public base::RefCountedThreadSafe<NodeIDWrapper> { - public: - NodeIDWrapper(AutomationInternalCustomBindings* automation_bindings, - NodeIDFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsNumber()) - ThrowInvalidArgumentsException(automation_bindings_); - - v8::Local<v8::Context> context = - automation_bindings_->context()->v8_context(); - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context).FromMaybe(0); - - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper, node); - } - - private: - virtual ~NodeIDWrapper() {} - - friend class base::RefCountedThreadSafe<NodeIDWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - NodeIDFunction function_; -}; - -// -// Helper class that helps implement bindings for a JavaScript function -// that takes three input arguments: a tree ID, node ID, and string -// argument. Looks up the AutomationAXTreeWrapper and the AXNode and passes them -// to the function passed to the constructor. -// - -typedef void (*NodeIDPlusAttributeFunction)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, - ui::AXNode* node, - const std::string& attribute); - -class NodeIDPlusAttributeWrapper - : public base::RefCountedThreadSafe<NodeIDPlusAttributeWrapper> { - public: - NodeIDPlusAttributeWrapper( - AutomationInternalCustomBindings* automation_bindings, - NodeIDPlusAttributeFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsNumber() || - !args[2]->IsString()) { - ThrowInvalidArgumentsException(automation_bindings_); - } - - v8::Local<v8::Context> context = - automation_bindings_->context()->v8_context(); - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context).FromMaybe(0); - std::string attribute = *v8::String::Utf8Value(isolate, args[2]); - - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper->tree(), node, - attribute); - } - - private: - virtual ~NodeIDPlusAttributeWrapper() {} - - friend class base::RefCountedThreadSafe<NodeIDPlusAttributeWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - NodeIDPlusAttributeFunction function_; -}; - -// -// Helper class that helps implement bindings for a JavaScript function -// that takes four input arguments: a tree ID, node ID, and integer start -// and end indices. Looks up the AutomationAXTreeWrapper and the AXNode and -// passes them to the function passed to the constructor. -// - -typedef void (*NodeIDPlusRangeFunction)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - int start, - int end); - -class NodeIDPlusRangeWrapper - : public base::RefCountedThreadSafe<NodeIDPlusRangeWrapper> { - public: - NodeIDPlusRangeWrapper(AutomationInternalCustomBindings* automation_bindings, - NodeIDPlusRangeFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() < 4 || !args[0]->IsString() || !args[1]->IsNumber() || - !args[2]->IsNumber() || !args[3]->IsNumber()) { - ThrowInvalidArgumentsException(automation_bindings_); - } - - v8::Local<v8::Context> context = - automation_bindings_->context()->v8_context(); - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context).FromMaybe(0); - int start = args[2]->Int32Value(context).FromMaybe(0); - int end = args[3]->Int32Value(context).FromMaybe(0); - - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper, node, start, end); - } - - private: - virtual ~NodeIDPlusRangeWrapper() {} - - friend class base::RefCountedThreadSafe<NodeIDPlusRangeWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - NodeIDPlusRangeFunction function_; -}; - -typedef std::function<void(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - const std::string& strVal, - bool boolVal)> - NodeIDPlusStringBoolFunction; - -class NodeIDPlusStringBoolWrapper - : public base::RefCountedThreadSafe<NodeIDPlusStringBoolWrapper> { - public: - NodeIDPlusStringBoolWrapper( - AutomationInternalCustomBindings* automation_bindings, - NodeIDPlusStringBoolFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() < 4 || !args[0]->IsString() || !args[1]->IsNumber() || - !args[2]->IsString() || !args[3]->IsBoolean()) { - ThrowInvalidArgumentsException(automation_bindings_); - } - - v8::Local<v8::Context> context = - automation_bindings_->context()->v8_context(); - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context).FromMaybe(0); - std::string str_val = *v8::String::Utf8Value(isolate, args[2]); - bool bool_val = args[3].As<v8::Boolean>()->Value(); - - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper, node, str_val, - bool_val); - } - - private: - virtual ~NodeIDPlusStringBoolWrapper() {} - - friend class base::RefCountedThreadSafe<NodeIDPlusStringBoolWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - NodeIDPlusStringBoolFunction function_; -}; - -using NodeIDPlusDimensionsFunction = - void (*)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - int x, - int y, - int width, - int height); - -class NodeIDPlusDimensionsWrapper - : public base::RefCountedThreadSafe<NodeIDPlusDimensionsWrapper> { - public: - NodeIDPlusDimensionsWrapper( - AutomationInternalCustomBindings* automation_bindings, - NodeIDPlusDimensionsFunction function) - : automation_bindings_(automation_bindings), function_(function) {} - - void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = automation_bindings_->GetIsolate(); - if (args.Length() < 6 || !args[0]->IsString() || !args[1]->IsInt32() || - !args[2]->IsInt32() || !args[3]->IsInt32() || !args[4]->IsInt32() || - !args[5]->IsInt32()) { - ThrowInvalidArgumentsException(automation_bindings_); - } - - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1].As<v8::Int32>()->Value(); - int x = args[2].As<v8::Int32>()->Value(); - int y = args[3].As<v8::Int32>()->Value(); - int width = args[4].As<v8::Int32>()->Value(); - int height = args[5].As<v8::Int32>()->Value(); - - AutomationAXTreeWrapper* tree_wrapper = - automation_bindings_->GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - function_(isolate, args.GetReturnValue(), tree_wrapper, node, x, y, width, - height); - } - - private: - virtual ~NodeIDPlusDimensionsWrapper() {} - - friend class base::RefCountedThreadSafe<NodeIDPlusDimensionsWrapper>; - - AutomationInternalCustomBindings* automation_bindings_; - NodeIDPlusDimensionsFunction function_; -}; -} // namespace - -class AutomationMessageFilter : public IPC::MessageFilter { - public: - explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) - : owner_(owner), removed_(false) { - DCHECK(owner); - content::RenderThread::Get()->AddFilter(this); - task_runner_ = base::ThreadTaskRunnerHandle::Get(); - } - - void Detach() { - owner_ = nullptr; - Remove(); - } - - // IPC::MessageFilter - bool OnMessageReceived(const IPC::Message& message) override { - task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - &AutomationMessageFilter::OnMessageReceivedOnRenderThread, this, - message)); - - // Always return false in case there are multiple - // AutomationInternalCustomBindings instances attached to the same thread. - return false; - } - - void OnFilterRemoved() override { removed_ = true; } - - private: - void OnMessageReceivedOnRenderThread(const IPC::Message& message) { - if (owner_) - owner_->OnMessageReceived(message); - } - - ~AutomationMessageFilter() override { Remove(); } - - void Remove() { - if (!removed_) { - removed_ = true; - content::RenderThread::Get()->RemoveFilter(this); - } - } - - AutomationInternalCustomBindings* owner_; - bool removed_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - - DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter); -}; - -AutomationInternalCustomBindings::AutomationInternalCustomBindings( - ScriptContext* context, - NativeExtensionBindingsSystem* bindings_system) - : ObjectBackedNativeHandler(context), - is_active_profile_(true), - tree_change_observer_overall_filter_(0), - bindings_system_(bindings_system), - should_ignore_context_(false) { - // We will ignore this instance if the extension has a background page and - // this context is not that background page. In all other cases, we will have - // multiple instances floating around in the same process. - if (context && context->extension()) { - const GURL background_page_url = - extensions::BackgroundInfo::GetBackgroundURL(context->extension()); - should_ignore_context_ = - background_page_url != "" && background_page_url != context->url(); - } -} - -AutomationInternalCustomBindings::~AutomationInternalCustomBindings() {} - -void AutomationInternalCustomBindings::AddRoutes() { -// It's safe to use base::Unretained(this) here because these bindings -// will only be called on a valid AutomationInternalCustomBindings instance -// and none of the functions have any side effects. -#define ROUTE_FUNCTION(FN) \ - RouteHandlerFunction( \ - #FN, "automation", \ - base::BindRepeating(&AutomationInternalCustomBindings::FN, \ - base::Unretained(this))) - ROUTE_FUNCTION(IsInteractPermitted); - ROUTE_FUNCTION(GetSchemaAdditions); - ROUTE_FUNCTION(StartCachingAccessibilityTrees); - ROUTE_FUNCTION(DestroyAccessibilityTree); - ROUTE_FUNCTION(AddTreeChangeObserver); - ROUTE_FUNCTION(RemoveTreeChangeObserver); - ROUTE_FUNCTION(GetChildIDAtIndex); - ROUTE_FUNCTION(GetFocus); - ROUTE_FUNCTION(GetHtmlAttributes); - ROUTE_FUNCTION(GetState); -#undef ROUTE_FUNCTION - - // Bindings that take a Tree ID and return a property of the tree. - - RouteTreeIDFunction("GetRootID", [](v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Integer::New(isolate, tree_wrapper->tree()->root()->id())); - }); - RouteTreeIDFunction("GetDocURL", [](v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::String::NewFromUtf8(isolate, - tree_wrapper->tree()->data().url.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteTreeIDFunction( - "GetDocTitle", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::String::NewFromUtf8( - isolate, tree_wrapper->tree()->data().title.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteTreeIDFunction( - "GetDocLoaded", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set( - v8::Boolean::New(isolate, tree_wrapper->tree()->data().loaded)); - }); - RouteTreeIDFunction( - "GetDocLoadingProgress", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Number::New( - isolate, tree_wrapper->tree()->data().loading_progress)); - }); - RouteTreeIDFunction( - "GetAnchorObjectID", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Number::New( - isolate, tree_wrapper->tree()->data().sel_anchor_object_id)); - }); - RouteTreeIDFunction( - "GetAnchorOffset", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Number::New( - isolate, tree_wrapper->tree()->data().sel_anchor_offset)); - }); - RouteTreeIDFunction( - "GetAnchorAffinity", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(CreateV8String( - isolate, - ui::ToString(tree_wrapper->tree()->data().sel_anchor_affinity))); - }); - RouteTreeIDFunction( - "GetFocusObjectID", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Number::New( - isolate, tree_wrapper->tree()->data().sel_focus_object_id)); - }); - RouteTreeIDFunction( - "GetFocusOffset", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(v8::Number::New( - isolate, tree_wrapper->tree()->data().sel_focus_offset)); - }); - RouteTreeIDFunction( - "GetFocusAffinity", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper) { - result.Set(CreateV8String( - isolate, - ui::ToString(tree_wrapper->tree()->data().sel_focus_affinity))); - }); - - // Bindings that take a Tree ID and Node ID and return a property of the node. - - RouteNodeIDFunction( - "GetParentID", - [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - ui::AXNode* parent = GetParent(node, &tree_wrapper); - if (parent) { - gin::DataObjectBuilder response(isolate); - response.Set("treeId", tree_wrapper->tree_id().ToString()); - response.Set("nodeId", parent->id()); - result.Set(response.Build()); - } - }); - RouteNodeIDFunction( - "GetChildCount", - [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - int child_count; - if (GetRootOfChildTree(&node, &tree_wrapper)) - child_count = 1; - else - child_count = node->child_count(); - - result.Set(v8::Integer::New(isolate, child_count)); - }); - RouteNodeIDFunction( - "GetIndexInParent", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - result.Set(v8::Integer::New(isolate, node->index_in_parent())); - }); - RouteNodeIDFunction( - "GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - std::string role_name = ui::ToString(node->data().role); - result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteNodeIDFunction( - "GetLocation", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - gfx::Rect global_clipped_bounds = - ComputeGlobalNodeBounds(tree_wrapper, node); - result.Set(RectToV8Object(isolate, global_clipped_bounds)); - }); - RouteNodeIDFunction( - "GetUnclippedLocation", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - bool offscreen = false; - gfx::Rect global_unclipped_bounds = - ComputeGlobalNodeBounds(tree_wrapper, node, gfx::RectF(), - &offscreen, false /* clip_bounds */); - result.Set(RectToV8Object(isolate, global_unclipped_bounds)); - }); - RouteNodeIDFunction( - "GetLineStartOffsets", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - const std::vector<int> line_starts = - node->GetOrComputeLineStartOffsets(); - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result( - v8::Array::New(isolate, line_starts.size())); - for (size_t i = 0; i < line_starts.size(); ++i) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(i), - v8::Integer::New(isolate, line_starts[i])) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDFunction( - "GetChildIDs", - [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - std::vector<int> child_ids; - if (GetRootOfChildTree(&node, &tree_wrapper)) { - child_ids.push_back(node->id()); - } else { - const std::vector<ui::AXNode*>& children = node->children(); - for (size_t i = 0; i < children.size(); ++i) - child_ids.push_back(children[i]->id()); - } - - gin::DataObjectBuilder response(isolate); - response.Set("treeId", tree_wrapper->tree_id().ToString()); - response.Set("nodeIds", child_ids); - result.Set(response.Build()); - }); - - // Bindings that take a Tree ID and Node ID and string attribute name - // and return a property of the node. - - RouteNodeIDPlusRangeFunction( - "GetBoundsForRange", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, int start, - int end) { - if (node->data().role != ax::mojom::Role::kInlineTextBox) - return; - - // Use character offsets to compute the local bounds of this subrange. - gfx::RectF local_bounds(0, 0, - node->data().relative_bounds.bounds.width(), - node->data().relative_bounds.bounds.height()); - std::string name = - node->data().GetStringAttribute(ax::mojom::StringAttribute::kName); - std::vector<int> character_offsets = node->data().GetIntListAttribute( - ax::mojom::IntListAttribute::kCharacterOffsets); - int len = - static_cast<int>(std::min(name.size(), character_offsets.size())); - if (start >= 0 && start <= end && end <= len) { - int start_offset = start > 0 ? character_offsets[start - 1] : 0; - int end_offset = end > 0 ? character_offsets[end - 1] : 0; - - switch (node->data().GetTextDirection()) { - case ax::mojom::TextDirection::kLtr: - default: - local_bounds.set_x(local_bounds.x() + start_offset); - local_bounds.set_width(end_offset - start_offset); - break; - case ax::mojom::TextDirection::kRtl: - local_bounds.set_x(local_bounds.x() + local_bounds.width() - - end_offset); - local_bounds.set_width(end_offset - start_offset); - break; - case ax::mojom::TextDirection::kTtb: - local_bounds.set_y(local_bounds.y() + start_offset); - local_bounds.set_height(end_offset - start_offset); - break; - case ax::mojom::TextDirection::kBtt: - local_bounds.set_y(local_bounds.y() + local_bounds.height() - - end_offset); - local_bounds.set_height(end_offset - start_offset); - break; - } - } - - // Convert from local to global coordinates second, after subsetting, - // because the local to global conversion might involve matrix - // transformations. - gfx::Rect global_bounds = ComputeGlobalNodeBounds( - tree_wrapper, node, local_bounds, nullptr, true /* clip_bounds */); - result.Set(RectToV8Object(isolate, global_bounds)); - }); - - RouteNodeIDPlusDimensionsFunction( - "ComputeGlobalBounds", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, int x, int y, - int width, int height) { - gfx::RectF local_bounds(x, y, width, height); - - // Convert from local coordinates in Android window, to global - // coordinates spanning entire screen. - gfx::Rect global_bounds = ComputeGlobalNodeBounds( - tree_wrapper, node, local_bounds, nullptr, false /* clip_bounds */); - result.Set(RectToV8Object(isolate, global_bounds)); - }); - - // Bindings that take a Tree ID and Node ID and string attribute name - // and return a property of the node. - - RouteNodeIDPlusAttributeFunction( - "GetStringAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::StringAttribute attribute = - ui::ParseStringAttribute(attribute_name.c_str()); - std::string attr_value; - if (attribute == ax::mojom::StringAttribute::kFontFamily || - attribute == ax::mojom::StringAttribute::kLanguage) { - attr_value = node->GetInheritedStringAttribute(attribute); - } else if (!node->data().GetStringAttribute(attribute, &attr_value)) { - return; - } - - result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteNodeIDPlusAttributeFunction( - "GetBoolAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::BoolAttribute attribute = - ui::ParseBoolAttribute(attribute_name.c_str()); - bool attr_value; - if (!node->data().GetBoolAttribute(attribute, &attr_value)) - return; - - result.Set(v8::Boolean::New(isolate, attr_value)); - }); - RouteNodeIDPlusAttributeFunction( - "GetIntAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::IntAttribute attribute = - ui::ParseIntAttribute(attribute_name.c_str()); - int attr_value; - - if (attribute == ax::mojom::IntAttribute::kPosInSet) { - attr_value = node->GetPosInSet(); - if (attr_value == 0) - return; - } else if (attribute == ax::mojom::IntAttribute::kSetSize) { - attr_value = node->GetSetSize(); - if (attr_value == 0) - return; - } else if (!node->data().GetIntAttribute(attribute, &attr_value)) { - return; - } - - result.Set(v8::Integer::New(isolate, attr_value)); - }); - RouteNodeIDPlusAttributeFunction( - "GetIntAttributeReverseRelations", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::IntAttribute attribute = - ui::ParseIntAttribute(attribute_name.c_str()); - std::set<int32_t> ids = - tree->GetReverseRelations(attribute, node->id()); - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result(v8::Array::New(isolate, ids.size())); - size_t count = 0; - for (int32_t id : ids) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(count++), - v8::Integer::New(isolate, id)) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDPlusAttributeFunction( - "GetFloatAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::FloatAttribute attribute = - ui::ParseFloatAttribute(attribute_name.c_str()); - float attr_value; - - if (!node->data().GetFloatAttribute(attribute, &attr_value)) - return; - - double intpart, fracpart; - fracpart = modf(attr_value, &intpart); - double value_precision_2 = - intpart + std::round(fracpart * 100) / 100.0f; - result.Set(v8::Number::New(isolate, value_precision_2)); - }); - RouteNodeIDPlusAttributeFunction( - "GetIntListAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::IntListAttribute attribute = - ui::ParseIntListAttribute(attribute_name.c_str()); - if (!node->data().HasIntListAttribute(attribute)) - return; - const std::vector<int32_t>& attr_value = - node->data().GetIntListAttribute(attribute); - - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result( - v8::Array::New(isolate, attr_value.size())); - for (size_t i = 0; i < attr_value.size(); ++i) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(i), - v8::Integer::New(isolate, attr_value[i])) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDPlusAttributeFunction( - "GetIntListAttributeReverseRelations", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - ax::mojom::IntListAttribute attribute = - ui::ParseIntListAttribute(attribute_name.c_str()); - std::set<int32_t> ids = - tree->GetReverseRelations(attribute, node->id()); - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result(v8::Array::New(isolate, ids.size())); - size_t count = 0; - for (int32_t id : ids) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(count++), - v8::Integer::New(isolate, id)) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDPlusAttributeFunction( - "GetHtmlAttribute", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, ui::AXNode* node, - const std::string& attribute_name) { - std::string attr_value; - if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value)) - return; - - result.Set(v8::String::NewFromUtf8(isolate, attr_value.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteNodeIDFunction( - "GetNameFrom", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>( - node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom)); - std::string name_from_str = ui::ToString(name_from); - result.Set(v8::String::NewFromUtf8(isolate, name_from_str.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteNodeIDFunction("GetSubscript", [](v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node) { - bool value = - node->data().GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) == - static_cast<int32_t>(ax::mojom::TextPosition::kSubscript); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction( - "GetSuperscript", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - bool value = - node->data().GetIntAttribute( - ax::mojom::IntAttribute::kTextPosition) == - static_cast<int32_t>(ax::mojom::TextPosition::kSuperscript); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction( - "GetBold", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - bool value = node->data().HasTextStyle(ax::mojom::TextStyle::kBold); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction( - "GetItalic", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - bool value = node->data().HasTextStyle(ax::mojom::TextStyle::kItalic); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction("GetUnderline", [](v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node) { - bool value = node->data().HasTextStyle(ax::mojom::TextStyle::kUnderline); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction( - "GetLineThrough", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - bool value = - node->data().HasTextStyle(ax::mojom::TextStyle::kLineThrough); - result.Set(v8::Boolean::New(isolate, value)); - }); - RouteNodeIDFunction( - "GetCustomActions", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - const std::vector<int32_t>& custom_action_ids = - node->data().GetIntListAttribute( - ax::mojom::IntListAttribute::kCustomActionIds); - if (custom_action_ids.empty()) { - result.SetUndefined(); - return; - } - - const std::vector<std::string>& custom_action_descriptions = - node->data().GetStringListAttribute( - ax::mojom::StringListAttribute::kCustomActionDescriptions); - if (custom_action_ids.size() != custom_action_descriptions.size()) { - NOTREACHED(); - return; - } - - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> custom_actions( - v8::Array::New(isolate, custom_action_ids.size())); - for (size_t i = 0; i < custom_action_ids.size(); i++) { - gin::DataObjectBuilder custom_action(isolate); - custom_action.Set("id", custom_action_ids[i]); - custom_action.Set("description", custom_action_descriptions[i]); - custom_actions - ->CreateDataProperty(context, static_cast<uint32_t>(i), - custom_action.Build()) - .Check(); - } - result.Set(custom_actions); - }); - RouteNodeIDFunction( - "GetStandardActions", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - std::vector<std::string> standard_actions; - for (uint32_t action = static_cast<uint32_t>(ax::mojom::Action::kNone); - action <= static_cast<uint32_t>(ax::mojom::Action::kMaxValue); - ++action) { - if (node->data().HasAction(static_cast<ax::mojom::Action>(action))) - standard_actions.push_back( - ToString(static_cast<api::automation::ActionType>(action))); - } - - // The doDefault action is implied by having a default action verb. - int default_action_verb = - static_cast<int>(ax::mojom::DefaultActionVerb::kNone); - if (node->data().GetIntAttribute( - ax::mojom::IntAttribute::kDefaultActionVerb, - &default_action_verb) && - default_action_verb != - static_cast<int>(ax::mojom::DefaultActionVerb::kNone)) { - standard_actions.push_back( - ToString(static_cast<api::automation::ActionType>( - ax::mojom::Action::kDoDefault))); - } - - auto actions_result = v8::Array::New(isolate, standard_actions.size()); - for (size_t i = 0; i < standard_actions.size(); i++) { - const v8::Maybe<bool>& did_set_value = actions_result->Set( - isolate->GetCurrentContext(), i, - v8::String::NewFromUtf8(isolate, standard_actions[i].c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - - bool did_set_value_result = false; - if (!did_set_value.To(&did_set_value_result) || !did_set_value_result) - return; - } - result.Set(actions_result); - }); - RouteNodeIDFunction( - "GetChecked", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - const ax::mojom::CheckedState checked_state = - static_cast<ax::mojom::CheckedState>(node->data().GetIntAttribute( - ax::mojom::IntAttribute::kCheckedState)); - if (checked_state != ax::mojom::CheckedState::kNone) { - const std::string checked_str = ui::ToString(checked_state); - result.Set(v8::String::NewFromUtf8(isolate, checked_str.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - } - }); - RouteNodeIDFunction( - "GetRestriction", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - const ax::mojom::Restriction restriction = - node->data().GetRestriction(); - if (restriction != ax::mojom::Restriction::kNone) { - const std::string restriction_str = ui::ToString(restriction); - result.Set(v8::String::NewFromUtf8(isolate, restriction_str.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - } - }); - RouteNodeIDFunction( - "GetDefaultActionVerb", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - ax::mojom::DefaultActionVerb default_action_verb = - static_cast<ax::mojom::DefaultActionVerb>( - node->data().GetIntAttribute( - ax::mojom::IntAttribute::kDefaultActionVerb)); - std::string default_action_verb_str = ui::ToString(default_action_verb); - result.Set(v8::String::NewFromUtf8(isolate, - default_action_verb_str.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - }); - RouteNodeIDPlusStringBoolFunction( - "GetNextTextMatch", - [this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, - const std::string& search_str, bool backward) { - base::string16 search_str_16 = base::UTF8ToUTF16(search_str); - auto func = - backward ? &AutomationInternalCustomBindings::GetPreviousInTreeOrder - : &AutomationInternalCustomBindings::GetNextInTreeOrder; - std::function<ui::AXNode*(ui::AXNode*, AutomationAXTreeWrapper**)> next( - std::bind(func, this, std::placeholders::_1, - std::placeholders::_2)); - - AutomationAXTreeWrapper** target_tree_wrapper = &tree_wrapper; - while (true) { - node = next(node, target_tree_wrapper); - - // We explicitly disallow searches in the desktop tree. - if ((*target_tree_wrapper)->IsDesktopTree()) - return; - - if (!node) - return; - - base::string16 name; - if (!node->GetString16Attribute(ax::mojom::StringAttribute::kName, - &name)) - continue; - - if (base::i18n::StringSearchIgnoringCaseAndAccents( - search_str_16, name, nullptr, nullptr)) { - gin::DataObjectBuilder response(isolate); - response.Set("treeId", - (*target_tree_wrapper)->tree_id().ToString()); - response.Set("nodeId", node->id()); - result.Set(response.Build()); - return; - } - } - }); - RouteNodeIDFunction( - "GetTableCellColumnHeaders", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - std::vector<int32_t> col_headers; - node->GetTableCellColHeaderNodeIds(&col_headers); - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result( - v8::Array::New(isolate, col_headers.size())); - for (size_t i = 0; i < col_headers.size(); ++i) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(i), - v8::Integer::New(isolate, col_headers[i])) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDFunction( - "GetTableCellRowHeaders", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) { - std::vector<int32_t> row_headers; - node->GetTableCellRowHeaderNodeIds(&row_headers); - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - v8::Local<v8::Array> array_result( - v8::Array::New(isolate, row_headers.size())); - for (size_t i = 0; i < row_headers.size(); ++i) { - array_result - ->CreateDataProperty(context, static_cast<uint32_t>(i), - v8::Integer::New(isolate, row_headers[i])) - .Check(); - } - result.Set(array_result); - }); - RouteNodeIDFunction( - "GetTableCellColumnIndex", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node) { result.Set(node->GetTableCellColIndex()); }); - RouteNodeIDFunction( - "GetTableCellRowIndex", - [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node) { result.Set(node->GetTableCellRowIndex()); }); -} - -void AutomationInternalCustomBindings::Invalidate() { - ObjectBackedNativeHandler::Invalidate(); - - if (message_filter_) - message_filter_->Detach(); - - tree_id_to_tree_wrapper_map_.clear(); -} - -// http://crbug.com/784266 -// clang-format off -void AutomationInternalCustomBindings::OnMessageReceived( - const IPC::Message& message) { - IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message) - IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEventBundle, - OnAccessibilityEvents) - IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityLocationChange, - OnAccessibilityLocationChange) - IPC_END_MESSAGE_MAP() -} // clang-format on - -AutomationAXTreeWrapper* AutomationInternalCustomBindings:: - GetAutomationAXTreeWrapperFromTreeID(ui::AXTreeID tree_id) const { - const auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); - if (iter == tree_id_to_tree_wrapper_map_.end()) - return nullptr; - - return iter->second.get(); -} - -void AutomationInternalCustomBindings::IsInteractPermitted( - const v8::FunctionCallbackInfo<v8::Value>& args) { - const Extension* extension = context()->extension(); - CHECK(extension); - const AutomationInfo* automation_info = AutomationInfo::Get(extension); - CHECK(automation_info); - args.GetReturnValue().Set( - v8::Boolean::New(GetIsolate(), automation_info->interact)); -} - -void AutomationInternalCustomBindings::StartCachingAccessibilityTrees( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (should_ignore_context_) - return; - - if (!message_filter_) - message_filter_ = new AutomationMessageFilter(this); -} - -void AutomationInternalCustomBindings::GetSchemaAdditions( - const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = GetIsolate(); - - gin::DataObjectBuilder name_from_type(isolate); - for (int32_t i = static_cast<int32_t>(ax::mojom::NameFrom::kNone); - i <= static_cast<int32_t>(ax::mojom::NameFrom::kMaxValue); ++i) { - name_from_type.Set( - i, - base::StringPiece(ui::ToString(static_cast<ax::mojom::NameFrom>(i)))); - } - - gin::DataObjectBuilder restriction(isolate); - for (int32_t i = static_cast<int32_t>(ax::mojom::Restriction::kNone); - i <= static_cast<int32_t>(ax::mojom::Restriction::kMaxValue); ++i) { - restriction.Set(i, base::StringPiece(ui::ToString( - static_cast<ax::mojom::Restriction>(i)))); - } - - gin::DataObjectBuilder description_from_type(isolate); - for (int32_t i = static_cast<int32_t>(ax::mojom::DescriptionFrom::kNone); - i <= static_cast<int32_t>(ax::mojom::DescriptionFrom::kMaxValue); ++i) { - description_from_type.Set( - i, base::StringPiece( - ui::ToString(static_cast<ax::mojom::DescriptionFrom>(i)))); - } - - args.GetReturnValue().Set( - gin::DataObjectBuilder(isolate) - .Set("NameFromType", name_from_type.Build()) - .Set("Restriction", restriction.Build()) - .Set("DescriptionFromType", description_from_type.Build()) - .Build()); -} - -void AutomationInternalCustomBindings::DestroyAccessibilityTree( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (args.Length() != 1 || !args[0]->IsString()) { - ThrowInvalidArgumentsException(this); - return; - } - - ui::AXTreeID tree_id = ui::AXTreeID::FromString( - *v8::String::Utf8Value(args.GetIsolate(), args[0])); - auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); - if (iter == tree_id_to_tree_wrapper_map_.end()) - return; - - AutomationAXTreeWrapper* tree_wrapper = iter->second.get(); - axtree_to_tree_wrapper_map_.erase(tree_wrapper->tree()); - tree_id_to_tree_wrapper_map_.erase(tree_id); -} - -void AutomationInternalCustomBindings::AddTreeChangeObserver( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (args.Length() != 2 || !args[0]->IsNumber() || !args[1]->IsString()) { - ThrowInvalidArgumentsException(this); - return; - } - - TreeChangeObserver observer; - observer.id = args[0]->Int32Value(context()->v8_context()).FromMaybe(0); - std::string filter_str = *v8::String::Utf8Value(args.GetIsolate(), args[1]); - observer.filter = api::automation::ParseTreeChangeObserverFilter(filter_str); - - tree_change_observers_.push_back(observer); - UpdateOverallTreeChangeObserverFilter(); -} - -void AutomationInternalCustomBindings::RemoveTreeChangeObserver( - const v8::FunctionCallbackInfo<v8::Value>& args) { - // The argument is an integer key for an object which is automatically - // converted to a string. - if (args.Length() != 1 || !args[0]->IsString()) { - ThrowInvalidArgumentsException(this); - return; - } - - int observer_id = args[0]->Int32Value(context()->v8_context()).FromMaybe(0); - - for (auto iter = tree_change_observers_.begin(); - iter != tree_change_observers_.end(); ++iter) { - if (iter->id == observer_id) { - tree_change_observers_.erase(iter); - break; - } - } - - UpdateOverallTreeChangeObserverFilter(); -} - -bool AutomationInternalCustomBindings::GetFocusInternal( - AutomationAXTreeWrapper* tree_wrapper, - AutomationAXTreeWrapper** out_tree_wrapper, - ui::AXNode** out_node) { - int focus_id = tree_wrapper->tree()->data().focus_id; - ui::AXNode* focus = tree_wrapper->tree()->GetFromId(focus_id); - if (!focus) - return false; - - // If the focused node is the owner of a child tree, that indicates - // a node within the child tree is the one that actually has focus. - while (focus->data().HasStringAttribute( - ax::mojom::StringAttribute::kChildTreeId)) { - // Try to keep following focus recursively, by letting |tree_id| be the - // new subtree to search in, while keeping |focus_tree_id| set to the tree - // where we know we found a focused node. - ui::AXTreeID child_tree_id = - ui::AXTreeID::FromString(focus->data().GetStringAttribute( - ax::mojom::StringAttribute::kChildTreeId)); - - AutomationAXTreeWrapper* child_tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID(child_tree_id); - if (!child_tree_wrapper) - break; - - // If |child_tree_wrapper| is a frame tree that indicates a focused frame, - // jump to that frame if possible. - ui::AXTreeID focused_tree_id = - child_tree_wrapper->tree()->data().focused_tree_id; - if (focused_tree_id != ui::AXTreeIDUnknown() && - !child_tree_wrapper->IsDesktopTree()) { - AutomationAXTreeWrapper* focused_tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID( - child_tree_wrapper->tree()->data().focused_tree_id); - if (focused_tree_wrapper) - child_tree_wrapper = focused_tree_wrapper; - } - - int child_focus_id = child_tree_wrapper->tree()->data().focus_id; - ui::AXNode* child_focus = - child_tree_wrapper->tree()->GetFromId(child_focus_id); - if (!child_focus) - break; - - focus = child_focus; - tree_wrapper = child_tree_wrapper; - } - - *out_tree_wrapper = tree_wrapper; - *out_node = focus; - return true; -} - -void AutomationInternalCustomBindings::GetFocus( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (args.Length() != 1 || !args[0]->IsString()) { - ThrowInvalidArgumentsException(this); - return; - } - ui::AXTreeID tree_id = ui::AXTreeID::FromString( - *v8::String::Utf8Value(args.GetIsolate(), args[0])); - AutomationAXTreeWrapper* tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - AutomationAXTreeWrapper* focused_tree_wrapper = nullptr; - ui::AXNode* focused_node = nullptr; - if (!GetFocusInternal(tree_wrapper, &focused_tree_wrapper, &focused_node)) - return; - - args.GetReturnValue().Set( - gin::DataObjectBuilder(GetIsolate()) - .Set("treeId", focused_tree_wrapper->tree_id().ToString()) - .Set("nodeId", focused_node->id()) - .Build()); -} - -void AutomationInternalCustomBindings::GetHtmlAttributes( - const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = GetIsolate(); - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsNumber()) - ThrowInvalidArgumentsException(this); - - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context()->v8_context()).FromMaybe(0); - - AutomationAXTreeWrapper* tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - gin::DataObjectBuilder dst(isolate); - for (const auto& pair : node->data().html_attributes) - dst.Set(pair.first, pair.second); - args.GetReturnValue().Set(dst.Build()); -} - -void AutomationInternalCustomBindings::GetState( - const v8::FunctionCallbackInfo<v8::Value>& args) { - v8::Isolate* isolate = GetIsolate(); - if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsNumber()) - ThrowInvalidArgumentsException(this); - - ui::AXTreeID tree_id = - ui::AXTreeID::FromString(*v8::String::Utf8Value(isolate, args[0])); - int node_id = args[1]->Int32Value(context()->v8_context()).FromMaybe(0); - - AutomationAXTreeWrapper* tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID(tree_id); - if (!tree_wrapper) - return; - - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - gin::DataObjectBuilder state(isolate); - uint32_t state_pos = 0, state_shifter = node->data().state; - while (state_shifter) { - if (state_shifter & 1) - state.Set(ui::ToString(static_cast<ax::mojom::State>(state_pos)), true); - state_shifter = state_shifter >> 1; - state_pos++; - } - AutomationAXTreeWrapper* top_tree_wrapper = nullptr; - AutomationAXTreeWrapper* walker = tree_wrapper; - while (walker && walker != top_tree_wrapper) { - top_tree_wrapper = walker; - GetParent(walker->tree()->root(), &walker); - } - AutomationAXTreeWrapper* focused_tree_wrapper = nullptr; - ui::AXNode* focused_node = nullptr; - const bool focused = - (GetFocusInternal(top_tree_wrapper, &focused_tree_wrapper, - &focused_node) && - focused_tree_wrapper == tree_wrapper && focused_node == node) || - tree_wrapper->tree()->data().focus_id == node->id(); - if (focused) - state.Set(api::automation::ToString(api::automation::STATE_TYPE_FOCUSED), - true); - - bool offscreen = false; - ComputeGlobalNodeBounds(tree_wrapper, node, gfx::RectF(), &offscreen); - if (offscreen) - state.Set(api::automation::ToString(api::automation::STATE_TYPE_OFFSCREEN), - true); - - args.GetReturnValue().Set(state.Build()); -} - -void AutomationInternalCustomBindings::UpdateOverallTreeChangeObserverFilter() { - tree_change_observer_overall_filter_ = 0; - for (const auto& observer : tree_change_observers_) - tree_change_observer_overall_filter_ |= 1 << observer.filter; -} - -ui::AXNode* AutomationInternalCustomBindings::GetParent( - ui::AXNode* node, - AutomationAXTreeWrapper** in_out_tree_wrapper) const { - if (node->parent()) - return node->parent(); - - AutomationAXTreeWrapper* parent_tree_wrapper = nullptr; - - ui::AXTreeID parent_tree_id = - (*in_out_tree_wrapper)->tree()->data().parent_tree_id; - if (parent_tree_id != ui::AXTreeIDUnknown()) { - // If the tree specifies its parent tree ID, use that. That provides - // some additional security guarantees, so a tree can't be "claimed" - // by something else. - parent_tree_wrapper = GetAutomationAXTreeWrapperFromTreeID(parent_tree_id); - } else { - // Otherwise if it was unspecified, check to see if another tree listed - // this one as its child, and then we know the parent. - parent_tree_wrapper = AutomationAXTreeWrapper::GetParentOfTreeId( - (*in_out_tree_wrapper)->tree_id()); - } - - if (!parent_tree_wrapper) - return nullptr; - - std::set<int32_t> host_node_ids = - parent_tree_wrapper->tree()->GetNodeIdsForChildTreeId( - (*in_out_tree_wrapper)->tree_id()); - -#if !defined(NDEBUG) - if (host_node_ids.size() > 1) - DLOG(WARNING) << "Multiple nodes claim the same child tree id."; -#endif - - for (int32_t host_node_id : host_node_ids) { - ui::AXNode* host_node = - parent_tree_wrapper->tree()->GetFromId(host_node_id); - if (host_node) { - DCHECK_EQ((*in_out_tree_wrapper)->tree_id(), - ui::AXTreeID::FromString(host_node->GetStringAttribute( - ax::mojom::StringAttribute::kChildTreeId))); - *in_out_tree_wrapper = parent_tree_wrapper; - return host_node; - } - } - - return nullptr; -} - -bool AutomationInternalCustomBindings::GetRootOfChildTree( - ui::AXNode** in_out_node, - AutomationAXTreeWrapper** in_out_tree_wrapper) const { - std::string child_tree_id_str; - if (!(*in_out_node) - ->GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId, - &child_tree_id_str)) - return false; - - AutomationAXTreeWrapper* child_tree_wrapper = - GetAutomationAXTreeWrapperFromTreeID( - ui::AXTreeID::FromString(child_tree_id_str)); - if (!child_tree_wrapper || !child_tree_wrapper->tree()->root()) - return false; - - *in_out_tree_wrapper = child_tree_wrapper; - *in_out_node = child_tree_wrapper->tree()->root(); - return true; -} - -ui::AXNode* AutomationInternalCustomBindings::GetNextInTreeOrder( - ui::AXNode* start, - AutomationAXTreeWrapper** in_out_tree_wrapper) const { - ui::AXNode* walker = start; - if (walker->child_count() > 0) - return walker->ChildAtIndex(0); - - // We also have to check child tree id. - if (GetRootOfChildTree(&walker, in_out_tree_wrapper)) - return walker; - - // Find the next branch forward. - ui::AXNode* parent; - while ((parent = GetParent(walker, in_out_tree_wrapper))) { - if ((walker->index_in_parent() + 1) < parent->child_count()) - return parent->ChildAtIndex(walker->index_in_parent() + 1); - - walker = parent; - } - - return nullptr; -} - -ui::AXNode* AutomationInternalCustomBindings::GetPreviousInTreeOrder( - ui::AXNode* start, - AutomationAXTreeWrapper** in_out_tree_wrapper) const { - ui::AXNode* walker = start; - - ui::AXNode* parent = GetParent(start, in_out_tree_wrapper); - - // No more nodes. - if (!parent) - return nullptr; - - // No previous sibling; parent is previous. - if (walker->index_in_parent() == 0) - return parent; - - walker = parent->ChildAtIndex(walker->index_in_parent() - 1); - - // Walks to deepest last child. - while (true) { - if (walker->child_count() > 0) { - walker = walker->ChildAtIndex(walker->child_count() - 1); - } else if (!GetRootOfChildTree(&walker, in_out_tree_wrapper)) { - break; - } - } - return walker; -} - -float AutomationInternalCustomBindings::GetDeviceScaleFactor() const { - return context()->GetRenderFrame()->GetRenderView()->GetDeviceScaleFactor(); -} - -void AutomationInternalCustomBindings::RouteTreeIDFunction( - const std::string& name, - TreeIDFunction callback) { - scoped_refptr<TreeIDWrapper> wrapper = new TreeIDWrapper(this, callback); - RouteHandlerFunction(name, base::BindRepeating(&TreeIDWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::RouteNodeIDFunction( - const std::string& name, - NodeIDFunction callback) { - scoped_refptr<NodeIDWrapper> wrapper = new NodeIDWrapper(this, callback); - RouteHandlerFunction(name, base::BindRepeating(&NodeIDWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( - const std::string& name, - NodeIDPlusAttributeFunction callback) { - scoped_refptr<NodeIDPlusAttributeWrapper> wrapper = - new NodeIDPlusAttributeWrapper(this, callback); - RouteHandlerFunction( - name, base::BindRepeating(&NodeIDPlusAttributeWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction( - const std::string& name, - NodeIDPlusRangeFunction callback) { - scoped_refptr<NodeIDPlusRangeWrapper> wrapper = - new NodeIDPlusRangeWrapper(this, callback); - RouteHandlerFunction( - name, base::BindRepeating(&NodeIDPlusRangeWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::RouteNodeIDPlusStringBoolFunction( - const std::string& name, - NodeIDPlusStringBoolFunction callback) { - scoped_refptr<NodeIDPlusStringBoolWrapper> wrapper = - new NodeIDPlusStringBoolWrapper(this, callback); - RouteHandlerFunction( - name, base::BindRepeating(&NodeIDPlusStringBoolWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::RouteNodeIDPlusDimensionsFunction( - const std::string& name, - NodeIDPlusDimensionsFunction callback) { - scoped_refptr<NodeIDPlusDimensionsWrapper> wrapper = - new NodeIDPlusDimensionsWrapper(this, callback); - RouteHandlerFunction( - name, base::BindRepeating(&NodeIDPlusDimensionsWrapper::Run, wrapper)); -} - -void AutomationInternalCustomBindings::GetChildIDAtIndex( - const v8::FunctionCallbackInfo<v8::Value>& args) { - if (args.Length() < 3 || !args[2]->IsNumber()) { - ThrowInvalidArgumentsException(this); - return; - } - - ui::AXTreeID tree_id = ui::AXTreeID::FromString( - *v8::String::Utf8Value(args.GetIsolate(), args[0])); - int node_id = args[1]->Int32Value(context()->v8_context()).FromMaybe(0); - - const auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); - if (iter == tree_id_to_tree_wrapper_map_.end()) - return; - - AutomationAXTreeWrapper* tree_wrapper = iter->second.get(); - ui::AXNode* node = tree_wrapper->tree()->GetFromId(node_id); - if (!node) - return; - - int index = args[2]->Int32Value(context()->v8_context()).FromMaybe(0); - int child_id; - - // Check for a child tree, which is guaranteed to always be the only child. - if (index == 0 && GetRootOfChildTree(&node, &tree_wrapper)) - child_id = node->id(); - else if (index < 0 || index >= node->child_count()) - return; - else - child_id = node->children()[index]->id(); - - gin::DataObjectBuilder response(GetIsolate()); - response.Set("treeId", tree_wrapper->tree_id().ToString()); - response.Set("nodeId", child_id); - args.GetReturnValue().Set(response.Build()); -} - -// -// Handle accessibility events from the browser process. -// - -void AutomationInternalCustomBindings::OnAccessibilityEvents( - const ExtensionMsg_AccessibilityEventBundleParams& event_bundle, - bool is_active_profile) { - is_active_profile_ = is_active_profile; - ui::AXTreeID tree_id = event_bundle.tree_id; - AutomationAXTreeWrapper* tree_wrapper; - auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); - if (iter == tree_id_to_tree_wrapper_map_.end()) { - tree_wrapper = new AutomationAXTreeWrapper(tree_id, this); - tree_id_to_tree_wrapper_map_.insert( - std::make_pair(tree_id, base::WrapUnique(tree_wrapper))); - axtree_to_tree_wrapper_map_.insert( - std::make_pair(tree_wrapper->tree(), tree_wrapper)); - } else { - tree_wrapper = iter->second.get(); - } - - if (!tree_wrapper->OnAccessibilityEvents(event_bundle, is_active_profile)) { - LOG(ERROR) << tree_wrapper->tree()->error(); - base::ListValue args; - args.AppendString(tree_id.ToString()); - bindings_system_->DispatchEventInContext( - "automationInternal.onAccessibilityTreeSerializationError", &args, - nullptr, context()); - return; - } -} - -void AutomationInternalCustomBindings::OnAccessibilityLocationChange( - const ExtensionMsg_AccessibilityLocationChangeParams& params) { - ui::AXTreeID tree_id = params.tree_id; - auto iter = tree_id_to_tree_wrapper_map_.find(tree_id); - if (iter == tree_id_to_tree_wrapper_map_.end()) - return; - AutomationAXTreeWrapper* tree_wrapper = iter->second.get(); - ui::AXNode* node = tree_wrapper->tree()->GetFromId(params.id); - if (!node) - return; - node->SetLocation(params.new_location.offset_container_id, - params.new_location.bounds, - params.new_location.transform.get()); -} - -bool AutomationInternalCustomBindings::SendTreeChangeEvent( - api::automation::TreeChangeType change_type, - ui::AXTree* tree, - ui::AXNode* node) { - // Don't send tree change events when it's not the active profile. - if (!is_active_profile_) - return false; - - // Notify custom bindings when there's an unloaded tree; js will enable the - // renderer and wait for it to load. - std::string child_tree_id_str; - if (node->data().GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId, - &child_tree_id_str)) { - ui::AXTreeID child_tree_id = ui::AXTreeID::FromString(child_tree_id_str); - auto* tree_wrapper = GetAutomationAXTreeWrapperFromTreeID(child_tree_id); - if (!tree_wrapper || !tree_wrapper->tree()->data().loaded) - SendChildTreeIDEvent(child_tree_id); - } - - bool has_filter = false; - if (tree_change_observer_overall_filter_ & - (1 - << api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) { - if (node->data().HasStringAttribute( - ax::mojom::StringAttribute::kContainerLiveStatus) || - node->data().role == ax::mojom::Role::kAlert || - change_type == api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) { - has_filter = true; - } - } - - if (tree_change_observer_overall_filter_ & - (1 << api::automation::TREE_CHANGE_OBSERVER_FILTER_TEXTMARKERCHANGES)) { - if (node->data().HasIntListAttribute( - ax::mojom::IntListAttribute::kMarkerTypes)) - has_filter = true; - } - - if (tree_change_observer_overall_filter_ & - (1 << api::automation::TREE_CHANGE_OBSERVER_FILTER_ALLTREECHANGES)) - has_filter = true; - - if (!has_filter) - return false; - - auto iter = axtree_to_tree_wrapper_map_.find(tree); - if (iter == axtree_to_tree_wrapper_map_.end()) - return false; - - ui::AXTreeID tree_id = iter->second->tree_id(); - bool did_send_event = false; - for (const auto& observer : tree_change_observers_) { - switch (observer.filter) { - case api::automation::TREE_CHANGE_OBSERVER_FILTER_NOTREECHANGES: - default: - continue; - case api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES: - if (!node->data().HasStringAttribute( - ax::mojom::StringAttribute::kContainerLiveStatus) && - node->data().role != ax::mojom::Role::kAlert && - change_type != api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) { - continue; - } - break; - case api::automation::TREE_CHANGE_OBSERVER_FILTER_TEXTMARKERCHANGES: - if (!node->data().HasIntListAttribute( - ax::mojom::IntListAttribute::kMarkerTypes)) - continue; - break; - case api::automation::TREE_CHANGE_OBSERVER_FILTER_ALLTREECHANGES: - break; - } - - did_send_event = true; - base::ListValue args; - args.AppendInteger(observer.id); - args.AppendString(tree_id.ToString()); - args.AppendInteger(node->id()); - args.AppendString(ToString(change_type)); - bindings_system_->DispatchEventInContext("automationInternal.onTreeChange", - &args, nullptr, context()); - } - - return did_send_event; -} - -void AutomationInternalCustomBindings::SendAutomationEvent( - ui::AXTreeID tree_id, - const gfx::Point& mouse_location, - const ui::AXEvent& event, - api::automation::EventType event_type) { - auto event_params = std::make_unique<base::DictionaryValue>(); - event_params->SetString("treeID", tree_id.ToString()); - event_params->SetInteger("targetID", event.id); - event_params->SetString("eventType", api::automation::ToString(event_type)); - event_params->SetString("eventFrom", ui::ToString(event.event_from)); - event_params->SetInteger("actionRequestID", event.action_request_id); - event_params->SetInteger("mouseX", mouse_location.x()); - event_params->SetInteger("mouseY", mouse_location.y()); - base::ListValue args; - args.Append(std::move(event_params)); - bindings_system_->DispatchEventInContext( - "automationInternal.onAccessibilityEvent", &args, nullptr, context()); -} - -void AutomationInternalCustomBindings::SendChildTreeIDEvent( - ui::AXTreeID child_tree_id) { - base::ListValue args; - args.AppendString(child_tree_id.ToString()); - bindings_system_->DispatchEventInContext("automationInternal.onChildTreeID", - &args, nullptr, context()); -} - -void AutomationInternalCustomBindings::SendNodesRemovedEvent( - ui::AXTree* tree, - const std::vector<int>& ids) { - auto iter = axtree_to_tree_wrapper_map_.find(tree); - if (iter == axtree_to_tree_wrapper_map_.end()) - return; - - ui::AXTreeID tree_id = iter->second->tree_id(); - - base::ListValue args; - args.AppendString(tree_id.ToString()); - { - auto nodes = std::make_unique<base::ListValue>(); - for (auto id : ids) - nodes->AppendInteger(id); - args.Append(std::move(nodes)); - } - - bindings_system_->DispatchEventInContext("automationInternal.onNodesRemoved", - &args, nullptr, context()); -} - -} // namespace cast -} // namespace extensions
diff --git a/chromecast/renderer/extensions/automation_internal_custom_bindings.h b/chromecast/renderer/extensions/automation_internal_custom_bindings.h deleted file mode 100644 index da86d90..0000000 --- a/chromecast/renderer/extensions/automation_internal_custom_bindings.h +++ /dev/null
@@ -1,225 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ -#define CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ - -#include <map> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "chromecast/renderer/extensions/automation_ax_tree_wrapper.h" -#include "extensions/common/api/automation.h" -#include "extensions/renderer/object_backed_native_handler.h" -#include "ipc/ipc_message.h" -#include "ui/accessibility/ax_tree.h" -#include "v8/include/v8.h" - -struct ExtensionMsg_AccessibilityEventBundleParams; -struct ExtensionMsg_AccessibilityLocationChangeParams; - -namespace ui { -struct AXEvent; -} - -namespace extensions { -class NativeExtensionBindingsSystem; -} // namespace extensions - -namespace extensions { -namespace cast { - -class AutomationInternalCustomBindings; -class AutomationMessageFilter; - -struct TreeChangeObserver { - int id; - api::automation::TreeChangeObserverFilter filter; -}; - -// The native component of custom bindings for the chrome.automationInternal -// API. -class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { - public: - AutomationInternalCustomBindings( - ScriptContext* context, - NativeExtensionBindingsSystem* bindings_system); - ~AutomationInternalCustomBindings() override; - - // ObjectBackedNativeHandler: - void AddRoutes() override; - - void OnMessageReceived(const IPC::Message& message); - - AutomationAXTreeWrapper* GetAutomationAXTreeWrapperFromTreeID( - ui::AXTreeID tree_id) const; - - // Given a tree (|in_out_tree_wrapper|) and a node, returns the parent. - // If |node| is the root of its tree, the return value will be the host - // node of the parent tree and |in_out_tree_wrapper| will be updated to - // point to that parent tree. - ui::AXNode* GetParent(ui::AXNode* node, - AutomationAXTreeWrapper** in_out_tree_wrapper) const; - - // Gets the root of a node's child tree and adjusts incoming arguments - // accordingly. Returns false if no adjustments were made. - bool GetRootOfChildTree(ui::AXNode** in_out_node, - AutomationAXTreeWrapper** in_out_tree_wrapper) const; - - ui::AXNode* GetNextInTreeOrder( - ui::AXNode* start, - AutomationAXTreeWrapper** in_out_tree_wrapper) const; - ui::AXNode* GetPreviousInTreeOrder( - ui::AXNode* start, - AutomationAXTreeWrapper** in_out_tree_wrapper) const; - - ScriptContext* context() const { - return ObjectBackedNativeHandler::context(); - } - - float GetDeviceScaleFactor() const; - - void SendNodesRemovedEvent(ui::AXTree* tree, const std::vector<int>& ids); - bool SendTreeChangeEvent(api::automation::TreeChangeType change_type, - ui::AXTree* tree, - ui::AXNode* node); - void SendAutomationEvent(ui::AXTreeID tree_id, - const gfx::Point& mouse_location, - const ui::AXEvent& event, - api::automation::EventType event_type); - - private: - // ObjectBackedNativeHandler overrides: - void Invalidate() override; - - // Returns whether this extension has the "interact" permission set (either - // explicitly or implicitly after manifest parsing). - void IsInteractPermitted(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Returns an object with bindings that will be added to the - // chrome.automation namespace. - void GetSchemaAdditions(const v8::FunctionCallbackInfo<v8::Value>& args); - - // This is called by automation_internal_custom_bindings.js to indicate - // that an API was called that needs access to accessibility trees. This - // enables the MessageFilter that allows us to listen to accessibility - // events forwarded to this process. - void StartCachingAccessibilityTrees( - const v8::FunctionCallbackInfo<v8::Value>& args); - - // Called when an accessibility tree is destroyed and needs to be - // removed from our cache. - // Args: string ax_tree_id - void DestroyAccessibilityTree( - const v8::FunctionCallbackInfo<v8::Value>& args); - - void AddTreeChangeObserver(const v8::FunctionCallbackInfo<v8::Value>& args); - - void RemoveTreeChangeObserver( - const v8::FunctionCallbackInfo<v8::Value>& args); - - void GetFocus(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Given an initial AutomationAXTreeWrapper, return the - // AutomationAXTreeWrapper and node of the focused node within this tree or a - // focused descendant tree. - bool GetFocusInternal(AutomationAXTreeWrapper* top_tree, - AutomationAXTreeWrapper** out_tree, - ui::AXNode** out_node); - - void RouteTreeIDFunction( - const std::string& name, - void (*callback)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper)); - - void RouteNodeIDFunction( - const std::string& name, - std::function<void(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node)> callback); - void RouteNodeIDPlusAttributeFunction( - const std::string& name, - void (*callback)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - ui::AXTree* tree, - ui::AXNode* node, - const std::string& attribute_name)); - void RouteNodeIDPlusRangeFunction( - const std::string& name, - void (*callback)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - int start, - int end)); - void RouteNodeIDPlusStringBoolFunction( - const std::string& name, - std::function<void(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - const std::string& strVal, - bool boolVal)> callback); - void RouteNodeIDPlusDimensionsFunction( - const std::string& name, - void (*callback)(v8::Isolate* isolate, - v8::ReturnValue<v8::Value> result, - AutomationAXTreeWrapper* tree_wrapper, - ui::AXNode* node, - int start, - int end, - int width, - int height)); - - // - // Access the cached accessibility trees and properties of their nodes. - // - - // Args: string ax_tree_id, int node_id, Returns: int child_id. - void GetChildIDAtIndex(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: string ax_tree_id, int node_id - // Returns: JS object with a map from html attribute key to value. - void GetHtmlAttributes(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Args: string ax_tree_id, int node_id - // Returns: JS object with a string key for each state flag that's set. - void GetState(const v8::FunctionCallbackInfo<v8::Value>& args); - - // - // Helper functions. - // - - // Handle accessibility events from the browser process. - void OnAccessibilityEvents( - const ExtensionMsg_AccessibilityEventBundleParams& events, - bool is_active_profile); - void OnAccessibilityLocationChange( - const ExtensionMsg_AccessibilityLocationChangeParams& params); - - void UpdateOverallTreeChangeObserverFilter(); - - void SendChildTreeIDEvent(ui::AXTreeID child_tree_id); - - std::map<ui::AXTreeID, std::unique_ptr<AutomationAXTreeWrapper>> - tree_id_to_tree_wrapper_map_; - std::map<ui::AXTree*, AutomationAXTreeWrapper*> axtree_to_tree_wrapper_map_; - scoped_refptr<AutomationMessageFilter> message_filter_; - bool is_active_profile_; - std::vector<TreeChangeObserver> tree_change_observers_; - // A bit-map of api::automation::TreeChangeObserverFilter. - int tree_change_observer_overall_filter_; - NativeExtensionBindingsSystem* bindings_system_; - bool should_ignore_context_; - - DISALLOW_COPY_AND_ASSIGN(AutomationInternalCustomBindings); -}; - -} // namespace cast -} // namespace extensions - -#endif // CHROMECAST_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_
diff --git a/chromecast/renderer/resources/extensions/automation/automation_event.js b/chromecast/renderer/resources/extensions/automation/automation_event.js deleted file mode 100644 index 88c94d15..0000000 --- a/chromecast/renderer/resources/extensions/automation/automation_event.js +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var utils = require('utils'); - -function AutomationEventImpl(type, target, eventFrom) { - this.propagationStopped = false; - this.type = type; - this.target = target; - this.eventPhase = Event.NONE; - this.eventFrom = eventFrom; -} - -AutomationEventImpl.prototype = { - __proto__: null, - stopPropagation: function() { - this.propagationStopped = true; - }, -}; - -function AutomationEvent() { - privates(AutomationEvent).constructPrivate(this, arguments); -} -utils.expose(AutomationEvent, AutomationEventImpl, { - functions: [ - 'stopPropagation', - ], - readonly: [ - 'type', - 'target', - 'eventPhase', - 'eventFrom', - ], -}); - -exports.$set('AutomationEvent', AutomationEvent);
diff --git a/chromecast/renderer/resources/extensions/automation/automation_node.js b/chromecast/renderer/resources/extensions/automation/automation_node.js deleted file mode 100644 index 0383f33..0000000 --- a/chromecast/renderer/resources/extensions/automation/automation_node.js +++ /dev/null
@@ -1,1593 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var AutomationEvent = require('automationEvent').AutomationEvent; -var automationInternal = getInternalApi('automationInternal'); -var exceptionHandler = require('uncaught_exception_handler'); - -var natives = requireNative('automationInternal'); - -var IsInteractPermitted = natives.IsInteractPermitted; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The id of the root node. - */ -var GetRootID = natives.GetRootID; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?string} The title of the document. - */ -var GetDocTitle = natives.GetDocTitle; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?string} The url of the document. - */ -var GetDocURL = natives.GetDocURL; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?boolean} True if the document has finished loading. - */ -var GetDocLoaded = natives.GetDocLoaded; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded). - */ -var GetDocLoadingProgress = - natives.GetDocLoadingProgress; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The ID of the selection anchor object. - */ -var GetAnchorObjectID = natives.GetAnchorObjectID; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The selection anchor offset. - */ -var GetAnchorOffset = natives.GetAnchorOffset; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?string} The selection anchor affinity. - */ -var GetAnchorAffinity = natives.GetAnchorAffinity; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The ID of the selection focus object. - */ -var GetFocusObjectID = natives.GetFocusObjectID; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?number} The selection focus offset. - */ -var GetFocusOffset = natives.GetFocusOffset; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @return {?string} The selection focus affinity. - */ -var GetFocusAffinity = natives.GetFocusAffinity; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?number} The id of the node's parent, or undefined if it's the - * root of its tree or if the tree or node wasn't found. - */ -var GetParentID = natives.GetParentID; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?number} The number of children of the node, or undefined if - * the tree or node wasn't found. - */ -var GetChildCount = natives.GetChildCount; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {number} childIndex An index of a child of this node. - * @return {?number} The id of the child at the given index, or undefined - * if the tree or node or child at that index wasn't found. - */ -var GetChildIDAtIndex = natives.GetChildIDAtIndex; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?number} The ids of the children of the node, or undefined - * if the tree or node wasn't found. - */ -var GetChildIds = natives.GetChildIDs; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Object} An object mapping html attributes to values. - */ -var GetHtmlAttributes = natives.GetHtmlAttributes; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?number} The index of this node in its parent, or undefined if - * the tree or node or node parent wasn't found. - */ -var GetIndexInParent = natives.GetIndexInParent; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Object} An object with a string key for every state flag set, - * or undefined if the tree or node or node parent wasn't found. - */ -var GetState = natives.GetState; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {string} The restriction, one of - * "disabled", "readOnly" or undefined if enabled or other object not disabled - */ -var GetRestriction = natives.GetRestriction; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {string} The checked state, as undefined, "true", "false" or "mixed". - */ -var GetChecked = natives.GetChecked; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {string} The role of the node, or undefined if the tree or - * node wasn't found. - */ -var GetRole = natives.GetRole; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?automation.Rect} The location of the node, or undefined if - * the tree or node wasn't found. - */ -var GetLocation = natives.GetLocation; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {number} startIndex The start index of the range. - * @param {number} endIndex The end index of the range. - * @return {?automation.Rect} The bounding box of the subrange of this node, - * or the location if there are no subranges, or undefined if - * the tree or node wasn't found. - */ -var GetBoundsForRange = natives.GetBoundsForRange; - -/** - * @param {number} left The left location of the text range. - * @param {number} top The top location of the text range. - * @param {number} width The width of text range. - * @param {number} height The height of the text range. - * @param {number} requestID The request id associated with the query - * for this range. - * @return {?automation.Rect} The bounding box of the subrange of this node, - * specified by arguments provided to the function. - */ -var ComputeGlobalBounds = natives.ComputeGlobalBounds; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?automation.Rect} The unclipped location of the node, or - * undefined if the tree or node wasn't found. - */ -var GetUnclippedLocation = natives.GetUnclippedLocation; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {!Array<number>} The text offset where each line starts, or an empty - * array if this node has no text content, or undefined if the tree or node - * was not found. - */ -var GetLineStartOffsets = requireNative( - 'automationInternal').GetLineStartOffsets; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of a string attribute. - * @return {?string} The value of this attribute, or undefined if the tree, - * node, or attribute wasn't found. - */ -var GetStringAttribute = natives.GetStringAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?boolean} The value of this attribute, or undefined if the tree, - * node, or attribute wasn't found. - */ -var GetBoolAttribute = natives.GetBoolAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?number} The value of this attribute, or undefined if the tree, - * node, or attribute wasn't found. - */ -var GetIntAttribute = natives.GetIntAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?Array<number>} The ids of nodes who have a relationship pointing - * to |nodeID| (a reverse relationship). - */ -var GetIntAttributeReverseRelations = - natives.GetIntAttributeReverseRelations; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?number} The value of this attribute, or undefined if the tree, - * node, or attribute wasn't found. - */ -var GetFloatAttribute = natives.GetFloatAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?Array<number>} The value of this attribute, or undefined - * if the tree, node, or attribute wasn't found. - */ -var GetIntListAttribute = - natives.GetIntListAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an attribute. - * @return {?Array<number>} The ids of nodes who have a relationship pointing - * to |nodeID| (a reverse relationship). - */ -var GetIntListAttributeReverseRelations = - natives.GetIntListAttributeReverseRelations; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} attr The name of an HTML attribute. - * @return {?string} The value of this attribute, or undefined if the tree, - * node, or attribute wasn't found. - */ -var GetHtmlAttribute = natives.GetHtmlAttribute; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {automation.NameFromType} The source of the node's name. - */ -var GetNameFrom = natives.GetNameFrom; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {boolean} - */ -var GetBold = natives.GetBold; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {boolean} - */ -var GetItalic = natives.GetItalic; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {boolean} - */ -var GetUnderline = natives.GetUnderline; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {boolean} - */ -var GetLineThrough = natives.GetLineThrough; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Array<automation.CustomAction>} List of custom actions of the - * node. - */ -var GetCustomActions = natives.GetCustomActions; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Array<string>} List of standard actions of the node. - */ -var GetStandardActions = natives.GetStandardActions; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {automation.NameFromType} The source of the node's name. - */ -var GetDefaultActionVerb = natives.GetDefaultActionVerb; - - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @param {string} searchStr - * @param {boolean} backward - * @return {{treeId: string, nodeId: number}} - */ -var GetNextTextMatch = natives.GetNextTextMatch; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Array<number>} A list of column header ids. - - * @return {?number} The id of the column header, if it exists. - */ -var GetTableCellColumnHeaders = natives.GetTableCellColumnHeaders; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {?Array<number>} A list of row header ids. - */ -var GetTableCellRowHeaders = natives.GetTableCellRowHeaders; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {number} Column index for this cell. - */ -var GetTableCellColumnIndex = natives.GetTableCellColumnIndex; - -/** - * @param {string} axTreeID The id of the accessibility tree. - * @param {number} nodeID The id of a node. - * @return {number} Row index for this cell. - */ -var GetTableCellRowIndex = natives.GetTableCellRowIndex; - -var logging = requireNative('logging'); -var utils = require('utils'); - -/** - * A single node in the Automation tree. - * @param {AutomationRootNodeImpl} root The root of the tree. - * @constructor - */ -function AutomationNodeImpl(root) { - this.rootImpl = root; - this.listeners = {__proto__: null}; -} - -AutomationNodeImpl.prototype = { - __proto__: null, - treeID: '', - id: -1, - isRootNode: false, - - detach: function() { - this.rootImpl = null; - this.listeners = {__proto__: null}; - }, - - get root() { - return this.rootImpl && this.rootImpl.wrapper; - }, - - get parent() { - var info = GetParentID(this.treeID, this.id); - if (!info) - return; - return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId); - }, - - get htmlAttributes() { - return GetHtmlAttributes(this.treeID, this.id) || {}; - }, - - get state() { - return GetState(this.treeID, this.id) || {}; - }, - - get role() { - return GetRole(this.treeID, this.id); - }, - - get restriction() { - return GetRestriction(this.treeID, this.id); - }, - - get checked() { - return GetChecked(this.treeID, this.id); - }, - - get location() { - return GetLocation(this.treeID, this.id); - }, - - boundsForRange: function(startIndex, endIndex, callback) { - if (!this.rootImpl) - return; - - // Not yet initialized. - if (this.rootImpl.treeID === undefined || this.id === undefined) { - return; - } - - if (!callback) - return; - - if (!GetBoolAttribute(this.treeID, this.id, 'supportsTextLocation')) { - try { - callback( - GetBoundsForRange(this.treeID, this.id, startIndex, endIndex)); - return; - } catch (e) { - logging.WARNING('Error with bounds for range callback' + e); - } - return; - } - - this.performAction_( - 'getTextLocation', {startIndex: startIndex, endIndex: endIndex}, - callback); - return; - }, - - get unclippedLocation() { - var result = GetUnclippedLocation(this.treeID, this.id); - if (result === undefined) - result = GetLocation(this.treeID, this.id); - return result; - }, - - get indexInParent() { - return GetIndexInParent(this.treeID, this.id); - }, - - get lineStartOffsets() { - return GetLineStartOffsets(this.treeID, this.id); - }, - - get childTree() { - var childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId'); - if (childTreeID) - return AutomationRootNodeImpl.get(childTreeID); - }, - - get firstChild() { - if (GetChildCount(this.treeID, this.id) == 0) - return undefined; - var info = GetChildIDAtIndex(this.treeID, this.id, 0); - if (info) - return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId); - }, - - get lastChild() { - var count = GetChildCount(this.treeID, this.id); - if (count == 0) - return; - - var info = GetChildIDAtIndex(this.treeID, this.id, count - 1); - if (info) - return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId); - }, - - get children() { - var info = GetChildIds(this.treeID, this.id); - if (!info) - return []; - - var children = []; - for (var i = 0; i < info.nodeIds.length; ++i) { - var childID = info.nodeIds[i]; - var child = AutomationRootNodeImpl.getNodeFromTree(info.treeId, childID); - if (child) - $Array.push(children, child); - } - return children; - }, - - get previousSibling() { - var parent = this.parent; - if (!parent) - return undefined; - parent = privates(parent).impl; - var indexInParent = GetIndexInParent(this.treeID, this.id); - var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1); - if (info) - return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId); - }, - - get nextSibling() { - var parent = this.parent; - if (!parent) - return undefined; - parent = privates(parent).impl; - var indexInParent = GetIndexInParent(this.treeID, this.id); - var info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1); - if (info) - return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId); - }, - - get nameFrom() { - return GetNameFrom(this.treeID, this.id); - }, - - get bold() { - return GetBold(this.treeID, this.id); - }, - - get italic() { - return GetItalic(this.treeID, this.id); - }, - - get underline() { - return GetUnderline(this.treeID, this.id); - }, - - get lineThrough() { - return GetLineThrough(this.treeID, this.id); - }, - - get customActions() { - return GetCustomActions(this.treeID, this.id); - }, - - get standardActions() { - return GetStandardActions(this.treeID, this.id); - }, - - get defaultActionVerb() { - return GetDefaultActionVerb(this.treeID, this.id); - }, - - get tableCellColumnHeaders() { - var ids = GetTableCellColumnHeaders(this.treeID, this.id); - if (ids && this.rootImpl) { - var result = []; - for (var i = 0; i < ids.length; i++) - result.push(this.rootImpl.get(ids[i])); - return result; - } - }, - - get tableCellRowHeaders() { - var id = GetTableCellRowHeaders(this.treeID, this.id); - if (ids && this.rootImpl) { - var result = []; - for (var i = 0; i < ids.length; i++) - result.push(this.rootImpl.get(ids[i])); - return result; - } - }, - - get tableCellColumnIndex() { - return GetTableCellColumnIndex(this.treeID, this.id); - }, - - get tableCellRowIndex() { - return GetTableCellRowIndex(this.treeID, this.id); - }, - - doDefault: function() { - this.performAction_('doDefault'); - }, - - focus: function() { - this.performAction_('focus'); - }, - - getImageData: function(maxWidth, maxHeight) { - this.performAction_('getImageData', - { maxWidth: maxWidth, - maxHeight: maxHeight }); - }, - - hitTest: function(x, y, eventToFire) { - this.hitTestInternal(x, y, eventToFire); - }, - - hitTestWithReply: function(x, y, opt_callback) { - this.hitTestInternal(x, y, 'hitTestResult', opt_callback); - }, - - hitTestInternal: function(x, y, eventToFire, opt_callback) { - // Convert from global to tree-relative coordinates. - var location = GetLocation(this.treeID, GetRootID(this.treeID)); - this.performAction_('hitTest', - { x: Math.floor(x - location.left), - y: Math.floor(y - location.top), - eventToFire: eventToFire }, - opt_callback); - }, - - makeVisible: function() { - this.performAction_('scrollToMakeVisible'); - }, - - performCustomAction: function(customActionId) { - this.performAction_('customAction', { customActionID: customActionId }); - }, - - performStandardAction: function(action) { - var standardActions = GetStandardActions(this.treeID, this.id); - if (!standardActions || - !standardActions.find(item => action == item)) { - throw 'Inapplicable action for node: ' + action; - } - this.performAction_(action); - }, - - replaceSelectedText: function(value) { - if (this.state.editable) { - this.performAction_('replaceSelectedText', { value: value}); - } - }, - - resumeMedia: function() { - this.performAction_('resumeMedia'); - }, - - scrollBackward: function(opt_callback) { - this.performAction_('scrollBackward', {}, opt_callback); - }, - - scrollForward: function(opt_callback) { - this.performAction_('scrollForward', {}, opt_callback); - }, - - scrollUp: function(opt_callback) { - this.performAction_('scrollUp', {}, opt_callback); - }, - - scrollDown: function(opt_callback) { - this.performAction_('scrollDown', {}, opt_callback); - }, - - scrollLeft: function(opt_callback) { - this.performAction_('scrollLeft', {}, opt_callback); - }, - - scrollRight: function(opt_callback) { - this.performAction_('scrollRight', {}, opt_callback); - }, - - setSelection: function(startIndex, endIndex) { - if (this.state.editable) { - this.performAction_('setSelection', - { focusNodeID: this.id, - anchorOffset: startIndex, - focusOffset: endIndex }); - } - }, - - setSequentialFocusNavigationStartingPoint: function() { - this.performAction_('setSequentialFocusNavigationStartingPoint'); - }, - - setValue: function(value) { - if (this.state.editable) { - this.performAction_('setValue', { value: value}); - } - }, - - showContextMenu: function() { - this.performAction_('showContextMenu'); - }, - - startDuckingMedia: function() { - this.performAction_('startDuckingMedia'); - }, - - stopDuckingMedia: function() { - this.performAction_('stopDuckingMedia'); - }, - - suspendMedia: function() { - this.performAction_('suspendMedia'); - }, - - domQuerySelector: function(selector, callback) { - if (!this.rootImpl) - callback(); - automationInternal.querySelector( - { treeID: this.rootImpl.treeID, - automationNodeID: this.id, - selector: selector }, - $Function.bind(this.domQuerySelectorCallback_, this, callback)); - }, - - find: function(params) { - return this.findInternal_(params); - }, - - findAll: function(params) { - return this.findInternal_(params, []); - }, - - matches: function(params) { - return this.matchInternal_(params); - }, - - getNextTextMatch: function(searchStr, backward) { - var info = GetNextTextMatch(this.treeID, this.id, searchStr, backward); - - if (!info) - return; - - var impl = privates(AutomationRootNodeImpl.get(info.treeId)).impl; - if (impl) - return impl.get(info.nodeId); - }, - - addEventListener: function(eventType, callback, capture) { - this.removeEventListener(eventType, callback); - if (!this.listeners[eventType]) - this.listeners[eventType] = []; - $Array.push(this.listeners[eventType], { - __proto__: null, - callback: callback, - capture: !!capture, - }); - }, - - // TODO(dtseng/aboxhall): Check this impl against spec. - removeEventListener: function(eventType, callback) { - if (this.listeners[eventType]) { - var listeners = this.listeners[eventType]; - for (var i = 0; i < listeners.length; i++) { - if (callback === listeners[i].callback) - $Array.splice(listeners, i, 1); - } - } - }, - - toJSON: function() { - return { treeID: this.treeID, - id: this.id, - role: this.role, - attributes: this.attributes }; - }, - - dispatchEvent: function(eventType, eventFrom, mouseX, mouseY) { - var path = []; - var parent = this.parent; - while (parent) { - $Array.push(path, parent); - parent = parent.parent; - } - var event = new AutomationEvent(eventType, this.wrapper, eventFrom); - event.mouseX = mouseX; - event.mouseY = mouseY; - - // Dispatch the event through the propagation path in three phases: - // - capturing: starting from the root and going down to the target's parent - // - targeting: dispatching the event on the target itself - // - bubbling: starting from the target's parent, going back up to the root. - // At any stage, a listener may call stopPropagation() on the event, which - // will immediately stop event propagation through this path. - if (this.dispatchEventAtCapturing_(event, path)) { - if (this.dispatchEventAtTargeting_(event, path)) - this.dispatchEventAtBubbling_(event, path); - } - }, - - toString: function() { - var parentID = GetParentID(this.treeID, this.id); - parentID = parentID ? parentID.nodeId : null; - var childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId'); - var count = GetChildCount(this.treeID, this.id); - var childIDs = []; - for (var i = 0; i < count; ++i) { - var childID = GetChildIDAtIndex(this.treeID, this.id, i).nodeId; - $Array.push(childIDs, childID); - } - var name = GetStringAttribute(this.treeID, this.id, 'name'); - - var result = 'node id=' + this.id + - ' role=' + this.role + - ' state=' + $JSON.stringify(this.state) + - ' parentID=' + parentID + - ' childIds=' + $JSON.stringify(childIDs); - if (childTreeID) - result += ' childTreeID=' + childTreeID; - if (name) - result += ' name=' + name; - return result; - }, - - dispatchEventAtCapturing_: function(event, path) { - privates(event).impl.eventPhase = Event.CAPTURING_PHASE; - for (var i = path.length - 1; i >= 0; i--) { - this.fireEventListeners_(path[i], event); - if (privates(event).impl.propagationStopped) - return false; - } - return true; - }, - - dispatchEventAtTargeting_: function(event) { - privates(event).impl.eventPhase = Event.AT_TARGET; - this.fireEventListeners_(this.wrapper, event); - return !privates(event).impl.propagationStopped; - }, - - dispatchEventAtBubbling_: function(event, path) { - privates(event).impl.eventPhase = Event.BUBBLING_PHASE; - for (var i = 0; i < path.length; i++) { - this.fireEventListeners_(path[i], event); - if (privates(event).impl.propagationStopped) - return false; - } - return true; - }, - - fireEventListeners_: function(node, event) { - var nodeImpl = privates(node).impl; - if (!nodeImpl.rootImpl) - return; - - var listeners = nodeImpl.listeners[event.type]; - if (!listeners) - return; - var eventPhase = event.eventPhase; - for (var i = 0; i < listeners.length; i++) { - if (eventPhase == Event.CAPTURING_PHASE && !listeners[i].capture) - continue; - if (eventPhase == Event.BUBBLING_PHASE && listeners[i].capture) - continue; - - try { - listeners[i].callback(event); - } catch (e) { - exceptionHandler.handle('Error in event handler for ' + event.type + - ' during phase ' + eventPhase, e); - } - } - }, - - performAction_: function(actionType, opt_args, opt_callback) { - if (!this.rootImpl) - return; - - // Not yet initialized. - if (this.rootImpl.treeID === undefined || - this.id === undefined) { - return; - } - - // Check permissions. - if (!IsInteractPermitted()) { - throw new Error(actionType + ' requires {"desktop": true} or' + - ' {"interact": true} in the "automation" manifest key.'); - } - var requestID = -1; - if (opt_callback) { - requestID = this.rootImpl.addActionResultCallback(opt_callback); - } - - automationInternal.performAction({ treeID: this.rootImpl.treeID, - automationNodeID: this.id, - actionType: actionType, - requestID: requestID}, - opt_args || {}); - }, - - domQuerySelectorCallback_: function(userCallback, resultAutomationNodeID) { - // resultAutomationNodeID could be zero or undefined or (unlikely) null; - // they all amount to the same thing here, which is that no node was - // returned. - if (!resultAutomationNodeID || !this.rootImpl) { - userCallback(null); - return; - } - var resultNode = this.rootImpl.get(resultAutomationNodeID); - if (!resultNode) { - logging.WARNING('Query selector result not in tree: ' + - resultAutomationNodeID); - userCallback(null); - } - userCallback(resultNode); - }, - - findInternal_: function(params, opt_results) { - var result = null; - this.forAllDescendants_(function(node) { - if (privates(node).impl.matchInternal_(params)) { - if (opt_results) - $Array.push(opt_results, node); - else - result = node; - return !opt_results; - } - }); - if (opt_results) - return opt_results; - return result; - }, - - /** - * Executes a closure for all of this node's descendants, in pre-order. - * Early-outs if the closure returns true. - * @param {Function(AutomationNode):boolean} closure Closure to be executed - * for each node. Return true to early-out the traversal. - */ - forAllDescendants_: function(closure) { - var stack = $Array.reverse(this.wrapper.children); - while (stack.length > 0) { - var node = $Array.pop(stack); - if (closure(node)) - return; - - var children = node.children; - for (var i = children.length - 1; i >= 0; i--) - $Array.push(stack, children[i]); - } - }, - - matchInternal_: function(params) { - if ($Object.keys(params).length === 0) - return false; - - if ('role' in params && this.role != params.role) - return false; - - if ('state' in params) { - for (var state in params.state) { - if (params.state[state] != (state in this.state)) - return false; - } - } - if ('attributes' in params) { - for (var attribute in params.attributes) { - var attrValue = params.attributes[attribute]; - if (typeof attrValue != 'object') { - if (this[attribute] !== attrValue) - return false; - } else if (attrValue instanceof $RegExp.self) { - if (typeof this[attribute] != 'string') - return false; - if (!attrValue.test(this[attribute])) - return false; - } else { - // TODO(aboxhall): handle intlist case. - return false; - } - } - } - return true; - } -}; - -var stringAttributes = [ - 'accessKey', - 'ariaInvalidValue', - 'autoComplete', - 'className', - 'containerLiveRelevant', - 'containerLiveStatus', - 'description', - 'display', - 'htmlTag', - 'imageDataUrl', - 'innerHtml', - 'language', - 'liveRelevant', - 'liveStatus', - 'name', - 'placeholder', - 'roleDescription', - 'textInputType', - 'url', - 'value']; - -var boolAttributes = [ - 'busy', 'clickable', 'containerLiveAtomic', 'containerLiveBusy', 'liveAtomic', - 'modal', 'scrollable', 'selected', 'supportsTextLocation' -]; - -var intAttributes = [ - 'backgroundColor', - 'color', - 'colorValue', - 'hierarchicalLevel', - 'posInSet', - 'scrollX', - 'scrollXMax', - 'scrollXMin', - 'scrollY', - 'scrollYMax', - 'scrollYMin', - 'setSize', - 'ariaCellColumnIndex', - 'tableCellColumnSpan', - 'ariaCellRowIndex', - 'tableCellRowSpan', - 'tableColumnCount', - 'ariaColumnCount', - 'tableColumnIndex', - 'tableRowCount', - 'ariaRowCount', - 'tableRowIndex', - 'textSelEnd', - 'textSelStart']; - -// Int attribute, relation property to expose, reverse relation to expose. -var nodeRefAttributes = [ - ['activedescendantId', 'activeDescendant', 'activeDescendantFor'], - ['detailsId', 'details', 'detailsFor'], - ['errorMessageId', 'errorMessage', 'errorMessageFor'], - ['inPageLinkTargetId', 'inPageLinkTarget', null], - ['nextFocusId', 'nextFocus', null], - ['nextOnLineId', 'nextOnLine', null], - ['previousFocusId', 'previousFocus', null], - ['previousOnLineId', 'previousOnLine', null], - ['tableColumnHeaderId', 'tableColumnHeader', null], - ['tableHeaderId', 'tableHeader', null], - ['tableRowHeaderId', 'tableRowHeader', null]]; - -var intListAttributes = [ - 'lineBreaks', - 'markerEnds', - 'markerStarts', - 'markerTypes', - 'wordEnds', - 'wordStarts']; - -// Intlist attribute, relation property to expose, reverse relation to expose. -var nodeRefListAttributes = [ - ['controlsIds', 'controls', 'controlledBy'], - ['describedbyIds', 'describedBy', 'descriptionFor'], - ['flowtoIds', 'flowTo', 'flowFrom'], - ['labelledbyIds', 'labelledBy', 'labelFor']]; - -var floatAttributes = [ - 'valueForRange', - 'minValueForRange', - 'maxValueForRange']; - -var htmlAttributes = [ - ['type', 'inputType']]; - -var publicAttributes = []; - -$Array.forEach(stringAttributes, function(attributeName) { - $Array.push(publicAttributes, attributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, { - __proto__: null, - get: function() { - return GetStringAttribute(this.treeID, this.id, attributeName); - } - }); -}); - -$Array.forEach(boolAttributes, function(attributeName) { - $Array.push(publicAttributes, attributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, { - __proto__: null, - get: function() { - return GetBoolAttribute(this.treeID, this.id, attributeName); - } - }); -}); - -$Array.forEach(intAttributes, function(attributeName) { - $Array.push(publicAttributes, attributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, { - __proto__: null, - get: function() { - return GetIntAttribute(this.treeID, this.id, attributeName); - } - }); -}); - -$Array.forEach(nodeRefAttributes, function(params) { - var srcAttributeName = params[0]; - var dstAttributeName = params[1]; - var dstReverseAttributeName = params[2]; - $Array.push(publicAttributes, dstAttributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, { - __proto__: null, - get: function() { - var id = GetIntAttribute(this.treeID, this.id, srcAttributeName); - if (id && this.rootImpl) - return this.rootImpl.get(id); - else - return undefined; - } - }); - if (dstReverseAttributeName) { - $Array.push(publicAttributes, dstReverseAttributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, - dstReverseAttributeName, { - __proto__: null, - get: function() { - var ids = GetIntAttributeReverseRelations( - this.treeID, this.id, srcAttributeName); - if (!ids || !this.rootImpl) - return undefined; - var result = []; - for (var i = 0; i < ids.length; ++i) { - var node = this.rootImpl.get(ids[i]); - if (node) - $Array.push(result, node); - } - return result; - } - }); - } -}); - -$Array.forEach(intListAttributes, function(attributeName) { - $Array.push(publicAttributes, attributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, { - __proto__: null, - get: function() { - return GetIntListAttribute(this.treeID, this.id, attributeName); - } - }); -}); - -$Array.forEach(nodeRefListAttributes, function(params) { - var srcAttributeName = params[0]; - var dstAttributeName = params[1]; - var dstReverseAttributeName = params[2]; - $Array.push(publicAttributes, dstAttributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, { - __proto__: null, - get: function() { - var ids = GetIntListAttribute(this.treeID, this.id, srcAttributeName); - if (!ids || !this.rootImpl) - return undefined; - var result = []; - for (var i = 0; i < ids.length; ++i) { - var node = this.rootImpl.get(ids[i]); - if (node) - $Array.push(result, node); - } - return result; - } - }); - if (dstReverseAttributeName) { - $Array.push(publicAttributes, dstReverseAttributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, - dstReverseAttributeName, { - __proto__: null, - get: function() { - var ids = GetIntListAttributeReverseRelations( - this.treeID, this.id, srcAttributeName); - if (!ids || !this.rootImpl) - return undefined; - var result = []; - for (var i = 0; i < ids.length; ++i) { - var node = this.rootImpl.get(ids[i]); - if (node) - $Array.push(result, node); - } - return result; - } - }); - } -}); - -$Array.forEach(floatAttributes, function(attributeName) { - $Array.push(publicAttributes, attributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, attributeName, { - __proto__: null, - get: function() { - return GetFloatAttribute(this.treeID, this.id, attributeName); - } - }); -}); - -$Array.forEach(htmlAttributes, function(params) { - var srcAttributeName = params[0]; - var dstAttributeName = params[1]; - $Array.push(publicAttributes, dstAttributeName); - $Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, { - __proto__: null, - get: function() { - return GetHtmlAttribute(this.treeID, this.id, srcAttributeName); - } - }); -}); - -/** - * AutomationRootNode. - * - * An AutomationRootNode is the javascript end of an AXTree living in the - * browser. AutomationRootNode handles unserializing incremental updates from - * the source AXTree. Each update contains node data that form a complete tree - * after applying the update. - * - * A brief note about ids used through this class. The source AXTree assigns - * unique ids per node and we use these ids to build a hash to the actual - * AutomationNode object. - * Thus, tree traversals amount to a lookup in our hash. - * - * The tree itself is identified by the accessibility tree id of the - * renderer widget host. - * @constructor - */ -function AutomationRootNodeImpl(treeID) { - $Function.call(AutomationNodeImpl, this, this); - this.treeID = treeID; - this.axNodeDataCache_ = {__proto__: null}; -} - -utils.defineProperty(AutomationRootNodeImpl, 'idToAutomationRootNode_', - {__proto__: null}); - -utils.defineProperty(AutomationRootNodeImpl, 'get', function(treeID) { - var result = AutomationRootNodeImpl.idToAutomationRootNode_[treeID]; - return result || undefined; -}); - -utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) { - if (AutomationRootNodeImpl.idToAutomationRootNode_[treeID]) - return AutomationRootNodeImpl.idToAutomationRootNode_[treeID]; - var result = new AutomationRootNode(treeID); - AutomationRootNodeImpl.idToAutomationRootNode_[treeID] = result; - return result; -}); - -utils.defineProperty( - AutomationRootNodeImpl, 'getNodeFromTree', function(treeId, nodeId) { - var tree = AutomationRootNodeImpl.get(treeId); - if (!tree) - return; - var impl = privates(tree).impl; - if (impl) - return impl.get(nodeId); -}); - -utils.defineProperty(AutomationRootNodeImpl, 'destroy', function(treeID) { - delete AutomationRootNodeImpl.idToAutomationRootNode_[treeID]; -}); - -/** - * A counter keeping track of IDs to use for mapping action requests to - * their callback function. - */ -AutomationRootNodeImpl.actionRequestCounter = 0; - -/** - * A map from a request ID to the corresponding callback function to call - * when the action response event is received. - */ -AutomationRootNodeImpl.actionRequestIDToCallback = {}; - -AutomationRootNodeImpl.prototype = { - __proto__: AutomationNodeImpl.prototype, - - /** - * @type {boolean} - */ - isRootNode: true, - - /** - * @type {string} - */ - treeID: '', - - /** - * A map from id to AutomationNode. - * @type {Object.<number, AutomationNode>} - * @private - */ - axNodeDataCache_: null, - - get id() { - var result = GetRootID(this.treeID); - - // Don't return undefined, because the id is often passed directly - // as an argument to a native binding that expects only a valid number. - if (result === undefined) - return -1; - - return result; - }, - - get docUrl() { - return GetDocURL(this.treeID); - }, - - get docTitle() { - return GetDocTitle(this.treeID); - }, - - get docLoaded() { - return GetDocLoaded(this.treeID); - }, - - get docLoadingProgress() { - return GetDocLoadingProgress(this.treeID); - }, - - get anchorObject() { - var id = GetAnchorObjectID(this.treeID); - if (id && id != -1) - return this.get(id); - else - return undefined; - }, - - get anchorOffset() { - var id = GetAnchorObjectID(this.treeID); - if (id && id != -1) - return GetAnchorOffset(this.treeID); - }, - - get anchorAffinity() { - var id = GetAnchorObjectID(this.treeID); - if (id && id != -1) - return GetAnchorAffinity(this.treeID); - }, - - get focusObject() { - var id = GetFocusObjectID(this.treeID); - if (id && id != -1) - return this.get(id); - else - return undefined; - }, - - get focusOffset() { - var id = GetFocusObjectID(this.treeID); - if (id && id != -1) - return GetFocusOffset(this.treeID); - }, - - get focusAffinity() { - var id = GetFocusObjectID(this.treeID); - if (id && id != -1) - return GetFocusAffinity(this.treeID); - }, - - get: function(id) { - if (id == undefined) - return undefined; - - if (id == this.id) - return this.wrapper; - - var obj = this.axNodeDataCache_[id]; - if (obj) - return obj; - - // Validate the backing AXTree has the specified node. - if (!GetRole(this.treeID, id)) - return; - - obj = new AutomationNode(this); - privates(obj).impl.treeID = this.treeID; - privates(obj).impl.id = id; - this.axNodeDataCache_[id] = obj; - - return obj; - }, - - remove: function(id) { - if (this.axNodeDataCache_[id]) - privates(this.axNodeDataCache_[id]).impl.detach(); - delete this.axNodeDataCache_[id]; - }, - - destroy: function() { - this.dispatchEvent('destroyed', 'none'); - for (var id in this.axNodeDataCache_) - this.remove(id); - this.detach(); - }, - - onAccessibilityEvent: function(eventParams) { - var targetNode = this.get(eventParams.targetID); - if (targetNode) { - var targetNodeImpl = privates(targetNode).impl; - targetNodeImpl.dispatchEvent( - eventParams.eventType, eventParams.eventFrom, - eventParams.mouseX, eventParams.mouseY); - - if (eventParams.actionRequestID != -1) { - this.onActionResult(eventParams.actionRequestID, targetNode); - } - } else { - logging.WARNING('Got ' + eventParams.eventType + - ' event on unknown node: ' + eventParams.targetID + - '; this: ' + this.id); - } - return true; - }, - - addActionResultCallback: function(callback) { - AutomationRootNodeImpl.actionRequestIDToCallback[ - ++AutomationRootNodeImpl.actionRequestCounter] = callback; - return AutomationRootNodeImpl.actionRequestCounter; - }, - - onGetTextLocationResult: function(textLocationParams) { - let requestID = textLocationParams.requestID; - if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) { - let callback = - AutomationRootNodeImpl.actionRequestIDToCallback[requestID]; - try { - if (textLocationParams.result) { - callback(ComputeGlobalBounds( - this.treeID, textLocationParams.nodeID, textLocationParams.left, - textLocationParams.top, textLocationParams.width, - textLocationParams.height)); - } else { - callback(undefined); - } - } catch (e) { - logging.WARNING('Error with onGetTextLocationResult callback:' + e); - } - delete AutomationNodeImpl.actionRequestIDToCallback[requestID]; - } - }, - - - onActionResult: function(requestID, result) { - if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) { - AutomationRootNodeImpl.actionRequestIDToCallback[requestID](result); - delete AutomationRootNodeImpl.actionRequestIDToCallback[requestID]; - } - }, - - toString: function() { - function toStringInternal(nodeImpl, indent) { - if (!nodeImpl) - return ''; - var output = ''; - if (nodeImpl.isRootNode) - output += indent + 'tree id=' + nodeImpl.treeID + '\n'; - output += indent + - $Function.call(AutomationNodeImpl.prototype.toString, nodeImpl) + '\n'; - indent += ' '; - var children = nodeImpl.children; - for (var i = 0; i < children.length; ++i) - output += toStringInternal(privates(children[i]).impl, indent); - return output; - } - return toStringInternal(this, ''); - }, -}; - -function AutomationNode() { - privates(AutomationNode).constructPrivate(this, arguments); -} -utils.expose(AutomationNode, AutomationNodeImpl, { - functions: [ - 'doDefault', - 'find', - 'findAll', - 'focus', - 'getImageData', - 'getNextTextMatch', - 'hitTest', - 'hitTestWithReply', - 'makeVisible', - 'matches', - 'performCustomAction', - 'performStandardAction', - 'replaceSelectedText', - 'resumeMedia', - 'scrollBackward', - 'scrollForward', - 'scrollUp', - 'scrollDown', - 'scrollLeft', - 'scrollRight', - 'setSelection', - 'setSequentialFocusNavigationStartingPoint', - 'setValue', - 'showContextMenu', - 'startDuckingMedia', - 'stopDuckingMedia', - 'suspendMedia', - 'addEventListener', - 'removeEventListener', - 'domQuerySelector', - 'toString', - 'boundsForRange', - ], - readonly: $Array.concat( - publicAttributes, - [ - 'parent', - 'firstChild', - 'lastChild', - 'children', - 'previousSibling', - 'nextSibling', - 'isRootNode', - 'role', - 'checked', - 'defaultActionVerb', - 'restriction', - 'state', - 'location', - 'indexInParent', - 'lineStartOffsets', - 'root', - 'htmlAttributes', - 'nameFrom', - 'bold', - 'italic', - 'underline', - 'lineThrough', - 'customActions', - 'standardActions', - 'unclippedLocation', - 'tableCellColumnHeaders', - 'tableCellRowHeaders', - 'tableCellColumnIndex', - 'tableCellRowIndex', - ]), -}); - -function AutomationRootNode() { - privates(AutomationRootNode).constructPrivate(this, arguments); -} -utils.expose(AutomationRootNode, AutomationRootNodeImpl, { - superclass: AutomationNode, - readonly: [ - 'docTitle', - 'docUrl', - 'docLoaded', - 'docLoadingProgress', - 'anchorObject', - 'anchorOffset', - 'anchorAffinity', - 'focusObject', - 'focusOffset', - 'focusAffinity', - ], -}); - -utils.defineProperty(AutomationRootNode, 'get', function(treeID) { - return AutomationRootNodeImpl.get(treeID); -}); - -utils.defineProperty(AutomationRootNode, 'getOrCreate', function(treeID) { - return AutomationRootNodeImpl.getOrCreate(treeID); -}); - -utils.defineProperty(AutomationRootNode, 'destroy', function(treeID) { - AutomationRootNodeImpl.destroy(treeID); -}); - -exports.$set('AutomationNode', AutomationNode); -exports.$set('AutomationRootNode', AutomationRootNode);
diff --git a/chromecast/renderer/resources/extensions/automation_custom_bindings.js b/chromecast/renderer/resources/extensions/automation_custom_bindings.js deleted file mode 100644 index a215606..0000000 --- a/chromecast/renderer/resources/extensions/automation_custom_bindings.js +++ /dev/null
@@ -1,353 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Custom bindings for the automation API. -var AutomationNode = require('automationNode').AutomationNode; -var AutomationRootNode = require('automationNode').AutomationRootNode; -var automationInternal = getInternalApi('automationInternal'); -var exceptionHandler = require('uncaught_exception_handler'); -var logging = requireNative('logging'); -var nativeAutomationInternal = requireNative('automationInternal'); -var DestroyAccessibilityTree = - nativeAutomationInternal.DestroyAccessibilityTree; -var GetIntAttribute = nativeAutomationInternal.GetIntAttribute; -var StartCachingAccessibilityTrees = - nativeAutomationInternal.StartCachingAccessibilityTrees; -var AddTreeChangeObserver = nativeAutomationInternal.AddTreeChangeObserver; -var RemoveTreeChangeObserver = - nativeAutomationInternal.RemoveTreeChangeObserver; -var GetFocusNative = nativeAutomationInternal.GetFocus; - -/** - * A namespace to export utility functions to other files in automation. - */ -window.automationUtil = function() {}; - -// TODO(aboxhall): Look into using WeakMap -var idToCallback = {}; - -var desktopId = undefined; - -automationUtil.storeTreeCallback = function(id, callback) { - if (!callback) - return; - - var targetTree = AutomationRootNode.get(id); - if (!targetTree) { - // If we haven't cached the tree, hold the callback until the tree is - // populated by the initial onAccessibilityEvent call. - if (id in idToCallback) - idToCallback[id].push(callback); - else - idToCallback[id] = [callback]; - } else { - callback(targetTree); - } -}; - -/** - * Global list of tree change observers. - * @type {Object<number, TreeChangeObserver>} - */ -automationUtil.treeChangeObserverMap = {}; - -/** - * The id of the next tree change observer. - * @type {number} - */ -automationUtil.nextTreeChangeObserverId = 1; - -/** - * @type {AutomationNode} The current focused node. This is only updated - * when calling automationUtil.updateFocusedNode. - */ -automationUtil.focusedNode = null; - -/** - * Gets the currently focused AutomationNode. - * @return {AutomationNode} - */ -automationUtil.getFocus = function() { - if (desktopId === undefined) - return; - - var focusedNodeInfo = GetFocusNative(desktopId); - if (!focusedNodeInfo) - return null; - var tree = AutomationRootNode.getOrCreate(focusedNodeInfo.treeId); - if (tree) - return privates(tree).impl.get(focusedNodeInfo.nodeId); -}; - -/** - * Update automationUtil.focusedNode to be the node that currently has focus. - */ -automationUtil.updateFocusedNode = function() { - automationUtil.focusedNode = automationUtil.getFocus(); -}; - -/** - * Updates the focus on blur. - */ -automationUtil.updateFocusedNodeOnBlur = function() { - var focus = automationUtil.getFocus(); - automationUtil.focusedNode = focus ? focus.root : null; -}; - -apiBridge.registerCustomHook(function(bindingsAPI) { - var apiFunctions = bindingsAPI.apiFunctions; - - // TODO(aboxhall, dtseng): Make this return the speced AutomationRootNode obj. - apiFunctions.setHandleRequest('getTree', function getTree(tabID, callback) { - StartCachingAccessibilityTrees(); - - // enableTab() ensures the renderer for the active or specified tab has - // accessibility enabled, and fetches its ax tree id to use as - // a key in the idToAutomationRootNode map. The callback to - // enableTab is bound to the callback passed in to getTree(), so that once - // the tree is available (either due to having been cached earlier, or after - // an accessibility event occurs which causes the tree to be populated), the - // callback can be called. - var params = { tabID: tabID }; - automationInternal.enableTab(params, - function onEnable(id) { - if (bindingUtil.hasLastError()) { - callback(); - return; - } - automationUtil.storeTreeCallback(id, callback); - }); - }); - - var desktopTree = null; - apiFunctions.setHandleRequest('getDesktop', function(callback) { - StartCachingAccessibilityTrees(); - if (desktopId !== undefined) - desktopTree = AutomationRootNode.get(desktopId); - if (!desktopTree) { - automationInternal.enableDesktop(function(treeId) { - if (bindingUtil.hasLastError()) { - AutomationRootNode.destroy(treeId); - desktopId = undefined; - callback(); - return; - } - desktopId = treeId; - desktopTree = AutomationRootNode.getOrCreate(desktopId); - callback(desktopTree); - - // TODO(dtseng): Disable desktop tree once desktop object goes out of - // scope. - }); - } else { - callback(desktopTree); - } - }); - - apiFunctions.setHandleRequest('getFocus', function(callback) { - callback(automationUtil.getFocus()); - }); - - function removeTreeChangeObserver(observer) { - for (var id in automationUtil.treeChangeObserverMap) { - if (automationUtil.treeChangeObserverMap[id] == observer) { - RemoveTreeChangeObserver(id); - delete automationUtil.treeChangeObserverMap[id]; - return; - } - } - } - apiFunctions.setHandleRequest('removeTreeChangeObserver', function(observer) { - removeTreeChangeObserver(observer); - }); - - function addTreeChangeObserver(filter, observer) { - removeTreeChangeObserver(observer); - var id = automationUtil.nextTreeChangeObserverId++; - AddTreeChangeObserver(id, filter); - automationUtil.treeChangeObserverMap[id] = observer; - } - apiFunctions.setHandleRequest('addTreeChangeObserver', - function(filter, observer) { - addTreeChangeObserver(filter, observer); - }); - - apiFunctions.setHandleRequest('setDocumentSelection', function(params) { - var anchorNodeImpl = privates(params.anchorObject).impl; - var focusNodeImpl = privates(params.focusObject).impl; - if (anchorNodeImpl.treeID !== focusNodeImpl.treeID) - throw new Error('Selection anchor and focus must be in the same tree.'); - if (anchorNodeImpl.treeID === desktopId) { - throw new Error('Use AutomationNode.setSelection to set the selection ' + - 'in the desktop tree.'); - } - automationInternal.performAction({ treeID: anchorNodeImpl.treeID, - automationNodeID: anchorNodeImpl.id, - actionType: 'setSelection'}, - { focusNodeID: focusNodeImpl.id, - anchorOffset: params.anchorOffset, - focusOffset: params.focusOffset }); - }); - -}); - -automationInternal.onChildTreeID.addListener(function(childTreeId) { - var targetTree = AutomationRootNode.get(childTreeId); - - // If the tree is already loded, or if we previously requested it be loaded - // (i.e. have a callback for it), don't try to do so again. - if (targetTree || idToCallback[childTreeId]) - return; - - // A WebView in the desktop tree has a different AX tree as its child. - // When we encounter a WebView with a child AX tree id that we don't - // currently have cached, explicitly request that AX tree from the - // browser process and set up a callback when it loads to attach that - // tree as a child of this node and fire appropriate events. - automationUtil.storeTreeCallback(childTreeId, function(root) { - privates(root).impl.dispatchEvent('loadComplete', 'page'); - }, true); - - automationInternal.enableFrame(childTreeId); -}); - -automationInternal.onTreeChange.addListener(function(observerID, - treeID, - nodeID, - changeType) { - var tree = AutomationRootNode.getOrCreate(treeID); - if (!tree) - return; - - var node = privates(tree).impl.get(nodeID); - if (!node) - return; - - var observer = automationUtil.treeChangeObserverMap[observerID]; - if (!observer) - return; - - try { - observer({target: node, type: changeType}); - } catch (e) { - exceptionHandler.handle('Error in tree change observer for ' + - changeType, e); - } -}); - -automationInternal.onNodesRemoved.addListener(function(treeID, nodeIDs) { - var tree = AutomationRootNode.getOrCreate(treeID); - if (!tree) - return; - - for (var i = 0; i < nodeIDs.length; i++) { - privates(tree).impl.remove(nodeIDs[i]); - } -}); - -/** - * Dispatch accessibility events fired on individual nodes to its - * corresponding AutomationNode. Handle focus events specially - * (see below). - */ -automationInternal.onAccessibilityEvent.addListener(function(eventParams) { - var id = eventParams.treeID; - var targetTree = AutomationRootNode.getOrCreate(id); - if (eventParams.eventType == 'blur') { - // Work around an issue where Chrome sends us 'blur' events on the - // root node when nothing has focus, we need to treat those as focus - // events but otherwise not handle blur events specially. - var node = privates(targetTree).impl.get(eventParams.targetID); - if (!node) - return; - - if (node == node.root) - automationUtil.updateFocusedNodeOnBlur(); - } else if (eventParams.eventType == 'mediaStartedPlaying' || - eventParams.eventType == 'mediaStoppedPlaying') { - // These events are global to the tree. - eventParams.targetID = privates(targetTree).impl.id; - } else { - var previousFocusedNode = automationUtil.focusedNode; - automationUtil.updateFocusedNode(); - - // Fire focus events if necessary. - if (automationUtil.focusedNode && - automationUtil.focusedNode != previousFocusedNode) { - var eventParamsCopy = {}; - for (var key in eventParams) - eventParamsCopy[key] = eventParams[key]; - eventParamsCopy['eventType'] = 'focus'; - eventParamsCopy['treeID'] = - privates(automationUtil.focusedNode.root).impl.treeID; - eventParamsCopy['targetID'] = - privates(automationUtil.focusedNode).impl.id; - privates(automationUtil.focusedNode.root) - .impl.onAccessibilityEvent(eventParamsCopy); - } - } - - // Note that focus type events have already been handled above if there was a - // focused node. All other events, even non-focus events that triggered a - // focus dispatch, still need to have their original event fired. - if ((!automationUtil.focusedNode || eventParams.eventType != 'focus') && - !privates(targetTree).impl.onAccessibilityEvent(eventParams)) - return; - - // If we're not waiting on a callback to getTree(), we can early out here. - if (!(id in idToCallback)) - return; - - // We usually get a 'placeholder' tree first, which doesn't have any url - // attribute or child nodes. If we've got that, wait for the full tree before - // calling the callback. - // TODO(dmazzoni): Don't send down placeholder (crbug.com/397553) - if (id != desktopId && !targetTree.url && targetTree.children.length == 0) - return; - - // If the tree wasn't available when getTree() was called, the callback will - // have been cached in idToCallback, so call and delete it now that we - // have the complete tree. - for (var i = 0; i < idToCallback[id].length; i++) { - var callback = idToCallback[id][i]; - callback(targetTree); - } - delete idToCallback[id]; -}); - -automationInternal.onAccessibilityTreeDestroyed.addListener(function(id) { - // Destroy the AutomationRootNode. - var targetTree = AutomationRootNode.get(id); - if (targetTree) { - privates(targetTree).impl.destroy(); - AutomationRootNode.destroy(id); - } else { - logging.WARNING('no targetTree to destroy'); - } - - // Destroy the native cache of the accessibility tree. - DestroyAccessibilityTree(id); -}); - -automationInternal.onAccessibilityTreeSerializationError.addListener( - function(id) { - automationInternal.enableFrame(id); -}); - -automationInternal.onActionResult.addListener(function( - treeID, requestID, result) { - var targetTree = AutomationRootNode.get(treeID); - if (!targetTree) - return; - - privates(targetTree).impl.onActionResult(requestID, result); -}); - -automationInternal.onGetTextLocationResult.addListener(function( - textLocationParams) { - var targetTree = AutomationRootNode.get(textLocationParams.treeID); - if (!targetTree) - return; - privates(targetTree).impl.onGetTextLocationResult(textLocationParams); -});
diff --git a/chromecast/renderer/resources/extensions_renderer_resources.grd b/chromecast/renderer/resources/extensions_renderer_resources.grd index f71c0c5..04b8ffd 100644 --- a/chromecast/renderer/resources/extensions_renderer_resources.grd +++ b/chromecast/renderer/resources/extensions_renderer_resources.grd
@@ -9,9 +9,6 @@ <release seq="1"> <includes> <!-- Custom bindings for extension APIs. --> - <include name="IDR_AUTOMATION_CUSTOM_BINDINGS_JS" file="extensions\automation_custom_bindings.js" type="BINDATA" /> - <include name="IDR_AUTOMATION_EVENT_JS" file="extensions\automation\automation_event.js" type="BINDATA" /> - <include name="IDR_AUTOMATION_NODE_JS" file="extensions\automation\automation_node.js" type="BINDATA" /> <include name="IDR_TTS_CUSTOM_BINDINGS_JS" file="extensions\tts_custom_bindings.js" type="BINDATA" /> </includes> </release>
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.cc b/chromeos/dbus/session_manager/fake_session_manager_client.cc index 55b8c7a5..44c76fa 100644 --- a/chromeos/dbus/session_manager/fake_session_manager_client.cc +++ b/chromeos/dbus/session_manager/fake_session_manager_client.cc
@@ -545,12 +545,7 @@ void FakeSessionManagerClient::GetServerBackedStateKeys( StateKeysCallback callback) { if (force_state_keys_missing_) { - // Need delay to prevent excessive warning output in tests caused by being - // retried in a tight loop. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::BindOnce(std::move(callback), std::vector<std::string>()), - base::TimeDelta::FromSeconds(1)); + PostReply(FROM_HERE, std::move(callback), std::vector<std::string>()); return; }
diff --git a/components/gcm_driver/account_tracker.cc b/components/gcm_driver/account_tracker.cc index e844187..f0c0f8d 100644 --- a/components/gcm_driver/account_tracker.cc +++ b/components/gcm_driver/account_tracker.cc
@@ -69,7 +69,7 @@ void AccountTracker::OnRefreshTokenUpdatedForAccount( const CoreAccountInfo& account_info) { TRACE_EVENT1("identity", "AccountTracker::OnRefreshTokenUpdatedForAccount", - "account_id", account_info.account_id); + "account_id", account_info.account_id.id); // Ignore refresh tokens if there is no active account ID at all. if (!identity_manager_->HasPrimaryAccount())
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc index 7fc1e6f..39930ba 100644 --- a/components/gcm_driver/gcm_driver_android.cc +++ b/components/gcm_driver/gcm_driver_android.cc
@@ -85,7 +85,7 @@ message.sender_id = ConvertJavaStringToUTF8(env, j_sender_id); if (!j_message_id.is_null()) - ConvertJavaStringToUTF8(env, j_collapse_key, &message.message_id); + ConvertJavaStringToUTF8(env, j_message_id, &message.message_id); if (!j_collapse_key.is_null()) ConvertJavaStringToUTF8(env, j_collapse_key, &message.collapse_key);
diff --git a/components/offline_pages/core/prefetch/BUILD.gn b/components/offline_pages/core/prefetch/BUILD.gn index a6fa076..620dcc3d 100644 --- a/components/offline_pages/core/prefetch/BUILD.gn +++ b/components/offline_pages/core/prefetch/BUILD.gn
@@ -90,6 +90,8 @@ "tasks/metrics_finalization_task.h", "tasks/page_bundle_update_task.cc", "tasks/page_bundle_update_task.h", + "tasks/remove_url_task.cc", + "tasks/remove_url_task.h", "tasks/sent_get_operation_cleanup_task.cc", "tasks/sent_get_operation_cleanup_task.h", "tasks/stale_entry_finalizer_task.cc",
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc index 58edc63..a679cb6 100644 --- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc +++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -44,6 +44,7 @@ #include "components/offline_pages/core/prefetch/tasks/mark_operation_done_task.h" #include "components/offline_pages/core/prefetch/tasks/metrics_finalization_task.h" #include "components/offline_pages/core/prefetch/tasks/page_bundle_update_task.h" +#include "components/offline_pages/core/prefetch/tasks/remove_url_task.h" #include "components/offline_pages/core/prefetch/tasks/sent_get_operation_cleanup_task.h" #include "components/offline_pages/core/prefetch/tasks/stale_entry_finalizer_task.h" #include "components/offline_pages/core/prefetch/thumbnail_fetcher.h" @@ -146,8 +147,17 @@ void PrefetchDispatcherImpl::RemoveSuggestion(const GURL& url) { if (!prefetch_prefs::IsEnabled(pref_service_)) return; - // TODO(https://crbug.com/841516): to be implemented soon. - NOTIMPLEMENTED(); + + // Remove the URL from the prefetch database. + task_queue_.AddTask(MakeRemoveUrlTask(service_->GetPrefetchStore(), url)); + + // Remove the URL from the offline model. + PageCriteria criteria; + criteria.url = url; + criteria.client_namespaces = + std::vector<std::string>{kSuggestedArticlesNamespace}; + service_->GetOfflinePageModel()->DeletePagesWithCriteria(criteria, + base::DoNothing()); } void PrefetchDispatcherImpl::RemoveAllUnprocessedPrefetchURLs(
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc index 2007a27..37ceb4f 100644 --- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc +++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -166,6 +166,8 @@ MOCK_METHOD2(AddPage, void(const OfflinePageItem& page, AddPageCallback callback)); + MOCK_METHOD2(DeletePagesWithCriteria, + void(const PageCriteria& criteria, DeletePageCallback callback)); void StoreThumbnail(int64_t offline_id, std::string thumbnail) override { insert_or_update_visuals(offline_id, thumbnail, std::string()); @@ -425,6 +427,13 @@ PrefetchService* prefetch_service() { return taco_->prefetch_service(); } TestDownloadService* download_service() { return taco_->download_service(); } + // Asserts that there exists a single item in the database, and returns it. + PrefetchItem GetSingleItem() { + std::set<PrefetchItem> items; + EXPECT_EQ(1ul, store_util_.GetAllItems(&items)); + return *items.begin(); + } + protected: // Owned by |taco_|. MockOfflinePageModel* offline_model_; @@ -1069,4 +1078,50 @@ EXPECT_EQ(PrefetchItemErrorCode::SUCCESS, item.error_code); } +// Tests that |RemoveSuggestion()| removes items from the offline database, and +// triggers finalization of the prefetch item. +TEST_F(PrefetchDispatcherTest, RemoveSuggestion) { + Configure(PrefetchServiceTestTaco::kFeed); + + EXPECT_CALL(*offline_model_, DeletePagesWithCriteria(_, _)) + .WillOnce([&](const PageCriteria& criteria, DeletePageCallback callback) { + EXPECT_EQ(TestSuggestion1().article_url, criteria.url); + EXPECT_EQ(std::vector<std::string>({kSuggestedArticlesNamespace}), + criteria.client_namespaces); + }); + + suggestions_provider_->SetSuggestions({TestSuggestion1()}); + prefetch_service()->NewSuggestionsAvailable(); + RunUntilIdle(); + + const PrefetchItem item_state_1 = GetSingleItem(); + + dispatcher()->RemoveSuggestion(TestSuggestion1().article_url); + RunUntilIdle(); + const PrefetchItem item_state_2 = GetSingleItem(); + + // The item is initially not finished. + EXPECT_EQ(TestSuggestion1().article_url, item_state_1.url); + EXPECT_NE(PrefetchItemState::FINISHED, item_state_1.state); + + // The item is finished after the suggestion is removed. + EXPECT_EQ(PrefetchItemState::FINISHED, item_state_2.state); +} + +// Verify that we can attempt to remove a URL that isn't in the prefetch +// database. +TEST_F(PrefetchDispatcherTest, RemoveSuggestionDoesNotExist) { + Configure(PrefetchServiceTestTaco::kFeed); + + suggestions_provider_->SetSuggestions({TestSuggestion1()}); + prefetch_service()->NewSuggestionsAvailable(); + RunUntilIdle(); + + dispatcher()->RemoveSuggestion(GURL("http://otherurl.com")); + RunUntilIdle(); + + // Verify the item still exists. + GetSingleItem(); +} + } // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/tasks/remove_url_task.cc b/components/offline_pages/core/prefetch/tasks/remove_url_task.cc new file mode 100644 index 0000000..ae2fe6f --- /dev/null +++ b/components/offline_pages/core/prefetch/tasks/remove_url_task.cc
@@ -0,0 +1,40 @@ +// 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 "components/offline_pages/core/prefetch/tasks/remove_url_task.h" + +#include "components/offline_pages/core/prefetch/prefetch_types.h" +#include "components/offline_pages/core/prefetch/store/prefetch_store.h" +#include "sql/database.h" +#include "sql/statement.h" + +namespace offline_pages { + +namespace { + +bool RemoveURL(const GURL& url, sql::Database* db) { + static const char kSql[] = + "UPDATE prefetch_items SET state=?,error_code=?" + " WHERE requested_url=?" + " AND state NOT IN (?,?)"; + + sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); + statement.BindInt(0, static_cast<int>(PrefetchItemState::FINISHED)); + statement.BindInt( + 1, static_cast<int>(PrefetchItemErrorCode::SUGGESTION_INVALIDATED)); + statement.BindString(2, url.spec()); + statement.BindInt(3, static_cast<int>(PrefetchItemState::FINISHED)); + statement.BindInt(4, static_cast<int>(PrefetchItemState::ZOMBIE)); + return statement.Run(); +} + +} // namespace + +std::unique_ptr<SqlCallbackTask> MakeRemoveUrlTask(PrefetchStore* store, + const GURL& url) { + return std::make_unique<SqlCallbackTask>(store, + base::BindOnce(&RemoveURL, url)); +} + +} // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/tasks/remove_url_task.h b/components/offline_pages/core/prefetch/tasks/remove_url_task.h new file mode 100644 index 0000000..e5f4c22 --- /dev/null +++ b/components/offline_pages/core/prefetch/tasks/remove_url_task.h
@@ -0,0 +1,26 @@ +// 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 COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TASKS_REMOVE_URL_TASK_H_ +#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TASKS_REMOVE_URL_TASK_H_ + +#include <string> +#include <vector> + +#include "base/memory/weak_ptr.h" +#include "components/offline_pages/task/sql_callback_task.h" +#include "components/offline_pages/task/task.h" +#include "url/gurl.h" + +namespace offline_pages { +class PrefetchStore; + +// Creates a task that removes a URL from the pipeline. Finalizes any item with +// matching URL if it's not already finalized. +std::unique_ptr<SqlCallbackTask> MakeRemoveUrlTask(PrefetchStore* store, + const GURL& url); + +} // namespace offline_pages + +#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TASKS_REMOVE_URL_TASK_H_
diff --git a/components/offline_pages/task/BUILD.gn b/components/offline_pages/task/BUILD.gn index 14e59c2..8f33833 100644 --- a/components/offline_pages/task/BUILD.gn +++ b/components/offline_pages/task/BUILD.gn
@@ -10,6 +10,8 @@ sources = [ "closure_task.cc", "closure_task.h", + "sql_callback_task.cc", + "sql_callback_task.h", "sql_store_base.cc", "sql_store_base.h", "task.cc",
diff --git a/components/offline_pages/task/sql_callback_task.cc b/components/offline_pages/task/sql_callback_task.cc new file mode 100644 index 0000000..ceaa76ed --- /dev/null +++ b/components/offline_pages/task/sql_callback_task.cc
@@ -0,0 +1,34 @@ +// 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 "components/offline_pages/task/sql_callback_task.h" + +#include "components/offline_pages/task/sql_store_base.h" + +namespace offline_pages { + +using ExecuteCallback = SqlCallbackTask::ExecuteCallback; + +SqlCallbackTask::SqlCallbackTask(SqlStoreBase* store, + ExecuteCallback exec_callback, + DoneCallback done_callback) + : store_(store), + exec_callback_(std::move(exec_callback)), + done_callback_(std::move(done_callback)) {} + +SqlCallbackTask::~SqlCallbackTask() = default; + +void SqlCallbackTask::Run() { + // Execute() requires that the callback returns a value, so we use + // ExecuteAndReturn to return a dummy boolean which is ignored. + store_->Execute(std::move(exec_callback_), + base::BindOnce(&SqlCallbackTask::Done, GetWeakPtr()), false); +} + +void SqlCallbackTask::Done(bool result) { + TaskComplete(); + if (done_callback_) + std::move(done_callback_).Run(result); +} + +} // namespace offline_pages
diff --git a/components/offline_pages/task/sql_callback_task.h b/components/offline_pages/task/sql_callback_task.h new file mode 100644 index 0000000..9b54e54d --- /dev/null +++ b/components/offline_pages/task/sql_callback_task.h
@@ -0,0 +1,48 @@ +// 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 COMPONENTS_OFFLINE_PAGES_TASK_SQL_CALLBACK_TASK_H_ +#define COMPONENTS_OFFLINE_PAGES_TASK_SQL_CALLBACK_TASK_H_ + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "components/offline_pages/task/task.h" + +namespace sql { +class Database; +} + +namespace offline_pages { +class SqlStoreBase; + +// A simple task that calls store->Execute() with the provided |exec_callback| +// and completes. |done_callback|, if provided, is called with the result. This +// class can be used if there are no UI thread actions that need done. +class SqlCallbackTask : public Task { + public: + typedef base::OnceCallback<bool(sql::Database* db)> ExecuteCallback; + typedef base::OnceCallback<void(bool)> DoneCallback; + + SqlCallbackTask(SqlStoreBase* store, + ExecuteCallback exec_callback, + DoneCallback done_callback = {}); + ~SqlCallbackTask() override; + void Run() override; + + private: + base::WeakPtr<SqlCallbackTask> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + void Done(bool result); + + SqlStoreBase* store_; + ExecuteCallback exec_callback_; + DoneCallback done_callback_; + base::WeakPtrFactory<SqlCallbackTask> weak_ptr_factory_{this}; +}; + +} // namespace offline_pages + +#endif // COMPONENTS_OFFLINE_PAGES_TASK_SQL_CALLBACK_TASK_H_
diff --git a/components/policy/core/common/cloud/mock_cloud_policy_client.h b/components/policy/core/common/cloud/mock_cloud_policy_client.h index 7de1e5ca..8f1f730 100644 --- a/components/policy/core/common/cloud/mock_cloud_policy_client.h +++ b/components/policy/core/common/cloud/mock_cloud_policy_client.h
@@ -10,6 +10,7 @@ #include <string> #include "base/macros.h" +#include "base/threading/thread_task_runner_handle.h" #include "components/policy/core/common/cloud/cloud_policy_client.h" #include "testing/gmock/include/gmock/gmock.h" @@ -19,6 +20,11 @@ namespace policy { +ACTION_P(ScheduleStatusCallback, status) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::BindOnce(arg0, status)); +} + class MockCloudPolicyClient : public CloudPolicyClient { public: MockCloudPolicyClient(); @@ -64,6 +70,17 @@ const std::string&, const std::string&)); + void UploadChromeDesktopReport( + std::unique_ptr<enterprise_management::ChromeDesktopReportRequest> + request, + const StatusCallback& callback) override { + UploadChromeDesktopReportProxy(request.get(), callback); + } + // Use Proxy function because unique_ptr can't be used in mock function. + MOCK_METHOD2(UploadChromeDesktopReportProxy, + void(enterprise_management::ChromeDesktopReportRequest*, + const StatusCallback&)); + // Sets the DMToken. void SetDMToken(const std::string& token);
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index c79f31bd..17177dfb 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -15523,6 +15523,7 @@ 'type': 'string', }, 'supported_on': ['chrome_os:75-'], + 'device_only': True, 'tags': [], 'features': { 'dynamic_refresh': True,
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp index 2d46158..3893c96 100644 --- a/components/policy_strings.grdp +++ b/components/policy_strings.grdp
@@ -413,8 +413,8 @@ <message name="IDS_POLICY_SOURCE_PLATFORM" desc="Indicates that the policy is obtained from the local OS."> Platform </message> - <message name="IDS_POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE" desc="Indicates that the policy is set programmatically because of an active managed session (for Chrome OS only). It could have overridden other sources that set this policy."> - Managed session override + <message name="IDS_POLICY_SOURCE_DEVICE_LOCAL_ACCOUNT_OVERRIDE" desc="Indicates that the policy is set programmatically because of an active device-local account session: managed session or Kiosk app (both for Chrome OS only). It could have overridden other sources that set this policy."> + Device local account override </message> <message name="IDS_POLICY_RISK_TAG_FULL_ADMIN_ACCESS" desc="Title of a group/tag whose policies potentially allow the administrator to access all of a user's data."> Full Admin Access
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc index 034df22..f29d0de 100644 --- a/components/signin/core/browser/about_signin_internals.cc +++ b/components/signin/core/browser/about_signin_internals.cc
@@ -690,7 +690,7 @@ } else { for (const CoreAccountInfo& account_info : accounts_with_refresh_tokens) { auto entry = std::make_unique<base::DictionaryValue>(); - entry->SetString("accountId", account_info.account_id); + entry->SetString("accountId", account_info.account_id.id); // TODO(https://crbug.com/919793): Remove this field once the token // service is internally consistent on all platforms. entry->SetBoolean("hasRefreshToken",
diff --git a/components/signin/core/browser/account_info.h b/components/signin/core/browser/account_info.h index 884d918..ca47ac7 100644 --- a/components/signin/core/browser/account_info.h +++ b/components/signin/core/browser/account_info.h
@@ -8,6 +8,7 @@ #include <string> #include "components/account_id/account_id.h" +#include "google_apis/gaia/core_account_id.h" #include "ui/gfx/image/image.h" // Value representing no hosted domain associated with an account. @@ -29,7 +30,7 @@ CoreAccountInfo& operator=(const CoreAccountInfo& other); CoreAccountInfo& operator=(CoreAccountInfo&& other) noexcept; - std::string account_id; + CoreAccountId account_id; std::string gaia; std::string email;
diff --git a/components/signin/core/browser/account_tracker_service.cc b/components/signin/core/browser/account_tracker_service.cc index 4821588..8f108992 100644 --- a/components/signin/core/browser/account_tracker_service.cc +++ b/components/signin/core/browser/account_tracker_service.cc
@@ -572,12 +572,12 @@ return; base::DictionaryValue* dict = nullptr; - base::string16 account_id_16 = base::UTF8ToUTF16(account_info.account_id); ListPrefUpdate update(pref_service_, prefs::kAccountInfo); for (size_t i = 0; i < update->GetSize(); ++i, dict = nullptr) { if (update->GetDictionary(i, &dict)) { - base::string16 value; - if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) + std::string value; + if (dict->GetString(kAccountKeyPath, &value) && + value == account_info.account_id.id) break; } } @@ -587,7 +587,7 @@ update->Append(base::WrapUnique(dict)); // |dict| is invalidated at this point, so it needs to be reset. update->GetDictionary(update->GetSize() - 1, &dict); - dict->SetString(kAccountKeyPath, account_id_16); + dict->SetString(kAccountKeyPath, account_info.account_id.id); } dict->SetString(kAccountEmailPath, account_info.email); @@ -607,13 +607,13 @@ if (!pref_service_) return; - base::string16 account_id_16 = base::UTF8ToUTF16(account_info.account_id); ListPrefUpdate update(pref_service_, prefs::kAccountInfo); for (size_t i = 0; i < update->GetSize(); ++i) { base::DictionaryValue* dict = nullptr; if (update->GetDictionary(i, &dict)) { - base::string16 value; - if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) { + std::string value; + if (dict->GetString(kAccountKeyPath, &value) && + value == account_info.account_id.id) { update->Remove(i, nullptr); break; }
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc index 17b49e1c..af26b9b7 100644 --- a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -541,9 +541,9 @@ account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -582,9 +582,9 @@ account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -623,9 +623,9 @@ account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -659,7 +659,7 @@ account_tracker_service_.SeedAccountInfo(primary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle();
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc index 55f3640..4e92ebf 100644 --- a/components/sync/driver/glue/sync_engine_backend.cc +++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -432,7 +432,9 @@ host_.Call( FROM_HERE, &SyncEngineImpl::HandleInitializationSuccessOnFrontendLoop, registrar_->GetLastConfiguredTypes(), js_backend_, debug_info_listener_, - base::Passed(sync_manager_->GetModelTypeConnectorProxy())); + base::Passed(sync_manager_->GetModelTypeConnectorProxy()), + sync_manager_->cache_guid(), sync_manager_->birthday(), + sync_manager_->bag_of_chips()); js_backend_.Reset(); debug_info_listener_.Reset();
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc index 01007a3..5140252 100644 --- a/components/sync/driver/glue/sync_engine_impl.cc +++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -311,7 +311,10 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend> js_backend, const WeakHandle<DataTypeDebugInfoListener> debug_info_listener, - std::unique_ptr<ModelTypeConnector> model_type_connector) { + std::unique_ptr<ModelTypeConnector> model_type_connector, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); model_type_connector_ = std::move(model_type_connector); @@ -328,6 +331,7 @@ } host_->OnEngineInitialized(initial_types, js_backend, debug_info_listener, + cache_guid, birthday, bag_of_chips, /*success=*/true); } @@ -335,6 +339,8 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); host_->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(), WeakHandle<DataTypeDebugInfoListener>(), + /*cache_guid=*/"", + /*birthday=*/"", /*bag_of_chips=*/"", /*success=*/false); }
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h index 6d836036..9fce092e 100644 --- a/components/sync/driver/glue/sync_engine_impl.h +++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -122,7 +122,10 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend> js_backend, const WeakHandle<DataTypeDebugInfoListener> debug_info_listener, - std::unique_ptr<ModelTypeConnector> model_type_connector); + std::unique_ptr<ModelTypeConnector> model_type_connector, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips); // Forwards a ProtocolEvent to the host. Will not be called unless a call to // SetForwardProtocolEvents() explicitly requested that we start forwarding
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc index 21464b35..7c09163 100644 --- a/components/sync/driver/glue/sync_engine_impl_unittest.cc +++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -80,6 +80,9 @@ void OnEngineInitialized(ModelTypeSet initial_types, const WeakHandle<JsBackend>&, const WeakHandle<DataTypeDebugInfoListener>&, + const std::string&, + const std::string&, + const std::string&, bool success) override { EXPECT_EQ(expect_success_, success); set_engine_types_.Run(initial_types);
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc index a500cd6..3f1dbe36 100644 --- a/components/sync/driver/profile_sync_service.cc +++ b/components/sync/driver/profile_sync_service.cc
@@ -7,7 +7,6 @@ #include <cstddef> #include <utility> -#include "base/base64.h" #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" @@ -15,7 +14,6 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "base/rand_util.h" #include "components/invalidation/public/invalidation_service.h" #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/signin_metrics.h" @@ -123,14 +121,6 @@ return type_map; } -std::string GenerateCacheGUID() { - // Generate a GUID with 128 bits of randomness. - const int kGuidBytes = 128 / 8; - std::string guid; - base::Base64Encode(base::RandBytesAsString(kGuidBytes), &guid); - return guid; -} - } // namespace ProfileSyncService::InitParams::InitParams() = default; @@ -503,10 +493,6 @@ params.restored_keystore_key_for_bootstrapping = sync_prefs_.GetKeystoreEncryptionBootstrapToken(); params.cache_guid = sync_prefs_.GetCacheGuid(); - if (params.cache_guid.empty()) { - params.cache_guid = GenerateCacheGUID(); - sync_prefs_.SetCacheGuid(params.cache_guid); - } params.birthday = sync_prefs_.GetBirthday(); params.bag_of_chips = sync_prefs_.GetBagOfChips(); params.engine_components_factory = @@ -836,8 +822,12 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend>& js_backend, const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips, bool success) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!cache_guid.empty()); // TODO(treib): Based on some crash reports, it seems like the user could have // signed out already at this point, so many of the steps below, including @@ -861,6 +851,11 @@ sync_js_controller_.AttachJsBackend(js_backend); + // Save initialization data to preferences. + sync_prefs_.SetCacheGuid(cache_guid); + sync_prefs_.SetBirthday(birthday); + sync_prefs_.SetBagOfChips(bag_of_chips); + if (protocol_event_observers_.might_have_observers()) { engine_->RequestBufferedProtocolEventsAndEnableForwarding(); } @@ -929,7 +924,6 @@ DCHECK(!snapshot.poll_interval().is_zero()); sync_prefs_.SetPollInterval(snapshot.poll_interval()); - sync_prefs_.SetBirthday(snapshot.birthday()); sync_prefs_.SetBagOfChips(snapshot.bag_of_chips()); DVLOG(2) << "Notifying observers sync cycle completed"; @@ -1202,11 +1196,6 @@ return sync_client_.get(); } -// static -std::string ProfileSyncService::GenerateCacheGUIDForTest() { - return GenerateCacheGUID(); -} - void ProfileSyncService::AddObserver(SyncServiceObserver* observer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); observers_.AddObserver(observer);
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h index 90c1eddf5..eb371806 100644 --- a/components/sync/driver/profile_sync_service.h +++ b/components/sync/driver/profile_sync_service.h
@@ -161,6 +161,9 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend>& js_backend, const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips, bool success) override; void OnSyncCycleCompleted(const SyncCycleSnapshot& snapshot) override; void OnProtocolEvent(const ProtocolEvent& event) override; @@ -254,8 +257,6 @@ SyncClient* GetSyncClientForTest(); - static std::string GenerateCacheGUIDForTest(); - private: friend class TestProfileSyncService;
diff --git a/components/sync/driver/profile_sync_service_startup_unittest.cc b/components/sync/driver/profile_sync_service_startup_unittest.cc index c0432e4..a5ca326 100644 --- a/components/sync/driver/profile_sync_service_startup_unittest.cc +++ b/components/sync/driver/profile_sync_service_startup_unittest.cc
@@ -634,6 +634,8 @@ ON_CALL(*sync_engine, IsInitialized()).WillByDefault(Return(true)); sync_service()->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(), WeakHandle<DataTypeDebugInfoListener>(), + "test-guid", "test-birthday", + "test-bag-of-chips", /*success=*/true); ASSERT_TRUE(sync_service()->IsEngineInitialized()); EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION, @@ -712,6 +714,8 @@ ON_CALL(*sync_engine, IsInitialized()).WillByDefault(Return(true)); sync_service()->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(), WeakHandle<DataTypeDebugInfoListener>(), + "test-guid", "test-birthday", + "test-bag-of-chips", /*success=*/true); ON_CALL(*data_type_manager, state()) .WillByDefault(Return(DataTypeManager::CONFIGURING));
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc index 73bb024..05c9c12b 100644 --- a/components/sync/driver/profile_sync_service_unittest.cc +++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -1200,13 +1200,5 @@ ShutdownAndDeleteService(); } -TEST_F(ProfileSyncServiceTest, GenerateCacheGUID) { - const std::string guid1 = ProfileSyncService::GenerateCacheGUIDForTest(); - const std::string guid2 = ProfileSyncService::GenerateCacheGUIDForTest(); - EXPECT_EQ(24U, guid1.size()); - EXPECT_EQ(24U, guid2.size()); - EXPECT_NE(guid1, guid2); -} - } // namespace } // namespace syncer
diff --git a/components/sync/engine/engine_components_factory.h b/components/sync/engine/engine_components_factory.h index 7cd3791..639d78e 100644 --- a/components/sync/engine/engine_components_factory.h +++ b/components/sync/engine/engine_components_factory.h
@@ -91,10 +91,11 @@ base::TimeDelta poll_interval) = 0; virtual std::unique_ptr<syncable::DirectoryBackingStore> - BuildDirectoryBackingStore(StorageOption storage, - const std::string& dir_name, - const std::string& cache_guid, - const base::FilePath& backing_filepath) = 0; + BuildDirectoryBackingStore( + StorageOption storage, + const std::string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator, + const base::FilePath& backing_filepath) = 0; // Returns the Switches struct that this object is using as configuration, if // the implementation is making use of one.
diff --git a/components/sync/engine/engine_components_factory_impl.cc b/components/sync/engine/engine_components_factory_impl.cc index a9ef87f..7deb70d 100644 --- a/components/sync/engine/engine_components_factory_impl.cc +++ b/components/sync/engine/engine_components_factory_impl.cc
@@ -65,12 +65,12 @@ EngineComponentsFactoryImpl::BuildDirectoryBackingStore( StorageOption storage, const std::string& dir_name, - const std::string& cache_guid, + const base::RepeatingCallback<std::string()>& cache_guid_generator, const base::FilePath& backing_filepath) { if (storage == STORAGE_ON_DISK) { return std::unique_ptr<syncable::DirectoryBackingStore>( - new syncable::OnDiskDirectoryBackingStore(dir_name, cache_guid, - backing_filepath)); + new syncable::OnDiskDirectoryBackingStore( + dir_name, cache_guid_generator, backing_filepath)); } else { NOTREACHED(); return std::unique_ptr<syncable::DirectoryBackingStore>();
diff --git a/components/sync/engine/engine_components_factory_impl.h b/components/sync/engine/engine_components_factory_impl.h index 29b98be..7b28c09 100644 --- a/components/sync/engine/engine_components_factory_impl.h +++ b/components/sync/engine/engine_components_factory_impl.h
@@ -42,7 +42,7 @@ std::unique_ptr<syncable::DirectoryBackingStore> BuildDirectoryBackingStore( StorageOption storage, const std::string& dir_name, - const std::string& cache_guid, + const base::RepeatingCallback<std::string()>& cache_guid_generator, const base::FilePath& backing_filepath) override; Switches GetSwitches() const override;
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc index cb09080..fe06319 100644 --- a/components/sync/engine/fake_sync_engine.cc +++ b/components/sync/engine/fake_sync_engine.cc
@@ -9,6 +9,12 @@ #include "components/sync/model/model_type_controller_delegate.h" namespace syncer { +namespace { + +const char kTestCacheGuid[] = "test-guid"; +const char kTestBirthday[] = "1"; + +} // namespace FakeSyncEngine::FakeSyncEngine() {} FakeSyncEngine::~FakeSyncEngine() {} @@ -18,7 +24,8 @@ initialized_ = success; params.host->OnEngineInitialized(ModelTypeSet(), WeakHandle<JsBackend>(), WeakHandle<DataTypeDebugInfoListener>(), - success); + kTestCacheGuid, kTestBirthday, + /*bag_of_chips=*/"", success); } bool FakeSyncEngine::IsInitialized() const {
diff --git a/components/sync/engine/fake_sync_manager.cc b/components/sync/engine/fake_sync_manager.cc index c52a7306..3269ba7 100644 --- a/components/sync/engine/fake_sync_manager.cc +++ b/components/sync/engine/fake_sync_manager.cc
@@ -206,10 +206,20 @@ return std::make_unique<FakeModelTypeConnector>(); } -const std::string FakeSyncManager::cache_guid() { +std::string FakeSyncManager::cache_guid() { return test_user_share_.user_share()->directory->cache_guid(); } +std::string FakeSyncManager::birthday() { + NOTIMPLEMENTED(); + return std::string(); +} + +std::string FakeSyncManager::bag_of_chips() { + NOTIMPLEMENTED(); + return std::string(); +} + bool FakeSyncManager::HasUnsyncedItemsForTest() { NOTIMPLEMENTED(); return false;
diff --git a/components/sync/engine/fake_sync_manager.h b/components/sync/engine/fake_sync_manager.h index 1c1deab..79fa656 100644 --- a/components/sync/engine/fake_sync_manager.h +++ b/components/sync/engine/fake_sync_manager.h
@@ -102,7 +102,9 @@ UserShare* GetUserShare() override; ModelTypeConnector* GetModelTypeConnector() override; std::unique_ptr<ModelTypeConnector> GetModelTypeConnectorProxy() override; - const std::string cache_guid() override; + std::string cache_guid() override; + std::string birthday() override; + std::string bag_of_chips() override; bool HasUnsyncedItemsForTest() override; SyncEncryptionHandler* GetEncryptionHandler() override; base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
diff --git a/components/sync/engine/sync_engine_host.h b/components/sync/engine/sync_engine_host.h index 5da3e1f1..c345abb5 100644 --- a/components/sync/engine/sync_engine_host.h +++ b/components/sync/engine/sync_engine_host.h
@@ -40,6 +40,9 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend>& js_backend, const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips, bool success) = 0; // The engine queried the server recently and received some updates.
diff --git a/components/sync/engine/sync_engine_host_stub.cc b/components/sync/engine/sync_engine_host_stub.cc index 5952c0e3..255e9a77 100644 --- a/components/sync/engine/sync_engine_host_stub.cc +++ b/components/sync/engine/sync_engine_host_stub.cc
@@ -13,6 +13,9 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend>& js_backend, const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips, bool success) {} void SyncEngineHostStub::OnSyncCycleCompleted(
diff --git a/components/sync/engine/sync_engine_host_stub.h b/components/sync/engine/sync_engine_host_stub.h index 3e02d17c..edb7928 100644 --- a/components/sync/engine/sync_engine_host_stub.h +++ b/components/sync/engine/sync_engine_host_stub.h
@@ -21,6 +21,9 @@ ModelTypeSet initial_types, const WeakHandle<JsBackend>& js_backend, const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener, + const std::string& cache_guid, + const std::string& birthday, + const std::string& bag_of_chips, bool success) override; void OnSyncCycleCompleted(const SyncCycleSnapshot& snapshot) override; void OnProtocolEvent(const ProtocolEvent& event) override;
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h index f3548f4..514104e 100644 --- a/components/sync/engine/sync_manager.h +++ b/components/sync/engine/sync_manager.h
@@ -352,7 +352,15 @@ // Returns the cache_guid of the currently open database. // Requires that the SyncManager be initialized. - virtual const std::string cache_guid() = 0; + virtual std::string cache_guid() = 0; + + // Returns the birthday of the currently open database. + // Requires that the SyncManager be initialized. + virtual std::string birthday() = 0; + + // Returns the bag of chips of the currently open database. + // Requires that the SyncManager be initialized. + virtual std::string bag_of_chips() = 0; // Returns whether there are remaining unsynced items. virtual bool HasUnsyncedItemsForTest() = 0;
diff --git a/components/sync/engine/test_engine_components_factory.cc b/components/sync/engine/test_engine_components_factory.cc index 6796e2fc..c57fdbd0 100644 --- a/components/sync/engine/test_engine_components_factory.cc +++ b/components/sync/engine/test_engine_components_factory.cc
@@ -54,7 +54,7 @@ TestEngineComponentsFactory::BuildDirectoryBackingStore( StorageOption storage, const std::string& dir_name, - const std::string& cache_guid, + const base::RepeatingCallback<std::string()>& cache_guid_generator, const base::FilePath& backing_filepath) { if (storage_used_) *storage_used_ = storage; @@ -62,11 +62,12 @@ switch (storage_override_) { case STORAGE_IN_MEMORY: return std::unique_ptr<syncable::DirectoryBackingStore>( - new syncable::InMemoryDirectoryBackingStore(dir_name, cache_guid)); + new syncable::InMemoryDirectoryBackingStore(dir_name, + cache_guid_generator)); case STORAGE_ON_DISK: return std::unique_ptr<syncable::DirectoryBackingStore>( - new syncable::OnDiskDirectoryBackingStore(dir_name, cache_guid, - backing_filepath)); + new syncable::OnDiskDirectoryBackingStore( + dir_name, cache_guid_generator, backing_filepath)); case STORAGE_INVALID: return std::unique_ptr<syncable::DirectoryBackingStore>( new syncable::InvalidDirectoryBackingStore());
diff --git a/components/sync/engine/test_engine_components_factory.h b/components/sync/engine/test_engine_components_factory.h index ed3d373..b6164a0 100644 --- a/components/sync/engine/test_engine_components_factory.h +++ b/components/sync/engine/test_engine_components_factory.h
@@ -42,7 +42,7 @@ std::unique_ptr<syncable::DirectoryBackingStore> BuildDirectoryBackingStore( StorageOption storage, const std::string& dir_name, - const std::string& cache_guid, + const base::RepeatingCallback<std::string()>& cache_guid_generator, const base::FilePath& backing_filepath) override; Switches GetSwitches() const override;
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.cc b/components/sync/engine_impl/loopback_server/loopback_server.cc index 91b7933..e3d225f 100644 --- a/components/sync/engine_impl/loopback_server/loopback_server.cc +++ b/components/sync/engine_impl/loopback_server/loopback_server.cc
@@ -344,9 +344,9 @@ std::vector<ModelType> datatypes_to_migrate; switch (message.message_contents()) { case sync_pb::ClientToServerMessage::GET_UPDATES: - success = HandleGetUpdatesRequest(message.get_updates(), - response_proto.mutable_get_updates(), - &datatypes_to_migrate); + success = HandleGetUpdatesRequest( + message.get_updates(), message.store_birthday(), + response_proto.mutable_get_updates(), &datatypes_to_migrate); break; case sync_pb::ClientToServerMessage::COMMIT: success = HandleCommitRequest(message.commit(), @@ -394,10 +394,25 @@ bool LoopbackServer::HandleGetUpdatesRequest( const sync_pb::GetUpdatesMessage& get_updates, + const std::string& store_birthday, sync_pb::GetUpdatesResponse* response, std::vector<ModelType>* datatypes_to_migrate) { response->set_changes_remaining(0); + // It's a protocol-level contract that the birthday should only be empty + // during the initial sync cycle, which requires all progress markers to be + // empty. This is also DCHECK-ed on the client, inside syncer_proto_util.cc, + // but we guard against client-side code changes here. + if (store_birthday.empty()) { + for (const sync_pb::DataTypeProgressMarker& marker : + get_updates.from_progress_marker()) { + if (!marker.token().empty()) { + DLOG(WARNING) << "Non-empty progress marker without birthday"; + return false; + } + } + } + auto sieve = std::make_unique<UpdateSieve>(get_updates, migration_versions_); if (sieve->ShouldTriggerMigration(migration_versions_,
diff --git a/components/sync/engine_impl/loopback_server/loopback_server.h b/components/sync/engine_impl/loopback_server/loopback_server.h index 36ead60..a6129de68f 100644 --- a/components/sync/engine_impl/loopback_server/loopback_server.h +++ b/components/sync/engine_impl/loopback_server/loopback_server.h
@@ -89,6 +89,7 @@ // Processes a GetUpdates call. bool HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage& get_updates, + const std::string& store_birthday, sync_pb::GetUpdatesResponse* response, std::vector<ModelType>* datatypes_to_migrate);
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc index 01be457..e53d161 100644 --- a/components/sync/engine_impl/sync_manager_impl.cc +++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -8,12 +8,14 @@ #include <utility> +#include "base/base64.h" #include "base/bind.h" #include "base/callback.h" #include "base/compiler_specific.h" #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/observer_list.h" +#include "base/rand_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/values.h" #include "components/sync/base/cancelation_signal.h" @@ -74,6 +76,14 @@ return sync_pb::SyncEnums::UNKNOWN_ORIGIN; } +std::string GenerateCacheGUID() { + // Generate a GUID with 128 bits of randomness. + const int kGuidBytes = 128 / 8; + std::string guid; + base::Base64Encode(base::RandBytesAsString(kGuidBytes), &guid); + return guid; +} + // Relevant for UMA, do not change. enum class StringConsistency { kBothEqual = 0, @@ -113,10 +123,8 @@ const SyncManager::InitArgs* args) { DCHECK(directory); - const std::string directory_cache_guid = - directory->legacy_cache_guid_for_uma(); - const std::string directory_birthday = - directory->legacy_store_birthday_for_uma(); + const std::string directory_cache_guid = directory->legacy_cache_guid(); + const std::string directory_birthday = directory->legacy_store_birthday(); const StringConsistency cache_guid_consistency = CompareStringsForConsistency(args->cache_guid, directory_cache_guid); @@ -256,7 +264,6 @@ void SyncManagerImpl::Init(InitArgs* args) { DCHECK(!initialized_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!args->cache_guid.empty()); DCHECK(args->post_factory); DCHECK(!args->poll_interval.is_zero()); if (!args->enable_local_sync_backend) { @@ -265,6 +272,13 @@ DCHECK(args->cancelation_signal); DVLOG(1) << "SyncManager starting Init..."; + // In rare cases, the input cache_guid/birthday pair could be corrupt, + // because in normal cases both are empty or neither. + if (args->cache_guid.empty() != args->birthday.empty()) { + args->cache_guid.clear(); + args->birthday.clear(); + } + weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); change_delegate_ = args->change_delegate; @@ -313,10 +327,27 @@ base::FilePath absolute_db_path = database_path_; DCHECK(absolute_db_path.IsAbsolute()); + // If the directory is newly created, and if prefs already contain a cache + // GUID, we avoid creating a random GUID for the directory (which would be + // inconsistent with prefs). This is relevant mostly for the case where the + // new logic is reverted (and the legacy cache GUID in the directory becomes + // the authoritative one. + base::RepeatingCallback<std::string()> cache_guid_generator = + base::BindRepeating( + [](const std::string& args_cache_guid) { + if (args_cache_guid.empty()) { + return GenerateCacheGUID(); + } else { + return args_cache_guid; + } + }, + args->cache_guid); + std::unique_ptr<syncable::DirectoryBackingStore> backing_store = args->engine_components_factory->BuildDirectoryBackingStore( EngineComponentsFactory::STORAGE_ON_DISK, - args->authenticated_account_id, args->cache_guid, absolute_db_path); + args->authenticated_account_id, cache_guid_generator, + absolute_db_path); DCHECK(backing_store); @@ -470,7 +501,12 @@ return scheduler_.get(); } -bool SyncManagerImpl::OpenDirectory(const InitArgs* args) { +// static +std::string SyncManagerImpl::GenerateCacheGUIDForTest() { + return GenerateCacheGUID(); +} + +bool SyncManagerImpl::OpenDirectory(InitArgs* args) { DCHECK(!initialized_) << "Should only happen once"; // Set before Open(). @@ -488,6 +524,26 @@ return false; } + if (!args->cache_guid.empty()) { + // Regular case for sync-enabled: prefs know about a cache GUID and + // birthday. The values in Directory may or may not be consistent. If the + // directory was OPENED_NEW, the cache GUID is guaranteed to be consistent, + // because of how the cache GUID generator is plumbed. + DCHECK(!args->birthday.empty()); + } else { + // Prefs are empty: either they are legitimately empty (i.e. sync is + // disabled) or migration hasn't happened yet. In both cases, we read + // them from directory, which contains a random cache GUID for the first + // case, or triggers migration. + args->cache_guid = directory()->legacy_cache_guid(); + args->birthday = directory()->legacy_store_birthday(); + } + + // Set the in-memory "authoritative" cache GUID exposed by Directory, although + // it doesn't get persisted. + DCHECK(!args->cache_guid.empty()); + directory()->set_cache_guid(args->cache_guid); + RecordConsistencyBetweenDirectoryAndPrefs(directory(), args); // Unapplied datatypes (those that do not have initial sync ended set) get @@ -958,11 +1014,23 @@ model_type_registry_->AsWeakPtr()); } -const std::string SyncManagerImpl::cache_guid() { +std::string SyncManagerImpl::cache_guid() { DCHECK(initialized_); return directory()->cache_guid(); } +std::string SyncManagerImpl::birthday() { + DCHECK(initialized_); + DCHECK(cycle_context_); + return cycle_context_->birthday(); +} + +std::string SyncManagerImpl::bag_of_chips() { + DCHECK(initialized_); + DCHECK(cycle_context_); + return cycle_context_->bag_of_chips(); +} + bool SyncManagerImpl::HasUnsyncedItemsForTest() { return model_type_registry_->HasUnsyncedItems(); }
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h index 1c12aa2d..0c88aacc 100644 --- a/components/sync/engine_impl/sync_manager_impl.h +++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -94,7 +94,9 @@ UserShare* GetUserShare() override; ModelTypeConnector* GetModelTypeConnector() override; std::unique_ptr<ModelTypeConnector> GetModelTypeConnectorProxy() override; - const std::string cache_guid() override; + std::string cache_guid() override; + std::string birthday() override; + std::string bag_of_chips() override; bool HasUnsyncedItemsForTest() override; SyncEncryptionHandler* GetEncryptionHandler() override; base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate() @@ -174,6 +176,8 @@ const SyncScheduler* scheduler() const; + static std::string GenerateCacheGUIDForTest(); + protected: // Helper functions. Virtual for testing. virtual void NotifyInitializationSuccess(); @@ -215,7 +219,7 @@ Cryptographer* cryptographer) const; // Opens the directory. - bool OpenDirectory(const InitArgs* args); + bool OpenDirectory(InitArgs* args); void RequestNudgeForDataTypes(const base::Location& nudge_location, ModelTypeSet type);
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc index f811528..347c2c33 100644 --- a/components/sync/engine_impl/sync_manager_impl_unittest.cc +++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -2821,6 +2821,14 @@ } } +TEST(SyncManagerImplTest, GenerateCacheGUID) { + const std::string guid1 = SyncManagerImpl::GenerateCacheGUIDForTest(); + const std::string guid2 = SyncManagerImpl::GenerateCacheGUIDForTest(); + EXPECT_EQ(24U, guid1.size()); + EXPECT_EQ(24U, guid2.size()); + EXPECT_NE(guid1, guid2); +} + // A test harness to exercise the code that processes and passes changes from // the "SYNCER"-WriteTransaction destructor, through the SyncManager, to the // ChangeProcessor.
diff --git a/components/sync/syncable/directory.cc b/components/sync/syncable/directory.cc index fafe289..5fba705 100644 --- a/components/sync/syncable/directory.cc +++ b/components/sync/syncable/directory.cc
@@ -1035,7 +1035,7 @@ } } -std::string Directory::legacy_store_birthday_for_uma() const { +std::string Directory::legacy_store_birthday() const { ScopedKernelLock lock(this); return kernel_->persisted_info.legacy_store_birthday; } @@ -1057,10 +1057,16 @@ } const string& Directory::cache_guid() const { - return store_->cache_guid(); + DCHECK(!cache_guid_.empty()) << this; + return cache_guid_; } -string Directory::legacy_cache_guid_for_uma() const { +void Directory::set_cache_guid(const std::string& cache_guid) { + DCHECK(!cache_guid.empty()); + cache_guid_ = cache_guid; +} + +string Directory::legacy_cache_guid() const { // No need to lock since nothing ever writes to it after load. return kernel_->legacy_cache_guid; }
diff --git a/components/sync/syncable/directory.h b/components/sync/syncable/directory.h index 681f530..25dbe040 100644 --- a/components/sync/syncable/directory.h +++ b/components/sync/syncable/directory.h
@@ -309,23 +309,25 @@ // This applies only to types with implicitly created root folders. void MarkInitialSyncEndedForType(BaseWriteTransaction* trans, ModelType type); - // (Account) Store birthday is opaque to the client, so we keep it in the - // format it is in the proto buffer in case we switch to a binary birthday - // later. - std::string legacy_store_birthday_for_uma() const; + // Legacy store birthday, exposed for UMA purposes and migration from + // directory to prefs. + std::string legacy_store_birthday() const; void set_legacy_store_birthday(const std::string& store_birthday); // (Account) Bag of chip is an opaque state used by the server to track the // client. void set_legacy_bag_of_chips(const std::string& bag_of_chips); - // Authoritative cache GUID: unique to each account / client pair. Owned - // by DirectoryBackingStore but exposed here for convenience. + // Authoritative cache GUID: unique to each account / client pair. + // TODO(crbug.com/923285): Move authoritative cache GUID elsewhere, since its + // lifetime here is now complex and can be easily confused with the legacy + // cache GUID. const std::string& cache_guid() const; + void set_cache_guid(const std::string& cache_guid); // Legacy cache GUID (non-authoritative) as historically persisted on disk, - // exposed for UMA purposes only. - std::string legacy_cache_guid_for_uma() const; + // exposed for UMA purposes and migration from directory to prefs. + std::string legacy_cache_guid() const; // Returns a pointer to our Nigori node handler. NigoriHandler* GetNigoriHandler(); @@ -611,6 +613,8 @@ // error on it. bool unrecoverable_error_set(const BaseTransaction* trans) const; + std::string cache_guid_; + std::unique_ptr<Kernel> kernel_; std::unique_ptr<DirectoryBackingStore> store_;
diff --git a/components/sync/syncable/directory_backing_store.cc b/components/sync/syncable/directory_backing_store.cc index 179e6d3f..bfa5e5e 100644 --- a/components/sync/syncable/directory_backing_store.cc +++ b/components/sync/syncable/directory_backing_store.cc
@@ -275,10 +275,11 @@ /////////////////////////////////////////////////////////////////////////////// // DirectoryBackingStore implementation. -DirectoryBackingStore::DirectoryBackingStore(const string& dir_name, - const std::string& cache_guid) +DirectoryBackingStore::DirectoryBackingStore( + const string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator) : dir_name_(dir_name), - cache_guid_(cache_guid), + cache_guid_generator_(cache_guid_generator), database_page_size_(kCurrentPageSizeKB), needs_metas_column_refresh_(false), needs_share_info_column_refresh_(false) { @@ -286,9 +287,12 @@ ResetAndCreateConnection(); } -DirectoryBackingStore::DirectoryBackingStore(const string& dir_name, - sql::Database* db) +DirectoryBackingStore::DirectoryBackingStore( + const string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator, + sql::Database* db) : dir_name_(dir_name), + cache_guid_generator_(cache_guid_generator), database_page_size_(kCurrentPageSizeKB), db_(db), needs_metas_column_refresh_(false), @@ -1558,7 +1562,7 @@ s.BindString(0, dir_name_); // id s.BindString(1, dir_name_); // name s.BindString(2, std::string()); // store_birthday - s.BindString(3, cache_guid_); + s.BindString(3, cache_guid_generator_.Run()); // cache_guid s.BindBlob(4, nullptr, 0); // bag_of_chips if (!s.Run()) return false;
diff --git a/components/sync/syncable/directory_backing_store.h b/components/sync/syncable/directory_backing_store.h index 251f295e..a681447 100644 --- a/components/sync/syncable/directory_backing_store.h +++ b/components/sync/syncable/directory_backing_store.h
@@ -53,8 +53,9 @@ // OnDiskDirectoryBackingStore. class DirectoryBackingStore { public: - DirectoryBackingStore(const std::string& dir_name, - const std::string& cache_guid); + DirectoryBackingStore( + const std::string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator); virtual ~DirectoryBackingStore(); // Loads and drops all currently persisted meta entries into |handles_map| @@ -108,11 +109,12 @@ bool ReportMemoryUsage(base::trace_event::ProcessMemoryDump* pmd, const std::string& dump_name); - const std::string& cache_guid() const { return cache_guid_; } - protected: // For test classes. - DirectoryBackingStore(const std::string& dir_name, sql::Database* connection); + DirectoryBackingStore( + const std::string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator, + sql::Database* connection); // An accessor for the underlying sql::Database. Avoid using outside of // tests. @@ -260,7 +262,7 @@ sql::Statement* save_statement); const std::string dir_name_; - const std::string cache_guid_; + const base::RepeatingCallback<std::string()> cache_guid_generator_; const int database_page_size_; std::unique_ptr<sql::Database> db_;
diff --git a/components/sync/syncable/directory_backing_store_unittest.cc b/components/sync/syncable/directory_backing_store_unittest.cc index 94412c02..f5a3b220 100644 --- a/components/sync/syncable/directory_backing_store_unittest.cc +++ b/components/sync/syncable/directory_backing_store_unittest.cc
@@ -33,6 +33,10 @@ const char kTestCacheGuid[] = "test_cache_guid"; +base::RepeatingCallback<std::string()> TestCacheGuidGenerator() { + return base::BindRepeating([]() -> std::string { return kTestCacheGuid; }); +} + // A handler that simply sets |catastrophic_error_handler_was_called| to true. void CatastrophicErrorHandler(bool* catastrophic_error_handler_was_called) { *catastrophic_error_handler_was_called = true; @@ -3961,7 +3965,7 @@ MetahandleSet metahandles_to_purge; { - OnDiskDirectoryBackingStore dbs(GetUsername(), kTestCacheGuid, + OnDiskDirectoryBackingStore dbs(GetUsername(), TestCacheGuidGenerator(), GetDatabasePath()); ASSERT_EQ(OPENED_EXISTING, dbs.Load(&handles_map, &delete_journals, &metahandles_to_purge, &dir_info)); @@ -4262,7 +4266,9 @@ OnDiskDirectoryBackingStoreForTest::OnDiskDirectoryBackingStoreForTest( const std::string& dir_name, const base::FilePath& backing_filepath) - : OnDiskDirectoryBackingStore(dir_name, kTestCacheGuid, backing_filepath), + : OnDiskDirectoryBackingStore(dir_name, + TestCacheGuidGenerator(), + backing_filepath), first_open_failed_(false) {} OnDiskDirectoryBackingStoreForTest::~OnDiskDirectoryBackingStoreForTest() { } @@ -4286,7 +4292,7 @@ // due to read-only file system), is not tested here. TEST_F(DirectoryBackingStoreTest, MinorCorruption) { { - OnDiskDirectoryBackingStore dbs(GetUsername(), kTestCacheGuid, + OnDiskDirectoryBackingStore dbs(GetUsername(), TestCacheGuidGenerator(), GetDatabasePath()); EXPECT_TRUE(LoadAndIgnoreReturnedData(&dbs)); } @@ -4309,7 +4315,7 @@ TEST_F(DirectoryBackingStoreTest, MinorCorruptionAndUpgrade) { { - OnDiskDirectoryBackingStore dbs(GetUsername(), kTestCacheGuid, + OnDiskDirectoryBackingStore dbs(GetUsername(), TestCacheGuidGenerator(), GetDatabasePath()); EXPECT_TRUE(LoadAndIgnoreReturnedData(&dbs)); }
diff --git a/components/sync/syncable/directory_unittest.cc b/components/sync/syncable/directory_unittest.cc index fe78fff..747cfb2f 100644 --- a/components/sync/syncable/directory_unittest.cc +++ b/components/sync/syncable/directory_unittest.cc
@@ -85,6 +85,8 @@ if (open_result != OPENED_NEW && open_result != OPENED_EXISTING) { dir_.reset(); + } else { + dir_->set_cache_guid(dir_->legacy_cache_guid()); } return open_result; @@ -1825,10 +1827,13 @@ // DirectoryBackingStore error is detected. TEST_F(SyncableDirectoryTest, CatastrophicError) { MockUnrecoverableErrorHandler unrecoverable_error_handler; - Directory dir(std::make_unique<InMemoryDirectoryBackingStore>( - "catastrophic_error", "test_cache_guid"), - MakeWeakHandle(unrecoverable_error_handler.GetWeakPtr()), - base::Closure(), nullptr, nullptr); + Directory dir( + std::make_unique<InMemoryDirectoryBackingStore>( + "catastrophic_error", base::BindRepeating([]() -> std::string { + return "test_cache_guid"; + })), + MakeWeakHandle(unrecoverable_error_handler.GetWeakPtr()), + base::RepeatingClosure(), nullptr, nullptr); ASSERT_EQ(OPENED_NEW, dir.Open(kDirectoryName, directory_change_delegate(), NullTransactionObserver())); ASSERT_EQ(0, unrecoverable_error_handler.invocation_count());
diff --git a/components/sync/syncable/in_memory_directory_backing_store.cc b/components/sync/syncable/in_memory_directory_backing_store.cc index abdad5c5..e3f7840 100644 --- a/components/sync/syncable/in_memory_directory_backing_store.cc +++ b/components/sync/syncable/in_memory_directory_backing_store.cc
@@ -9,8 +9,8 @@ InMemoryDirectoryBackingStore::InMemoryDirectoryBackingStore( const std::string& dir_name, - const std::string& cache_guid) - : DirectoryBackingStore(dir_name, cache_guid) {} + const base::RepeatingCallback<std::string()>& cache_guid_generator) + : DirectoryBackingStore(dir_name, cache_guid_generator) {} DirOpenResult InMemoryDirectoryBackingStore::Load( Directory::MetahandlesMap* handles_map,
diff --git a/components/sync/syncable/in_memory_directory_backing_store.h b/components/sync/syncable/in_memory_directory_backing_store.h index 20989de..9fe7bf69 100644 --- a/components/sync/syncable/in_memory_directory_backing_store.h +++ b/components/sync/syncable/in_memory_directory_backing_store.h
@@ -24,8 +24,9 @@ // TestDirectoryBackingStore. class InMemoryDirectoryBackingStore : public DirectoryBackingStore { public: - InMemoryDirectoryBackingStore(const std::string& dir_name, - const std::string& cache_guid); + explicit InMemoryDirectoryBackingStore( + const std::string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator); DirOpenResult Load(Directory::MetahandlesMap* handles_map, JournalIndex* delete_journals, MetahandleSet* metahandles_to_purge,
diff --git a/components/sync/syncable/invalid_directory_backing_store.cc b/components/sync/syncable/invalid_directory_backing_store.cc index 60f98ab0..a67b397 100644 --- a/components/sync/syncable/invalid_directory_backing_store.cc +++ b/components/sync/syncable/invalid_directory_backing_store.cc
@@ -4,11 +4,18 @@ #include "components/sync/syncable/invalid_directory_backing_store.h" +#include <string> + +#include "base/bind.h" + namespace syncer { namespace syncable { InvalidDirectoryBackingStore::InvalidDirectoryBackingStore() - : DirectoryBackingStore("some_fake_user", "some_fake_cache_guid") {} + : DirectoryBackingStore("some_fake_user", + base::BindRepeating([]() -> std::string { + return "some_fake_cache_guid"; + })) {} InvalidDirectoryBackingStore::~InvalidDirectoryBackingStore() {}
diff --git a/components/sync/syncable/on_disk_directory_backing_store.cc b/components/sync/syncable/on_disk_directory_backing_store.cc index ff452df..f31e98c 100644 --- a/components/sync/syncable/on_disk_directory_backing_store.cc +++ b/components/sync/syncable/on_disk_directory_backing_store.cc
@@ -23,9 +23,9 @@ OnDiskDirectoryBackingStore::OnDiskDirectoryBackingStore( const std::string& dir_name, - const std::string& cache_guid, + const base::RepeatingCallback<std::string()>& cache_guid_generator, const base::FilePath& backing_file_path) - : DirectoryBackingStore(dir_name, cache_guid), + : DirectoryBackingStore(dir_name, cache_guid_generator), backing_file_path_(backing_file_path) { DCHECK(backing_file_path_.IsAbsolute()); }
diff --git a/components/sync/syncable/on_disk_directory_backing_store.h b/components/sync/syncable/on_disk_directory_backing_store.h index 02d3657d..cadfe92c 100644 --- a/components/sync/syncable/on_disk_directory_backing_store.h +++ b/components/sync/syncable/on_disk_directory_backing_store.h
@@ -18,9 +18,10 @@ // DirectoryBackingStore. class OnDiskDirectoryBackingStore : public DirectoryBackingStore { public: - OnDiskDirectoryBackingStore(const std::string& dir_name, - const std::string& cache_guid, - const base::FilePath& backing_file_path); + OnDiskDirectoryBackingStore( + const std::string& dir_name, + const base::RepeatingCallback<std::string()>& cache_guid_generator, + const base::FilePath& backing_file_path); ~OnDiskDirectoryBackingStore() override; DirOpenResult Load(Directory::MetahandlesMap* handles_map, JournalIndex* delete_journals,
diff --git a/components/sync/syncable/syncable_unittest.cc b/components/sync/syncable/syncable_unittest.cc index b2471bb..55c0da9 100644 --- a/components/sync/syncable/syncable_unittest.cc +++ b/components/sync/syncable/syncable_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include "base/bind.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -66,7 +67,9 @@ TestBackingStore::TestBackingStore(const std::string& dir_name, const base::FilePath& backing_filepath) : OnDiskDirectoryBackingStore(dir_name, - "test_cache_guid", + base::BindRepeating([]() -> std::string { + return "test_cache_guid"; + }), backing_filepath), fail_save_changes_(false) {} @@ -182,8 +185,10 @@ test_directory_ = test_directory.get(); dir() = std::move(test_directory); DCHECK(dir()); - return dir()->Open(kDirectoryName, directory_change_delegate(), - NullTransactionObserver()); + DirOpenResult result = dir()->Open( + kDirectoryName, directory_change_delegate(), NullTransactionObserver()); + dir()->set_cache_guid(dir()->legacy_cache_guid()); + return result; } void SaveAndReloadDir() { @@ -318,7 +323,10 @@ dir()->SaveChanges(); dir() = std::make_unique<Directory>( std::make_unique<OnDiskDirectoryBackingStore>( - kDirectoryName, "test_cache_guid", file_path_), + kDirectoryName, base::BindRepeating([]() -> std::string { + return "test_cache_guid"; + }), + file_path_), MakeWeakHandle(unrecoverable_error_handler()->GetWeakPtr()), base::Closure(), nullptr, nullptr); @@ -531,7 +539,10 @@ { Directory dir(std::make_unique<OnDiskDirectoryBackingStore>( - "ScopeTest", "test_cache_guid", path), + "ScopeTest", base::BindRepeating([]() -> std::string { + return "test_cache_guid"; + }), + path), MakeWeakHandle(handler_.GetWeakPtr()), base::Closure(), nullptr, nullptr); DirOpenResult result =
diff --git a/components/sync/test/engine/test_directory_setter_upper.cc b/components/sync/test/engine/test_directory_setter_upper.cc index cd65d51..a193683 100644 --- a/components/sync/test/engine/test_directory_setter_upper.cc +++ b/components/sync/test/engine/test_directory_setter_upper.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/bind.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/strings/string_util.h" @@ -31,11 +32,13 @@ directory_ = std::make_unique<syncable::Directory>( std::make_unique<syncable::InMemoryDirectoryBackingStore>( - name_, "kTestCacheGuid"), + name_, base::BindRepeating( + []() -> std::string { return "kTestCacheGuid"; })), MakeWeakHandle(handler_.GetWeakPtr()), base::Closure(), &encryption_handler_, encryption_handler_.cryptographer()); ASSERT_EQ(syncable::OPENED_NEW, directory_->Open(name_, &delegate_, transaction_observer)); + directory_->set_cache_guid("kTestCacheGuid"); } void TestDirectorySetterUpper::SetUpWith( @@ -52,6 +55,7 @@ encryption_handler_.cryptographer()); ASSERT_EQ(syncable::OPENED_EXISTING, directory_->Open(name_, &delegate_, transaction_observer)); + directory_->set_cache_guid("kTestCacheGuid"); } void TestDirectorySetterUpper::TearDown() {
diff --git a/components/sync/test/test_directory_backing_store.cc b/components/sync/test/test_directory_backing_store.cc index d075c7c..9778c70a 100644 --- a/components/sync/test/test_directory_backing_store.cc +++ b/components/sync/test/test_directory_backing_store.cc
@@ -12,7 +12,11 @@ TestDirectoryBackingStore::TestDirectoryBackingStore( const std::string& dir_name, sql::Database* db) - : DirectoryBackingStore(dir_name, db) {} + : DirectoryBackingStore(dir_name, + base::BindRepeating([]() -> std::string { + return "test_cache_guid"; + }), + db) {} TestDirectoryBackingStore::~TestDirectoryBackingStore() { // This variant of the DirectoryBackingStore does not own its connection, so
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc index ad01575..55c5f6e 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc
@@ -774,9 +774,8 @@ } // |backdrop_filter_bounds| is a rounded rect in [-0.5,0.5] space that // represents |params->backdrop_filter_bounds| as a fraction of the space - // defined by |quad->rect|, not including its offset. - if (!GetScaledRRectF(gfx::Rect(quad->rect.size()), - params->backdrop_filter_bounds, + // defined by |quad->rect|. + if (!GetScaledRRectF(quad->rect, params->backdrop_filter_bounds, backdrop_filter_bounds)) { *backdrop_filter_bounds = gfx::RRectF(SharedGeometryQuad().BoundingBox()); } @@ -828,9 +827,12 @@ // and it is included in |contents_device_transform| (through // |projection_matrix|). Don't double-flip. *backdrop_filter_bounds_transform = params->contents_device_transform; - float old_y = backdrop_filter_bounds_transform->To2dTranslation().y(); + float new_y = 2 * backdrop_filter_bounds_transform->To2dTranslation().y() + + backdrop_rect.bottom() - unclipped_rect->bottom() + + backdrop_rect.y() - unclipped_rect->y(); backdrop_filter_bounds_transform->PostScale(1, -1); - backdrop_filter_bounds_transform->PostTranslate(0, 2 * old_y); + backdrop_filter_bounds_transform->PostTranslate(0, new_y); + // Shift to the space of the captured backdrop image. backdrop_filter_bounds_transform->PostTranslate(-backdrop_rect.x(), -backdrop_rect.y());
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc index 47cdbc8..007b064 100644 --- a/components/viz/service/display/renderer_pixeltest.cc +++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -2804,7 +2804,7 @@ } template <typename RendererType> -class RendererPixelTestWithBackgroundFilter +class RendererPixelTestWithBackdropFilter : public RendererPixelTest<RendererType> { protected: void SetUpRenderPassList() { @@ -2911,23 +2911,18 @@ gfx::Rect filter_pass_layer_rect_; }; -// The software renderer does not support background filters yet. -using BackgroundFilterRendererTypes = - ::testing::Types<GLRenderer, SkiaRenderer>; +// TODO(916318): The software renderer does not support background filters yet. +using BackdropFilterRendererTypes = ::testing::Types<GLRenderer, SkiaRenderer>; -TYPED_TEST_SUITE(RendererPixelTestWithBackgroundFilter, - BackgroundFilterRendererTypes); +TYPED_TEST_SUITE(RendererPixelTestWithBackdropFilter, + BackdropFilterRendererTypes); -TYPED_TEST(RendererPixelTestWithBackgroundFilter, InvertFilter) { +TYPED_TEST(RendererPixelTestWithBackdropFilter, InvertFilter) { this->backdrop_filters_.Append(cc::FilterOperation::CreateInvertFilter(1.f)); this->filter_pass_layer_rect_ = gfx::Rect(this->device_viewport_size_); this->filter_pass_layer_rect_.Inset(12, 14, 16, 18); - // The backdrop_filter_bounds will apply within the layer's coordinate space, - // so the clipping bounds should be 0,0 WxH, not - // this->filter_pass_layer_rect_. this->backdrop_filter_bounds_ = - gfx::RRectF(0, 0, this->filter_pass_layer_rect_.width(), - this->filter_pass_layer_rect_.height(), 0); + gfx::RRectF(gfx::RectF(this->filter_pass_layer_rect_)); this->SetUpRenderPassList(); EXPECT_TRUE(this->RunPixelTest( &this->pass_list_, @@ -4488,7 +4483,7 @@ TYPED_TEST(RendererPixelTest, RoundedCornerMultiRadii) { gfx::Rect viewport_rect(this->device_viewport_size_); - constexpr std::array<uint32_t, 4> kCornerRadii = {5, 15, 25, 35}; + constexpr gfx::RoundedCornersF kCornerRadii(5, 15, 25, 35); constexpr int kInset = 20; int root_pass_id = 1; @@ -4497,10 +4492,7 @@ gfx::Rect pass_rect(this->device_viewport_size_); pass_rect.Inset(kInset, kInset); - gfx::RRectF rounded_corner_bounds( - gfx::RectF(pass_rect), kCornerRadii[0], kCornerRadii[0], kCornerRadii[1], - kCornerRadii[1], kCornerRadii[2], kCornerRadii[2], kCornerRadii[3], - kCornerRadii[3]); + gfx::RRectF rounded_corner_bounds(gfx::RectF(pass_rect), kCornerRadii); gfx::Rect blue_rect = pass_rect; blue_rect.set_height(blue_rect.height() / 2); @@ -4547,7 +4539,10 @@ TYPED_TEST(RendererPixelTest, RoundedCornerMultipleQads) { const gfx::Rect viewport_rect(this->device_viewport_size_); - constexpr std::array<uint32_t, 4> kCornerRadii = {5, 15, 25, 35}; + constexpr gfx::RoundedCornersF kCornerRadiiUL(5, 0, 0, 0); + constexpr gfx::RoundedCornersF kCornerRadiiUR(0, 15, 0, 0); + constexpr gfx::RoundedCornersF kCornerRadiiLR(0, 0, 25, 0); + constexpr gfx::RoundedCornersF kCornerRadiiLL(0, 0, 0, 35); constexpr int kInset = 20; int root_pass_id = 1; @@ -4556,15 +4551,10 @@ gfx::Rect pass_rect(this->device_viewport_size_); pass_rect.Inset(kInset, kInset); - gfx::RRectF rounded_corner_bounds_ul(gfx::RectF(pass_rect), kCornerRadii[0], - kCornerRadii[0], 0, 0, 0, 0, 0, 0); - gfx::RRectF rounded_corner_bounds_ur(gfx::RectF(pass_rect), 0, 0, - kCornerRadii[1], kCornerRadii[1], 0, 0, - 0, 0); - gfx::RRectF rounded_corner_bounds_lr(gfx::RectF(pass_rect), 0, 0, 0, 0, - kCornerRadii[2], kCornerRadii[2], 0, 0); - gfx::RRectF rounded_corner_bounds_ll(gfx::RectF(pass_rect), 0, 0, 0, 0, 0, 0, - kCornerRadii[3], kCornerRadii[3]); + gfx::RRectF rounded_corner_bounds_ul(gfx::RectF(pass_rect), kCornerRadiiUL); + gfx::RRectF rounded_corner_bounds_ur(gfx::RectF(pass_rect), kCornerRadiiUR); + gfx::RRectF rounded_corner_bounds_lr(gfx::RectF(pass_rect), kCornerRadiiLR); + gfx::RRectF rounded_corner_bounds_ll(gfx::RectF(pass_rect), kCornerRadiiLL); gfx::Rect ul_rect = pass_rect; ul_rect.set_height(ul_rect.height() / 2);
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 8597d651..58aab45 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -1643,10 +1643,9 @@ // Scale by the filter's scale, but don't apply filter origin rpdq_params.backdrop_filter_bounds->Scale(quad->filters_scale.x(), quad->filters_scale.y()); - // Offset by the difference in origins between the quad->rect and the - // render pass' output rect. + // Offset by the render pass' output rect. rpdq_params.backdrop_filter_bounds->Offset( - quad->rect.origin() - + gfx::Point() - current_frame()->current_render_pass->output_rect.origin()); // If there are also regular image filters, they apply to the area of
diff --git a/components/viz/service/display_embedder/output_surface_provider_impl.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc index a666fbe5..8fe39758 100644 --- a/components/viz/service/display_embedder/output_surface_provider_impl.cc +++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -244,8 +244,8 @@ return std::make_unique<SoftwareOutputDevice>(); #if defined(OS_WIN) - return CreateSoftwareOutputDeviceWinGpu( - surface_handle, &output_device_backing_, display_client); + return CreateSoftwareOutputDeviceWin(surface_handle, &output_device_backing_, + display_client); #elif defined(OS_MACOSX) return std::make_unique<SoftwareOutputDeviceMac>(task_runner_); #elif defined(OS_ANDROID)
diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc index a339eaa..0fcded1 100644 --- a/components/viz/service/display_embedder/software_output_device_win.cc +++ b/components/viz/service/display_embedder/software_output_device_win.cc
@@ -170,62 +170,6 @@ ::ReleaseDC(hwnd(), hdc); } -// SoftwareOutputDevice implementation that uses layered window API to draw to -// the provided HWND. -class SoftwareOutputDeviceWinLayered : public SoftwareOutputDeviceWinBase { - public: - explicit SoftwareOutputDeviceWinLayered(HWND hwnd) - : SoftwareOutputDeviceWinBase(hwnd) {} - ~SoftwareOutputDeviceWinLayered() override = default; - - // SoftwareOutputDeviceWinBase implementation. - void ResizeDelegated() override; - SkCanvas* BeginPaintDelegated() override; - void EndPaintDelegated(const gfx::Rect& damage_rect) override; - - private: - std::unique_ptr<SkCanvas> canvas_; - - DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceWinLayered); -}; - -void SoftwareOutputDeviceWinLayered::ResizeDelegated() { - canvas_.reset(); -} - -SkCanvas* SoftwareOutputDeviceWinLayered::BeginPaintDelegated() { - if (!canvas_) { - // Layered windows can't share a pixel backing. - canvas_ = skia::CreatePlatformCanvasWithSharedSection( - viewport_pixel_size_.width(), viewport_pixel_size_.height(), true, - nullptr, skia::CRASH_ON_FAILURE); - } - return canvas_.get(); -} - -void SoftwareOutputDeviceWinLayered::EndPaintDelegated( - const gfx::Rect& damage_rect) { - if (!canvas_) - return; - - // Set WS_EX_LAYERED extended window style if not already set. - DWORD style = GetWindowLong(hwnd(), GWL_EXSTYLE); - DCHECK(!(style & WS_EX_COMPOSITED)); - if (!(style & WS_EX_LAYERED)) - SetWindowLong(hwnd(), GWL_EXSTYLE, style | WS_EX_LAYERED); - - RECT wr; - GetWindowRect(hwnd(), &wr); - SIZE size = {wr.right - wr.left, wr.bottom - wr.top}; - POINT position = {wr.left, wr.top}; - POINT zero = {0, 0}; - BLENDFUNCTION blend = {AC_SRC_OVER, 0x00, 0xFF, AC_SRC_ALPHA}; - - HDC dib_dc = skia::GetNativeDrawingContext(canvas_.get()); - UpdateLayeredWindow(hwnd(), nullptr, &position, &size, dib_dc, &zero, - RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); -} - // SoftwareOutputDevice implementation that uses layered window API to draw // indirectly. Since UpdateLayeredWindow() is blocked by the GPU sandbox an // implementation of mojom::LayeredWindowUpdater in the browser process handles @@ -340,16 +284,7 @@ } // namespace -std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinBrowser( - HWND hwnd, - OutputDeviceBacking* backing) { - if (NeedsToUseLayerWindow(hwnd)) - return std::make_unique<SoftwareOutputDeviceWinLayered>(hwnd); - - return std::make_unique<SoftwareOutputDeviceWinDirect>(hwnd, backing); -} - -std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinGpu( +std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWin( HWND hwnd, OutputDeviceBacking* backing, mojom::DisplayClient* display_client) {
diff --git a/components/viz/service/display_embedder/software_output_device_win.h b/components/viz/service/display_embedder/software_output_device_win.h index aa2dd64..e45b64a 100644 --- a/components/viz/service/display_embedder/software_output_device_win.h +++ b/components/viz/service/display_embedder/software_output_device_win.h
@@ -17,17 +17,11 @@ class OutputDeviceBacking; -// Creates an appropriate SoftwareOutputDevice implementation for the browser -// process. +// Creates an appropriate SoftwareOutputDevice implementation. VIZ_SERVICE_EXPORT std::unique_ptr<SoftwareOutputDevice> -CreateSoftwareOutputDeviceWinBrowser(HWND hwnd, OutputDeviceBacking* backing); - -// Creates an appropriate SoftwareOutputDevice implementation for the GPU -// process. -VIZ_SERVICE_EXPORT std::unique_ptr<SoftwareOutputDevice> -CreateSoftwareOutputDeviceWinGpu(HWND hwnd, - OutputDeviceBacking* backing, - mojom::DisplayClient* display_client); +CreateSoftwareOutputDeviceWin(HWND hwnd, + OutputDeviceBacking* backing, + mojom::DisplayClient* display_client); } // namespace viz
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index c500490..73f96e2 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1660,10 +1660,10 @@ "sandbox_parameters_mac.mm", "scheduler/browser_task_executor.cc", "scheduler/browser_task_executor.h", + "scheduler/browser_task_queues.cc", + "scheduler/browser_task_queues.h", "scheduler/browser_ui_thread_scheduler.cc", "scheduler/browser_ui_thread_scheduler.h", - "scheduler/browser_ui_thread_task_queue.cc", - "scheduler/browser_ui_thread_task_queue.h", "scheduler/responsiveness/calculator.cc", "scheduler/responsiveness/calculator.h", "scheduler/responsiveness/message_loop_observer.cc", @@ -2604,8 +2604,6 @@ "compositor/browser_compositor_output_surface.h", "compositor/gpu_browser_compositor_output_surface.cc", "compositor/gpu_browser_compositor_output_surface.h", - "compositor/gpu_output_surface_mac.cc", - "compositor/gpu_output_surface_mac.h", "compositor/gpu_process_transport_factory.cc", "compositor/gpu_process_transport_factory.h", "compositor/gpu_surfaceless_browser_compositor_output_surface.cc", @@ -2630,16 +2628,6 @@ "renderer_host/delegated_frame_host.cc", "renderer_host/delegated_frame_host.h", ] - if (is_mac) { - jumbo_excluded_sources = [ - # Both Mac SDK headers and third_party/khronos/GLES2/gl2ext.h - # declare macros GL_LINES_ADJACENCY_EXT, GL_LINE_STRIP_ADJACENCY_EXT, - # GL_TRIANGLES_ADJACENCY_EXT and GL_TRIANGLE_STRIP_ADJACENCY_EXT. They - # get the same values but with different formatting (0xD vs 0x000D). - # https://crbug.com/783666 - "compositor/gpu_output_surface_mac.cc", - ] - } deps += [ "//components/viz/service", "//ui/compositor",
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc index 2a8894d..2ff4266 100644 --- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc +++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -117,16 +117,16 @@ VLOG(1) << tree->ToString(); EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->data().role); - ASSERT_EQ(2, root->child_count()); + ASSERT_EQ(2, root->GetUnignoredChildCount()); - const ui::AXNode* live_region = root->ChildAtIndex(0); - ASSERT_EQ(1, live_region->child_count()); + const ui::AXNode* live_region = root->GetUnignoredChildAtIndex(0); + ASSERT_EQ(1, live_region->GetUnignoredChildCount()); EXPECT_EQ(ax::mojom::Role::kGenericContainer, live_region->data().role); - const ui::AXNode* para = live_region->ChildAtIndex(0); + const ui::AXNode* para = live_region->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kParagraph, para->data().role); - const ui::AXNode* button = root->ChildAtIndex(1); + const ui::AXNode* button = root->GetUnignoredChildAtIndex(1); EXPECT_EQ(ax::mojom::Role::kButton, button->data().role); }
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc index 26dd7e3..eb98c7c 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -181,6 +181,7 @@ AddPropertyFilter(property_filters, "flowtoIds*"); AddPropertyFilter(property_filters, "detailsIds*"); AddPropertyFilter(property_filters, "invalidState=*"); + AddPropertyFilter(property_filters, "ignored*"); AddPropertyFilter(property_filters, "invalidState=false", PropertyFilter::DENY); // Don't show false value AddPropertyFilter(property_filters, "roleDescription=*"); @@ -202,8 +203,9 @@ const BrowserAccessibility& node) const { if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) return node.PlatformChildCount(); - else - return node.InternalChildCount(); + // We don't want to use InternalGetChild as we want to include + // ignored nodes in the tree for tests. + return node.node()->child_count(); } BrowserAccessibility* AccessibilityTreeFormatterBlink::GetChild( @@ -211,8 +213,11 @@ uint32_t i) const { if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) return node.PlatformGetChild(i); - else - return node.InternalGetChild(i); + // We don't want to use InternalGetChild as we want to include + // ignored nodes in the tree for tests. + ui::AXNode* child_node = node.node()->ChildAtIndex(i); + DCHECK(child_node); + return node.manager()->GetFromAXNode(child_node); } void AccessibilityTreeFormatterBlink::AddProperties(
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index ba4c6e54..3fe6a2b 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc
@@ -101,7 +101,7 @@ if (!instance_active()) return nullptr; - ui::AXNode* parent = node_->parent(); + ui::AXNode* parent = node_->GetUnignoredParent(); if (parent) return manager_->GetFromAXNode(parent); @@ -307,25 +307,26 @@ uint32_t BrowserAccessibility::InternalChildCount() const { if (!node_ || !manager_) return 0; - return static_cast<uint32_t>(node_->child_count()); + return node_->GetUnignoredChildCount(); } BrowserAccessibility* BrowserAccessibility::InternalGetChild( uint32_t child_index) const { - if (!node_ || !manager_ || child_index >= InternalChildCount()) + if (!node_ || !manager_) return nullptr; + auto* child_node = node_->GetUnignoredChildAtIndex(child_index); + if (child_node) + return manager_->GetFromAXNode(child_node); - auto* child_node = node_->ChildAtIndex(child_index); - DCHECK(child_node); - return manager_->GetFromAXNode(child_node); + return nullptr; } BrowserAccessibility* BrowserAccessibility::InternalGetParent() const { if (!node_ || !manager_) return nullptr; - ui::AXNode* parent = node_->parent(); - if (parent) - return manager_->GetFromAXNode(parent); + auto* child_node = node_->GetUnignoredParent(); + if (child_node) + return manager_->GetFromAXNode(child_node); return nullptr; } @@ -1389,7 +1390,7 @@ } int BrowserAccessibility::GetIndexInParent() const { - return node_ ? node_->index_in_parent() : -1; + return node_ ? node_->GetUnignoredIndexInParent() : -1; } gfx::AcceleratedWidget
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index 574447b..fbbb490a 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h
@@ -244,9 +244,9 @@ bool instance_active() const { return node_ && manager_; } ui::AXNode* node() const { return node_; } - // These access the internal accessibility tree, which doesn't necessarily - // reflect the accessibility tree that should be exposed on each platform. - // Use PlatformChildCount and PlatformGetChild to implement platform + // These access the internal unignored accessibility tree, which doesn't + // necessarily reflect the accessibility tree that should be exposed on each + // platform. Use PlatformChildCount and PlatformGetChild to implement platform // accessibility APIs. uint32_t InternalChildCount() const; BrowserAccessibility* InternalGetChild(uint32_t child_index) const;
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index 8c257b6..9fa9a025 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -1200,7 +1200,6 @@ BrowserAccessibility* wrapper = factory_->Create(); id_wrapper_map_[node->id()] = wrapper; wrapper->Init(this, node); - wrapper->OnDataChanged(); } void BrowserAccessibilityManager::OnNodeReparented(ui::AXTree* tree, @@ -1212,7 +1211,6 @@ id_wrapper_map_[node->id()] = wrapper; } wrapper->Init(this, node); - wrapper->OnDataChanged(); } void BrowserAccessibilityManager::OnNodeChanged(ui::AXTree* tree, @@ -1246,6 +1244,25 @@ // we're properly connected. if (ax_tree_id_changed || root_changed) connected_to_parent_tree_node_ = false; + + // Calls OnDataChanged on newly created or reparented nodes. + for (const auto change : changes) { + ui::AXNode* node = change.node; + BrowserAccessibility* wrapper = GetFromAXNode(node); + if (wrapper) { + switch (change.type) { + case NODE_CREATED: + case NODE_REPARENTED: + wrapper->OnDataChanged(); + break; + // Unhandled. + case NODE_CHANGED: + case SUBTREE_CREATED: + case SUBTREE_REPARENTED: + break; + } + } + } } ui::AXNode* BrowserAccessibilityManager::GetNodeFromTree(
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc index 873b961a..f01e138 100644 --- a/content/browser/accessibility/browser_accessibility_unittest.cc +++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -100,11 +100,11 @@ TEST_F(BrowserAccessibilityTest, TestGetDescendants) { // Set up ax tree with the following structure: // - // root____________ - // | | - // para1___ text3 - // | | - // text1 text2 + // root_____________________ + // | | | + // para1____ text3 para2____ (hidden) + // | | | | + // text1 text2 text4 text5 (visible) ui::AXNodeData text1; text1.id = 111; text1.role = ax::mojom::Role::kStaticText; @@ -120,21 +120,41 @@ text3.role = ax::mojom::Role::kStaticText; text3.SetName("Three four five."); + ui::AXNodeData text4; + text4.id = 114; + text4.role = ax::mojom::Role::kStaticText; + text4.SetName("four five six."); + text4.AddState(ax::mojom::State::kIgnored); + + ui::AXNodeData text5; + text5.id = 115; + text5.role = ax::mojom::Role::kStaticText; + text5.SetName("five six seven."); + ui::AXNodeData para1; para1.id = 11; para1.role = ax::mojom::Role::kParagraph; para1.child_ids.push_back(text1.id); para1.child_ids.push_back(text2.id); + ui::AXNodeData para2; + para2.id = 12; + para2.role = ax::mojom::Role::kParagraph; + para2.child_ids.push_back(text4.id); + para2.child_ids.push_back(text5.id); + para2.AddState(ax::mojom::State::kIgnored); + ui::AXNodeData root; root.id = 1; root.role = ax::mojom::Role::kRootWebArea; root.child_ids.push_back(para1.id); root.child_ids.push_back(text3.id); + root.child_ids.push_back(para2.id); std::unique_ptr<BrowserAccessibilityManager> manager( BrowserAccessibilityManager::Create( - MakeAXTreeUpdate(root, para1, text1, text2, text3), + MakeAXTreeUpdate(root, para1, text1, text2, text3, para2, text4, + text5), test_browser_accessibility_delegate_.get(), new BrowserAccessibilityFactory())); @@ -143,6 +163,9 @@ BrowserAccessibility* text1_obj = manager->GetFromID(111); BrowserAccessibility* text2_obj = manager->GetFromID(112); BrowserAccessibility* text3_obj = manager->GetFromID(113); + BrowserAccessibility* para2_obj = manager->GetFromID(12); + BrowserAccessibility* text4_obj = manager->GetFromID(114); + BrowserAccessibility* text5_obj = root_obj->PlatformGetChild(2); // Leaf nodes should have no children. std::vector<gfx::NativeViewAccessible> descendants = @@ -156,6 +179,16 @@ descendants = text3_obj->GetDescendants(); EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants); + descendants = text4_obj->GetDescendants(); + EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants); + + descendants = text5_obj->GetDescendants(); + EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants); + + descendants = para2_obj->GetDescendants(); + expected_descendants = {text5_obj->GetNativeViewAccessible()}; + EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants); + // Verify that para1 has two children (text1 and tex2). descendants = para_obj->GetDescendants(); expected_descendants = {text1_obj->GetNativeViewAccessible(), @@ -164,11 +197,13 @@ // Calling GetChildNodeIds on the root should encompass the entire // right and left subtrees (para1, text1, text2, and text3). + // para2 and its subtree should be ignored, except for text5 descendants = root_obj->GetDescendants(); expected_descendants = {para_obj->GetNativeViewAccessible(), text1_obj->GetNativeViewAccessible(), text2_obj->GetNativeViewAccessible(), - text3_obj->GetNativeViewAccessible()}; + text3_obj->GetNativeViewAccessible(), + text5_obj->GetNativeViewAccessible()}; EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants); manager.reset();
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc index a34d8ab9..bcabb28 100644 --- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc +++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -188,8 +188,8 @@ EXPECT_EQ(ax::mojom::Role::kRootWebArea, root->data().role); // Check properties of the BODY element. - ASSERT_EQ(1, root->child_count()); - const ui::AXNode* body = root->ChildAtIndex(0); + ASSERT_EQ(1, root->GetUnignoredChildCount()); + const ui::AXNode* body = root->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kGenericContainer, body->data().role); EXPECT_STREQ("body", GetAttr(body, ax::mojom::StringAttribute::kHtmlTag).c_str()); @@ -197,9 +197,9 @@ GetAttr(body, ax::mojom::StringAttribute::kDisplay).c_str()); // Check properties of the two children of the BODY element. - ASSERT_EQ(2, body->child_count()); + ASSERT_EQ(2, body->GetUnignoredChildCount()); - const ui::AXNode* button = body->ChildAtIndex(0); + const ui::AXNode* button = body->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kButton, button->data().role); EXPECT_STREQ("input", GetAttr(button, ax::mojom::StringAttribute::kHtmlTag).c_str()); @@ -213,7 +213,7 @@ EXPECT_STREQ("value", button->data().html_attributes[1].first.c_str()); EXPECT_STREQ("push", button->data().html_attributes[1].second.c_str()); - const ui::AXNode* checkbox = body->ChildAtIndex(1); + const ui::AXNode* checkbox = body->GetUnignoredChildAtIndex(1); EXPECT_EQ(ax::mojom::Role::kCheckBox, checkbox->data().role); EXPECT_STREQ("input", GetAttr(checkbox, ax::mojom::StringAttribute::kHtmlTag).c_str()); @@ -238,10 +238,10 @@ const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - ASSERT_EQ(1, root->child_count()); - const ui::AXNode* body = root->ChildAtIndex(0); - ASSERT_EQ(1, body->child_count()); - const ui::AXNode* text = body->ChildAtIndex(0); + ASSERT_EQ(1, root->GetUnignoredChildCount()); + const ui::AXNode* body = root->GetUnignoredChildAtIndex(0); + ASSERT_EQ(1, body->GetUnignoredChildCount()); + const ui::AXNode* text = body->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kTextField, text->data().role); EXPECT_STREQ("input", GetAttr(text, ax::mojom::StringAttribute::kHtmlTag).c_str()); @@ -269,10 +269,10 @@ const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - ASSERT_EQ(1, root->child_count()); - const ui::AXNode* body = root->ChildAtIndex(0); - ASSERT_EQ(1, body->child_count()); - const ui::AXNode* text = body->ChildAtIndex(0); + ASSERT_EQ(1, root->GetUnignoredChildCount()); + const ui::AXNode* body = root->GetUnignoredChildAtIndex(0); + ASSERT_EQ(1, body->GetUnignoredChildCount()); + const ui::AXNode* text = body->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kTextField, text->data().role); EXPECT_STREQ("input", GetAttr(text, ax::mojom::StringAttribute::kHtmlTag).c_str()); @@ -323,33 +323,33 @@ const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - ASSERT_EQ(1, root->child_count()); - const ui::AXNode* body = root->ChildAtIndex(0); - ASSERT_EQ(3, body->child_count()); + ASSERT_EQ(1, root->GetUnignoredChildCount()); + const ui::AXNode* body = root->GetUnignoredChildAtIndex(0); + ASSERT_EQ(3, body->GetUnignoredChildCount()); - const ui::AXNode* button1 = body->ChildAtIndex(0); + const ui::AXNode* button1 = body->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kButton, button1->data().role); EXPECT_STREQ("Button 1", GetAttr(button1, ax::mojom::StringAttribute::kName).c_str()); - const ui::AXNode* iframe = body->ChildAtIndex(1); + const ui::AXNode* iframe = body->GetUnignoredChildAtIndex(1); EXPECT_STREQ("iframe", GetAttr(iframe, ax::mojom::StringAttribute::kHtmlTag).c_str()); - ASSERT_EQ(1, iframe->child_count()); + ASSERT_EQ(1, iframe->GetUnignoredChildCount()); - const ui::AXNode* sub_document = iframe->ChildAtIndex(0); + const ui::AXNode* sub_document = iframe->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kWebArea, sub_document->data().role); - ASSERT_EQ(1, sub_document->child_count()); + ASSERT_EQ(1, sub_document->GetUnignoredChildCount()); - const ui::AXNode* sub_body = sub_document->ChildAtIndex(0); - ASSERT_EQ(1, sub_body->child_count()); + const ui::AXNode* sub_body = sub_document->GetUnignoredChildAtIndex(0); + ASSERT_EQ(1, sub_body->GetUnignoredChildCount()); - const ui::AXNode* button2 = sub_body->ChildAtIndex(0); + const ui::AXNode* button2 = sub_body->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kButton, button2->data().role); EXPECT_STREQ("Button 2", GetAttr(button2, ax::mojom::StringAttribute::kName).c_str()); - const ui::AXNode* button3 = body->ChildAtIndex(2); + const ui::AXNode* button3 = body->GetUnignoredChildAtIndex(2); EXPECT_EQ(ax::mojom::Role::kButton, button3->data().role); EXPECT_STREQ("Button 3", GetAttr(button3, ax::mojom::StringAttribute::kName).c_str()); @@ -396,18 +396,24 @@ const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - const ui::AXNode* table = root->ChildAtIndex(0); + const ui::AXNode* table = root->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kTable, table->data().role); - ASSERT_GE(table->child_count(), 2); - EXPECT_EQ(ax::mojom::Role::kRow, table->ChildAtIndex(0)->data().role); - EXPECT_EQ(ax::mojom::Role::kRow, table->ChildAtIndex(1)->data().role); + ASSERT_GE(table->GetUnignoredChildCount(), 2); + EXPECT_EQ(ax::mojom::Role::kRow, + table->GetUnignoredChildAtIndex(0)->data().role); + EXPECT_EQ(ax::mojom::Role::kRow, + table->GetUnignoredChildAtIndex(1)->data().role); EXPECT_EQ(3, GetIntAttr(table, ax::mojom::IntAttribute::kTableColumnCount)); EXPECT_EQ(2, GetIntAttr(table, ax::mojom::IntAttribute::kTableRowCount)); - const ui::AXNode* cell1 = table->ChildAtIndex(0)->ChildAtIndex(0); - const ui::AXNode* cell2 = table->ChildAtIndex(0)->ChildAtIndex(1); - const ui::AXNode* cell3 = table->ChildAtIndex(1)->ChildAtIndex(0); - const ui::AXNode* cell4 = table->ChildAtIndex(1)->ChildAtIndex(1); + const ui::AXNode* cell1 = + table->GetUnignoredChildAtIndex(0)->GetUnignoredChildAtIndex(0); + const ui::AXNode* cell2 = + table->GetUnignoredChildAtIndex(0)->GetUnignoredChildAtIndex(1); + const ui::AXNode* cell3 = + table->GetUnignoredChildAtIndex(1)->GetUnignoredChildAtIndex(0); + const ui::AXNode* cell4 = + table->GetUnignoredChildAtIndex(1)->GetUnignoredChildAtIndex(1); EXPECT_EQ(0, GetIntAttr(cell1, ax::mojom::IntAttribute::kTableCellColumnIndex)); @@ -440,8 +446,8 @@ NavigateToURL(shell(), url); const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - ASSERT_EQ(1, root->child_count()); - const ui::AXNode* textbox = root->ChildAtIndex(0); + ASSERT_EQ(1, root->GetUnignoredChildCount()); + const ui::AXNode* textbox = root->GetUnignoredChildAtIndex(0); EXPECT_TRUE(textbox->data().HasAction(ax::mojom::Action::kSetValue)); } @@ -462,17 +468,17 @@ const ui::AXTree& tree = GetAXTree(); const ui::AXNode* root = tree.root(); - const ui::AXNode* table = root->ChildAtIndex(0); + const ui::AXNode* table = root->GetUnignoredChildAtIndex(0); EXPECT_EQ(ax::mojom::Role::kTable, table->data().role); - EXPECT_EQ(1, table->child_count()); - const ui::AXNode* row = table->ChildAtIndex(0); - EXPECT_EQ(5, row->child_count()); + EXPECT_EQ(1, table->GetUnignoredChildCount()); + const ui::AXNode* row = table->GetUnignoredChildAtIndex(0); + EXPECT_EQ(5, row->GetUnignoredChildCount()); - const ui::AXNode* header1 = row->ChildAtIndex(0); - const ui::AXNode* header2 = row->ChildAtIndex(1); - const ui::AXNode* header3 = row->ChildAtIndex(2); - const ui::AXNode* header4 = row->ChildAtIndex(3); - const ui::AXNode* header5 = row->ChildAtIndex(4); + const ui::AXNode* header1 = row->GetUnignoredChildAtIndex(0); + const ui::AXNode* header2 = row->GetUnignoredChildAtIndex(1); + const ui::AXNode* header3 = row->GetUnignoredChildAtIndex(2); + const ui::AXNode* header4 = row->GetUnignoredChildAtIndex(3); + const ui::AXNode* header5 = row->GetUnignoredChildAtIndex(4); EXPECT_EQ(static_cast<int>(ax::mojom::SortDirection::kAscending), GetIntAttr(header1, ax::mojom::IntAttribute::kSortDirection));
diff --git a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc index 912294f..f32a8cf 100644 --- a/content/browser/accessibility/snapshot_ax_tree_browsertest.cc +++ b/content/browser/accessibility/snapshot_ax_tree_browsertest.cc
@@ -52,7 +52,7 @@ node->data().GetStringAttribute(ax::mojom::StringAttribute::kName) + "'"; *dst += "\n"; - for (int i = 0; i < node->child_count(); ++i) + for (int i = 0; i < node->GetUnignoredChildCount(); ++i) DumpRolesAndNamesAsText(node->children()[i], indent + 1, dst); } @@ -87,9 +87,9 @@ ui::AXNode* root = tree.root(); ASSERT_NE(nullptr, root); ASSERT_EQ(ax::mojom::Role::kRootWebArea, root->data().role); - ui::AXNode* group = root->ChildAtIndex(0); + ui::AXNode* group = root->GetUnignoredChildAtIndex(0); ASSERT_EQ(ax::mojom::Role::kGenericContainer, group->data().role); - ui::AXNode* button = group->ChildAtIndex(0); + ui::AXNode* button = group->GetUnignoredChildAtIndex(0); ASSERT_EQ(ax::mojom::Role::kButton, button->data().role); }
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc index 4193784fd..96f0631 100644 --- a/content/browser/appcache/appcache_storage_impl.cc +++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -83,14 +83,11 @@ } // namespace -// Destroys |database|. If there is appcache data to be deleted -// (|force_keep_session_state| is false), deletes session-only appcache data. +// static void AppCacheStorageImpl::ClearSessionOnlyOrigins( - AppCacheDatabase* database, + std::unique_ptr<AppCacheDatabase> database, scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy, bool force_keep_session_state) { - std::unique_ptr<AppCacheDatabase> database_to_delete(database); - // If saving session state, only delete the database. if (force_keep_session_state) return; @@ -129,8 +126,8 @@ return; } std::vector<int64_t> deletable_response_ids; - bool success = DeleteGroupAndRelatedRecords(database, group.group_id, - &deletable_response_ids); + bool success = DeleteGroupAndRelatedRecords( + database.get(), group.group_id, &deletable_response_ids); success = success && transaction.Commit(); DCHECK(success); } // for each group @@ -144,7 +141,7 @@ public: explicit DatabaseTask(AppCacheStorageImpl* storage) : storage_(storage), - database_(storage->database_), + database_(storage->database_.get()), io_thread_(base::SequencedTaskRunnerHandle::Get()) { DCHECK(io_thread_.get()); } @@ -178,7 +175,7 @@ virtual ~DatabaseTask() {} AppCacheStorageImpl* storage_; - AppCacheDatabase* database_; + AppCacheDatabase* const database_; DelegateReferenceVector delegates_; private: @@ -186,7 +183,7 @@ void CallRunCompleted(base::TimeTicks schedule_time); void OnFatalError(); - scoped_refptr<base::SequencedTaskRunner> io_thread_; + const scoped_refptr<base::SequencedTaskRunner> io_thread_; }; void AppCacheStorageImpl::DatabaseTask::Schedule() { @@ -577,9 +574,10 @@ database_->FindCacheForGroup(group_record_.group_id, &cache_record_) && FindRelatedCacheRecords(cache_record_.cache_id); - if (success_) + if (success_) { database_->LazyUpdateLastAccessTime(group_record_.group_id, base::Time::Now()); + } } void AppCacheStorageImpl::GroupLoadTask::RunCompleted() { @@ -702,7 +700,7 @@ void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() { DCHECK(!success_); - sql::Database* connection = database_->db_connection(); + sql::Database* const connection = database_->db_connection(); if (!connection) return; @@ -898,7 +896,7 @@ // Key is cache id using WhiteListMap = std::map<int64_t, std::vector<AppCacheNamespace>>; WhiteListMap namespaces_map_; - AppCacheDatabase* database_; + AppCacheDatabase* const database_; }; } // namespace @@ -1411,13 +1409,12 @@ if (database_ && !db_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ClearSessionOnlyOrigins, database_, - base::WrapRefCounted( - service_->special_storage_policy()), - service()->force_keep_session_state()))) { - delete database_; + FROM_HERE, + base::BindOnce( + &ClearSessionOnlyOrigins, std::move(database_), + base::WrapRefCounted(service_->special_storage_policy()), + service()->force_keep_session_state()))) { } - database_ = nullptr; // So no further database tasks can be scheduled. } void AppCacheStorageImpl::Initialize( @@ -1429,7 +1426,7 @@ base::FilePath db_file_path; if (!is_incognito_) db_file_path = cache_directory_.Append(kAppCacheDatabaseName); - database_ = new AppCacheDatabase(db_file_path); + database_ = std::make_unique<AppCacheDatabase>(db_file_path); db_task_runner_ = db_task_runner;
diff --git a/content/browser/appcache/appcache_storage_impl.h b/content/browser/appcache/appcache_storage_impl.h index 528c262..de16f25 100644 --- a/content/browser/appcache/appcache_storage_impl.h +++ b/content/browser/appcache/appcache_storage_impl.h
@@ -130,8 +130,10 @@ void LazilyCommitLastAccessTimes(); void OnLazyCommitTimer(); + // If there is appcache data to be deleted (|force_keep_session_state| is + // false), deletes session-only appcache data. static void ClearSessionOnlyOrigins( - AppCacheDatabase* database, + std::unique_ptr<AppCacheDatabase> database, scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy, bool force_keep_session_state); @@ -180,7 +182,7 @@ int64_t last_deletable_response_rowid_; // Created on the IO thread, but only used on the DB thread. - AppCacheDatabase* database_; + std::unique_ptr<AppCacheDatabase> database_; // Set if we discover a fatal error like a corrupt SQL database or // disk cache and cannot continue.
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc index c58f47ef..585f1ba 100644 --- a/content/browser/appcache/appcache_storage_impl_unittest.cc +++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -1783,7 +1783,7 @@ return static_cast<AppCacheStorageImpl*>(service()->storage()); } - AppCacheDatabase* database() { return storage()->database_; } + AppCacheDatabase* database() { return storage()->database_.get(); } MockStorageDelegate* delegate() { return delegate_.get(); }
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc index 37489b3..72e7cfd2 100644 --- a/content/browser/browser_thread_unittest.cc +++ b/content/browser/browser_thread_unittest.cc
@@ -40,7 +40,7 @@ BrowserUIThreadScheduler::CreateForTesting( sequence_manager_.get(), sequence_manager_->GetRealTimeDomain()); - default_task_runner_ = browser_ui_thread_scheduler->GetTaskRunnerForTesting( + default_task_runner_ = browser_ui_thread_scheduler->GetHandle().task_runner( BrowserUIThreadScheduler::QueueType::kDefault); sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
diff --git a/content/browser/cache_storage/README.md b/content/browser/cache_storage/README.md index 77dfc1f..924e16a 100644 --- a/content/browser/cache_storage/README.md +++ b/content/browser/cache_storage/README.md
@@ -26,9 +26,11 @@ ##### `CacheStorageDispatcherHost`=>`CacheStorageCacheHandle`~>`CacheStorageCache` * The `CacheStorageDispatcherHost` holds onto handles for: - * currently running operations + * JavaScript references to cache objects + +##### `CacheStorageDispatcherHost`=>`CacheStorageHandle`~>`CacheStorage` +* The `CacheStorageDispatcherHost` holds onto handles for: * JavaScript references to caches - * recently opened caches (to prevent open/close/open churn) ##### `CacheStorageCacheDataHandle`=>`CacheStorageCacheHandle`~>`CacheStorageCache` * `CacheStorageCacheDataHandle` is the blob data handle for a response body @@ -38,6 +40,12 @@ one of its `disk_cache::Entry`s is reachable. Otherwise, a new backend might open and clobber the entry. +##### `CacheStorageCache`=>`CacheStorageCacheHandle`~>`CacheStorageCache` +* The `CacheStorageCache` will hold a self-reference while executing an + operation. This self-reference is dropped between subsequent operations, + so shutdown is possible when there are no external references even if there + are more operations in the scheduler queue. + ### CacheStorageDispatcherHost 1. Receives IPC messages from a render process and creates the appropriate `CacheStorageManager` or `CacheStorageCache` operation. @@ -45,8 +53,10 @@ alive since the operation is asynchronous. 3. For each cache reference held by the render process, holds a `CacheStorageCacheHandle`. -4. Holds a newly opened cache open for a few seconds (by storing a handle) to - mitigate rapid opening/closing/opening churn. +4. For each CacheStorage reference held by the renderer process, holds a + `CacheStorageHandle`. This is used to inform the CacheStorage about + whether its externally used so it can keep warmed cache objects alive + to mitigate rapid opening/closing/opening churn. ### CacheStorageManager 1. Forwards calls to the appropriate `CacheStorage` for a given origin-owner @@ -82,6 +92,14 @@ (on `CacheStorage` destruction), so it must be checked for validity before use. +### CacheStorageHandle +1. Holds a weak reference to a `CacheStorage`. +2. When the last `CacheStorageHandle` to a `CacheStorage` is + deleted, internal state is cleaned up. The `CacheStorage` object is not + deleted, however. +3. The `CacheStorage` may be deleted before the `CacheStorageHandle` + (on browser shutdown), so it must be checked for validity before use. + ## Directory Structure $PROFILE/Service Worker/CacheStorage/`origin`/`cache`/
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h index 0af3768..8350105 100644 --- a/content/browser/cache_storage/cache_storage_cache.h +++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -19,10 +19,15 @@ namespace content { -// Represents a ServiceWorker Cache as seen in -// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ The -// asynchronous methods are executed serially. Callbacks to the public functions -// will be called so long as the cache object lives. +// Represents a ServiceWorker Cache as seen in: +// +// https://w3c.github.io/ServiceWorker/#cache-interface +// +// The asynchronous methods are executed serially. Callbacks to the public +// functions will be called so long as the cache object lives. It is important +// to for client code hold a |CacheStorageCacheHandle| to the cache for the +// duration of any operations. Otherwise it is possible the operation may +// get cancelled in some circumstances. class CONTENT_EXPORT CacheStorageCache { public: using CacheEntry = std::pair<blink::mojom::FetchAPIRequestPtr, @@ -51,6 +56,14 @@ // additional data, such as response side blobs or request bodies. enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY, INDEX_SIDE_DATA }; + // Create a handle that will hold the CacheStorageCache alive. Client code + // should hold one of these handles while waiting for operation callbacks to + // be invoked. + // + // Note, its still possible for the CacheStorageCache to be deleted even if + // there are outstanding handle references. This can occur when the user + // triggers a storage wipe, for example. The handle value should be treated + // as a weak pointer. virtual CacheStorageCacheHandle CreateHandle() = 0; virtual void AddHandleRef() = 0; virtual void DropHandleRef() = 0;
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc index 8ba3eb9..f19da57 100644 --- a/content/browser/cache_storage/cache_storage_cache_unittest.cc +++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -370,18 +370,39 @@ } private: - CacheStorageCacheHandle CreateHandle() override { - // Returns an empty handle. There is no need for CacheStorage and its - // handles in these tests. - return CacheStorageCacheHandle(); - } - bool delay_backend_creation_; ErrorCallback backend_creation_callback_; DISALLOW_COPY_AND_ASSIGN(TestCacheStorageCache); }; +class MockLegacyCacheStorage : public LegacyCacheStorage { + public: + MockLegacyCacheStorage( + const base::FilePath& origin_path, + bool memory_only, + base::SequencedTaskRunner* cache_task_runner, + scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy, + base::WeakPtr<storage::BlobStorageContext> blob_context, + CacheStorageManager* cache_storage_manager, + const url::Origin& origin, + CacheStorageOwner owner) + : LegacyCacheStorage(origin_path, + memory_only, + cache_task_runner, + std::move(quota_manager_proxy), + std::move(blob_context), + cache_storage_manager, + std::move(origin), + owner) {} + + void CacheUnreferenced(LegacyCacheStorageCache* cache) override { + // Normally the LegacyCacheStorage will attempt to delete the cache + // from its map when the cache has become unreferenced. Since we are + // using detached cache objects we instead override to do nothing here. + } +}; + class CacheStorageCacheTest : public testing::Test { public: CacheStorageCacheTest() @@ -431,8 +452,17 @@ std::make_unique<storage::BlobDataHandle>(*blob_handle_), MakeRequest(&blob_ptr_)); + // Use a mock LegacyCacheStorage object so we can use real + // CacheStorageCacheHandle reference counting. A LegacyCacheStorage + // must be present to be notified when a cache becomes unreferenced. + mock_cache_storage_ = std::make_unique<MockLegacyCacheStorage>( + temp_dir_path, MemoryOnly(), base::ThreadTaskRunnerHandle::Get().get(), + quota_manager_proxy_, blob_storage_context_->AsWeakPtr(), + /* cache_storage_manager = */ nullptr, kOrigin, + CacheStorageOwner::kCacheAPI); + cache_ = std::make_unique<TestCacheStorageCache>( - kOrigin, kCacheName, temp_dir_path, nullptr /* CacheStorage */, + kOrigin, kCacheName, temp_dir_path, mock_cache_storage_.get(), quota_manager_proxy_, blob_storage_context->context()->AsWeakPtr()); cache_->Init(); } @@ -810,6 +840,7 @@ scoped_refptr<MockQuotaManager> mock_quota_manager_; scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_; storage::BlobStorageContext* blob_storage_context_; + std::unique_ptr<MockLegacyCacheStorage> mock_cache_storage_; std::unique_ptr<TestCacheStorageCache> cache_; @@ -2286,6 +2317,221 @@ EXPECT_EQ(expected_keys, callback_strings_); } +TEST_P(CacheStorageCacheTestP, SelfRefsDuringMatch) { + EXPECT_TRUE(Put(body_request_, CreateBlobBodyResponse())); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->Match(CopyFetchRequest(body_request_), /* match_options = */ nullptr, + /* trace_id = */ 0, + base::BindOnce(&CacheStorageCacheTest::ResponseAndErrorCallback, + base::Unretained(this), loop.get())); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, SelfRefsDuringMatchAll) { + EXPECT_TRUE(Put(body_request_, CreateBlobBodyResponse())); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + std::vector<blink::mojom::FetchAPIResponsePtr> responses; + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->MatchAll( + CopyFetchRequest(body_request_), /* match_options = */ nullptr, + /* trace_id = */ 0, + base::BindOnce(&CacheStorageCacheTest::ResponsesAndErrorCallback, + base::Unretained(this), loop->QuitClosure(), &responses)); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); + EXPECT_EQ(1u, responses.size()); +} + +TEST_P(CacheStorageCacheTestP, SelfRefsDuringWriteSideData) { + base::Time response_time(base::Time::Now()); + blink::mojom::FetchAPIResponsePtr response = CreateBlobBodyResponse(); + response->response_time = response_time; + EXPECT_TRUE(Put(body_request_, std::move(response))); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + const std::string expected_side_data = "SideDataSample"; + scoped_refptr<net::IOBuffer> buffer = + base::MakeRefCounted<net::StringIOBuffer>(expected_side_data); + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->WriteSideData( + base::BindOnce(&CacheStorageCacheTest::ErrorTypeCallback, + base::Unretained(this), base::Unretained(loop.get())), + kBodyUrl, response_time, /* trace_id = */ 0, buffer, + expected_side_data.length()); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, SelfRefsDuringBatchOperation) { + // Open the backend + EXPECT_TRUE(Keys()); + + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = BackgroundFetchSettledFetch::CloneRequest(body_request_); + operation->request->url = GURL("http://example.com/1"); + operation->response = CreateBlobBodyResponse(); + operation->response->url_list.push_back(GURL("http://example.com/1")); + + std::vector<blink::mojom::BatchOperationPtr> operations; + operations.push_back(std::move(operation)); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->BatchOperation( + std::move(operations), /* trace_id = */ 0, + base::BindOnce(&CacheStorageCacheTest::VerboseErrorTypeCallback, + base::Unretained(this), base::Unretained(loop.get())), + base::BindOnce(&OnBadMessage, base::Unretained(&bad_message_reason_))); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, SelfRefsDuringKeys) { + EXPECT_TRUE(Put(body_request_, CreateBlobBodyResponse())); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->Keys( + BackgroundFetchSettledFetch::CloneRequest(body_request_), + /* match_options = */ nullptr, + /* trace_id = */ 0, + base::BindOnce(&CacheStorageCacheTest::RequestsCallback, + base::Unretained(this), base::Unretained(loop.get()))); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + +TEST_P(CacheStorageCacheTestP, SelfRefsDuringPut) { + // Open the backend + EXPECT_TRUE(Keys()); + + // When there are no operations outstanding and we're not holding an + // explicit reference the cache should consider itself unreferenced. + EXPECT_TRUE(cache_->IsUnreferenced()); + + DelayableBackend* delayable_backend = cache_->UseDelayableBackend(); + delayable_backend->set_delay_open_entry(true); + + std::unique_ptr<base::RunLoop> loop(new base::RunLoop()); + cache_->Put( + BackgroundFetchSettledFetch::CloneRequest(body_request_), + CreateBlobBodyResponse(), /* trace_id = */ 0, + base::BindOnce(&CacheStorageCacheTest::ErrorTypeCallback, + base::Unretained(this), base::Unretained(loop.get()))); + + // Blocks on opening the cache entry. + base::RunLoop().RunUntilIdle(); + + // Since an operation is outstanding the cache should consider itself + // referenced. + EXPECT_FALSE(cache_->IsUnreferenced()); + + // Allow the operation to continue. + EXPECT_TRUE(delayable_backend->OpenEntryContinue()); + loop->Run(); + + // The operation should succeed. + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + INSTANTIATE_TEST_SUITE_P(CacheStorageCacheTest, CacheStorageCacheTestP, ::testing::Values(false, true));
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc index 380ca88f..3d2ef71f 100644 --- a/content/browser/cache_storage/cache_storage_manager_unittest.cc +++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -53,6 +53,7 @@ #include "storage/browser/blob/blob_url_request_job_factory.h" #include "storage/browser/quota/padding_key.h" #include "storage/browser/quota/quota_manager_proxy.h" +#include "storage/browser/test/fake_blob.h" #include "storage/browser/test/mock_quota_manager_proxy.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "storage/common/blob_storage/blob_handle.h" @@ -71,6 +72,58 @@ using blink::mojom::StorageType; using ResponseHeaderMap = base::flat_map<std::string, std::string>; +class DelayedBlob : public storage::FakeBlob { + public: + DelayedBlob(blink::mojom::BlobRequest request, + std::string data, + base::OnceClosure read_closure) + : FakeBlob("foo"), + binding_(this, std::move(request)), + data_(std::move(data)), + read_closure_(std::move(read_closure)) {} + + void Resume() { + paused_ = false; + MaybeComplete(); + } + + void ReadAll(mojo::ScopedDataPipeProducerHandle producer_handle, + blink::mojom::BlobReaderClientPtr client) override { + client_ = std::move(client); + producer_handle_ = std::move(producer_handle); + + client_->OnCalculatedSize(data_.length(), data_.length()); + + // This should always succeed immediately because we size the pipe to + // hold the entire blob for tiny data lengths. + uint32_t num_bytes = data_.length(); + producer_handle_->WriteData(data_.data(), &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(data_.length(), num_bytes); + + // Signal that ReadAll() was called. + std::move(read_closure_).Run(); + + MaybeComplete(); + } + + private: + void MaybeComplete() { + if (paused_ || !client_) + return; + client_->OnComplete(net::OK, data_.length()); + client_.reset(); + producer_handle_.reset(); + } + + mojo::Binding<blink::mojom::Blob> binding_; + std::string data_; + base::OnceClosure read_closure_; + blink::mojom::BlobReaderClientPtr client_; + mojo::ScopedDataPipeProducerHandle producer_handle_; + bool paused_ = true; +}; + class MockCacheStorageQuotaManagerProxy : public MockQuotaManagerProxy { public: MockCacheStorageQuotaManagerProxy(MockQuotaManager* quota_manager, @@ -508,6 +561,23 @@ blob->size = request->url.spec().size(); blob->blob = std::move(blob_ptr_info); + base::RunLoop loop; + CachePutWithStatusCodeAndBlobInternal(cache, std::move(request), + status_code, std::move(blob), &loop, + response_type, response_headers); + loop.Run(); + + return callback_error_ == CacheStorageError::kSuccess; + } + + void CachePutWithStatusCodeAndBlobInternal( + CacheStorageCache* cache, + blink::mojom::FetchAPIRequestPtr request, + int status_code, + blink::mojom::SerializedBlobPtr blob, + base::RunLoop* loop, + FetchResponseType response_type = FetchResponseType::kDefault, + ResponseHeaderMap response_headers = ResponseHeaderMap()) { auto response = blink::mojom::FetchAPIResponse::New( std::vector<GURL>({request->url}), status_code, "OK", response_type, network::mojom::FetchResponseSource::kUnspecified, response_headers, @@ -524,15 +594,11 @@ std::vector<blink::mojom::BatchOperationPtr> operations; operations.emplace_back(std::move(operation)); - base::RunLoop loop; cache->BatchOperation( std::move(operations), /* trace_id = */ 0, base::BindOnce(&CacheStorageManagerTest::CachePutCallback, - base::Unretained(this), base::Unretained(&loop)), + base::Unretained(this), base::Unretained(loop)), CacheStorageCache::BadMessageCallback()); - loop.Run(); - - return callback_error_ == CacheStorageError::kSuccess; } bool CacheDelete(CacheStorageCache* cache, const GURL& url) { @@ -2057,6 +2123,49 @@ EXPECT_FALSE(FlushCacheStorageIndex(origin1_)); } +TEST_P(CacheStorageManagerTestP, SlowPutCompletesWithoutExternalRef) { + EXPECT_TRUE(Open(origin1_, "foo")); + + auto request = blink::mojom::FetchAPIRequest::New(); + request->url = GURL("http://example.com/foo"); + + // Start defining a blob for the response body. + std::string body_data("hello world"); + auto blob = blink::mojom::SerializedBlob::New(); + blob->uuid = "mock blob"; + blob->size = body_data.length(); + + // Provide a fake blob implementation that delays completion. This will + // allow us to pause the writing operation so we can drop the external + // reference. + auto blob_request = mojo::MakeRequest(&blob->blob); + base::RunLoop blob_loop; + DelayedBlob delayed_blob(std::move(blob_request), body_data, + blob_loop.QuitClosure()); + + // Begin the operation to write the blob into the cache. + base::RunLoop cache_loop; + CachePutWithStatusCodeAndBlobInternal(callback_cache_handle_.value(), + std::move(request), 200, + std::move(blob), &cache_loop); + + // Wait for blob's ReadAll() method to be called. + blob_loop.Run(); + + // Drop the external reference to the cache. The operation should hold + // itself alive, but previous versions of the code would destroy the cache + // immediately at this step. + callback_cache_handle_ = CacheStorageCacheHandle(); + + // Signal the blob to complete reading. + delayed_blob.Resume(); + + // Wait for the cache write operation to be complete and verify that it + // succeeded. + cache_loop.Run(); + EXPECT_EQ(CacheStorageError::kSuccess, callback_error_); +} + class CacheStorageQuotaClientTest : public CacheStorageManagerTest { protected: CacheStorageQuotaClientTest() {}
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.cc b/content/browser/cache_storage/legacy/legacy_cache_storage.cc index 73d521e..75ba7c3f 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage.cc +++ b/content/browser/cache_storage/legacy/legacy_cache_storage.cc
@@ -830,6 +830,40 @@ ScheduleWriteIndex(); } +void LegacyCacheStorage::ReleaseUnreferencedCaches() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + for (auto& entry : cache_map_) { + if (entry.second && entry.second->IsUnreferenced()) + entry.second.reset(); + } +} + +void LegacyCacheStorage::CacheUnreferenced(LegacyCacheStorageCache* cache) { + DCHECK(cache); + DCHECK(cache->IsUnreferenced()); + auto doomed_caches_it = doomed_caches_.find(cache); + if (doomed_caches_it != doomed_caches_.end()) { + // The last reference to a doomed cache is gone, perform clean up. + DeleteCacheFinalize(cache); + return; + } + + // Opportunistically keep warmed caches open when the CacheStorage is + // still actively referenced. Repeatedly opening and closing simple + // disk_cache backends can be quite slow. This is easy to trigger when + // a site uses caches.match() frequently because the a Cache object is + // never exposed to script to explicitly hold the backend open. + if (handle_ref_count_) + return; + + // The CacheStorage is not actively being referenced. Close the cache + // immediately. + auto cache_map_it = cache_map_.find(cache->cache_name()); + DCHECK(cache_map_it != cache_map_.end()); + + cache_map_it->second.reset(); +} + void LegacyCacheStorage::StartAsyncOperationForTesting() { scheduler_->ScheduleOperation(CacheStorageSchedulerOp::kTest, base::DoNothing()); @@ -1320,38 +1354,4 @@ } } -void LegacyCacheStorage::CacheUnreferenced(LegacyCacheStorageCache* cache) { - DCHECK(cache); - DCHECK(cache->IsUnreferenced()); - auto doomed_caches_it = doomed_caches_.find(cache); - if (doomed_caches_it != doomed_caches_.end()) { - // The last reference to a doomed cache is gone, perform clean up. - DeleteCacheFinalize(cache); - return; - } - - // Opportunistically keep warmed caches open when the CacheStorage is - // still actively referenced. Repeatedly opening and closing simple - // disk_cache backends can be quite slow. This is easy to trigger when - // a site uses caches.match() frequently because the a Cache object is - // never exposed to script to explicitly hold the backend open. - if (handle_ref_count_) - return; - - // The CacheStorage is not actively being referenced. Close the cache - // immediately. - auto cache_map_it = cache_map_.find(cache->cache_name()); - DCHECK(cache_map_it != cache_map_.end()); - - cache_map_it->second.reset(); -} - -void LegacyCacheStorage::ReleaseUnreferencedCaches() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - for (auto& entry : cache_map_) { - if (entry.second && entry.second->IsUnreferenced()) - entry.second.reset(); - } -} - } // namespace content
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.h b/content/browser/cache_storage/legacy/legacy_cache_storage.h index 8a8e1244..fdb21f7 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage.h +++ b/content/browser/cache_storage/legacy/legacy_cache_storage.h
@@ -136,6 +136,10 @@ return static_cast<LegacyCacheStorage*>(handle.value()); } + protected: + // Virtual for testing + virtual void CacheUnreferenced(LegacyCacheStorageCache* cache); + private: friend class LegacyCacheStorageCache; friend class cache_storage_manager_unittest::CacheStorageManagerTest; @@ -254,8 +258,6 @@ bool InitiateScheduledIndexWriteForTest( base::OnceCallback<void(bool)> callback); - void CacheUnreferenced(LegacyCacheStorageCache* cache); - // Whether or not we've loaded the list of cache names into memory. bool initialized_; bool initializing_;
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc index 6c5be33f..0a5299b 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc +++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -1287,6 +1287,10 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + callback = WrapCallbackWithHandle(std::move(callback)); + QueryCache(std::move(request), std::move(options), QUERY_CACHE_REQUESTS | QUERY_CACHE_RESPONSES_WITH_BODIES, base::BindOnce(&LegacyCacheStorageCache::MatchAllDidQueryCache, @@ -1370,6 +1374,10 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + callback = WrapCallbackWithHandle(std::move(callback)); + std::unique_ptr<disk_cache::Entry*> scoped_entry_ptr( new disk_cache::Entry*()); disk_cache::Entry** entry_ptr = scoped_entry_ptr.get(); @@ -1538,6 +1546,11 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + put_context->callback = + WrapCallbackWithHandle(std::move(put_context->callback)); + // Explicitly delete the incumbent resource (which may not exist). This is // only done so that it's padding will be decremented from the calculated // cache padding. @@ -1923,6 +1936,10 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + callback = WrapCallbackWithHandle(std::move(callback)); + QueryCache( std::move(request), std::move(options), QUERY_CACHE_REQUESTS | QUERY_CACHE_RESPONSES_WITH_BODIES, @@ -1988,6 +2005,10 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + callback = WrapCallbackWithHandle(std::move(callback)); + QueryCache( std::move(request), std::move(match_options), QUERY_CACHE_ENTRIES | QUERY_CACHE_RESPONSES_NO_BODIES, @@ -2041,6 +2062,10 @@ return; } + // Hold the cache alive while performing any operation touching the + // disk_cache backend. + callback = WrapCallbackWithHandle(std::move(callback)); + QueryCache(std::move(request), std::move(options), QUERY_CACHE_REQUESTS, base::BindOnce(&LegacyCacheStorageCache::KeysDidQueryCache, weak_ptr_factory_.GetWeakPtr(), std::move(callback),
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h index 065bdc4f5..2a24559 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h +++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.h
@@ -458,6 +458,32 @@ base::CheckedNumeric<uint64_t> CalculateRequiredSafeSpaceForResponse( const blink::mojom::FetchAPIResponsePtr& response); + // Wrap |callback| in order to reference a CacheStorageCacheHandle + // for the duration of an asynchronous operation. We must keep this + // self reference for a couple reasons. First, we must allow any writes + // to cleanly complete in order to avoid truncated entries. In addition, + // we must keep the cache and its disk_cache backend alive until all + // open Entry objects are destroyed to avoid having a second backend + // opened by another CacheStorageCache clobbering the entries. + template <typename... Args> + base::OnceCallback<void(Args...)> WrapCallbackWithHandle( + base::OnceCallback<void(Args...)> callback) { + return base::BindOnce(&LegacyCacheStorageCache::RunWithHandle<Args...>, + weak_ptr_factory_.GetWeakPtr(), CreateHandle(), + std::move(callback)); + } + + // Invoked by wrapped callbacks with the CacheStorageCacheHandle passed + // as a parameter. The handle is kept alive here simply to maintain + // a self-reference during the operation. + template <typename... Args> + void RunWithHandle(CacheStorageCacheHandle handle, + base::OnceCallback<void(Args...)> callback, + Args... args) { + std::move(callback).Run(std::forward<Args>(args)...); + // |handle| is destroyed after running the inner wrapped callback. + } + // Be sure to check |backend_state_| before use. std::unique_ptr<disk_cache::Backend> backend_;
diff --git a/content/browser/compositor/gpu_output_surface_mac.cc b/content/browser/compositor/gpu_output_surface_mac.cc deleted file mode 100644 index eeace7b4..0000000 --- a/content/browser/compositor/gpu_output_surface_mac.cc +++ /dev/null
@@ -1,30 +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 "content/browser/compositor/gpu_output_surface_mac.h" - -#include "components/viz/service/display/output_surface_client.h" -#include "components/viz/service/display/output_surface_frame.h" -#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "services/ws/public/cpp/gpu/context_provider_command_buffer.h" - -namespace content { - -GpuOutputSurfaceMac::GpuOutputSurfaceMac( - scoped_refptr<ws::ContextProviderCommandBuffer> context, - gpu::SurfaceHandle surface_handle, - std::unique_ptr<viz::CompositorOverlayCandidateValidator> - overlay_candidate_validator, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) - : GpuSurfacelessBrowserCompositorOutputSurface( - std::move(context), - surface_handle, - std::move(overlay_candidate_validator), - gfx::BufferFormat::RGBA_8888, - gpu_memory_buffer_manager) {} - -GpuOutputSurfaceMac::~GpuOutputSurfaceMac() {} - -} // namespace content
diff --git a/content/browser/compositor/gpu_output_surface_mac.h b/content/browser/compositor/gpu_output_surface_mac.h deleted file mode 100644 index b9e184f..0000000 --- a/content/browser/compositor/gpu_output_surface_mac.h +++ /dev/null
@@ -1,30 +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 CONTENT_BROWSER_COMPOSITOR_GPU_OUTPUT_SURFACE_MAC_H_ -#define CONTENT_BROWSER_COMPOSITOR_GPU_OUTPUT_SURFACE_MAC_H_ - -#include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h" - -#include "ui/gfx/native_widget_types.h" - -namespace content { - -class GpuOutputSurfaceMac - : public GpuSurfacelessBrowserCompositorOutputSurface { - public: - GpuOutputSurfaceMac(scoped_refptr<ws::ContextProviderCommandBuffer> context, - gpu::SurfaceHandle surface_handle, - std::unique_ptr<viz::CompositorOverlayCandidateValidator> - overlay_candidate_validator, - gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager); - ~GpuOutputSurfaceMac() override; - - private: - DISALLOW_COPY_AND_ASSIGN(GpuOutputSurfaceMac); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_COMPOSITOR_GPU_OUTPUT_SURFACE_MAC_H_
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc index d60aefc..54c922b 100644 --- a/content/browser/compositor/gpu_process_transport_factory.cc +++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -76,19 +76,7 @@ #include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" -#if defined(USE_AURA) -#include "content/public/common/service_manager_connection.h" -#include "ui/aura/env.h" -#include "ui/aura/window_tree_host.h" -#endif - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h" -#include "components/viz/service/display_embedder/output_device_backing.h" -#include "components/viz/service/display_embedder/software_output_device_win.h" -#include "ui/gfx/win/rendering_window_manager.h" -#elif defined(USE_OZONE) +#if defined(USE_OZONE) #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h" #include "components/viz/service/display_embedder/software_output_device_ozone.h" #include "ui/ozone/public/overlay_candidates_ozone.h" @@ -100,12 +88,6 @@ #include "ui/ozone/public/surface_ozone_canvas.h" #elif defined(USE_X11) #include "components/viz/service/display_embedder/software_output_device_x11.h" -#elif defined(OS_MACOSX) -#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_mac.h" -#include "components/viz/service/display_embedder/software_output_device_mac.h" -#include "content/browser/compositor/gpu_output_surface_mac.h" -#include "ui/base/cocoa/remote_layer_api.h" -#include "ui/base/ui_base_switches.h" #endif #if !defined(GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW) #include "gpu/ipc/common/gpu_surface_tracker.h" @@ -131,13 +113,6 @@ constexpr gpu::SchedulingPriority kStreamPriority = content::kGpuStreamPriorityUI; -#if defined(OS_MACOSX) -bool IsCALayersDisabledFromCommandLine() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - return command_line->HasSwitch(switches::kDisableMacOverlays); -} -#endif - } // namespace namespace content { @@ -187,9 +162,6 @@ task_graph_runner_->Start("CompositorTileWorker1", base::SimpleThread::Options()); -#if defined(OS_WIN) - software_backing_ = std::make_unique<viz::OutputDeviceBacking>(); -#endif if (command_line->HasSwitch(switches::kDisableGpu) || command_line->HasSwitch(switches::kDisableGpuCompositing)) { @@ -218,9 +190,7 @@ return base::WrapUnique(new viz::SoftwareOutputDevice); DCHECK_CURRENTLY_ON(BrowserThread::UI); -#if defined(OS_WIN) - return CreateSoftwareOutputDeviceWinBrowser(widget, software_backing_.get()); -#elif defined(USE_OZONE) +#if defined(USE_OZONE) ui::SurfaceFactoryOzone* factory = ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone(); std::unique_ptr<ui::PlatformWindowSurface> platform_window_surface = @@ -232,8 +202,6 @@ std::move(platform_window_surface), std::move(surface_ozone)); #elif defined(USE_X11) return std::make_unique<viz::SoftwareOutputDeviceX11>(widget); -#elif defined(OS_MACOSX) - return std::make_unique<viz::SoftwareOutputDeviceMac>(std::move(task_runner)); #else NOTREACHED(); return std::unique_ptr<viz::SoftwareOutputDevice>(); @@ -241,13 +209,7 @@ } std::unique_ptr<viz::CompositorOverlayCandidateValidator> -CreateOverlayCandidateValidator( -#if defined(OS_MACOSX) - gfx::AcceleratedWidget widget, - bool disable_overlay_ca_layers) { -#else - gfx::AcceleratedWidget widget) { -#endif +CreateOverlayCandidateValidator(gfx::AcceleratedWidget widget) { std::unique_ptr<viz::CompositorOverlayCandidateValidator> validator; #if defined(USE_OZONE) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -269,18 +231,6 @@ std::move(overlay_candidates), viz::ParseOverlayStategies(enable_overlay_flag))); } -#elif defined(OS_MACOSX) - // Overlays are only supported through the remote layer API. - if (ui::RemoteLayerAPISupported()) { - static bool overlays_disabled_at_command_line = - IsCALayersDisabledFromCommandLine(); - const bool ca_layers_disabled = - overlays_disabled_at_command_line || disable_overlay_ca_layers; - validator.reset( - new viz::CompositorOverlayCandidateValidatorMac(ca_layers_disabled)); - } -#elif defined(OS_WIN) - validator = std::make_unique<viz::CompositorOverlayCandidateValidatorWin>(); #endif return validator; @@ -299,11 +249,6 @@ data->display_output_surface = nullptr; } -#if defined(OS_WIN) - gfx::RenderingWindowManager::GetInstance()->UnregisterParent( - compositor->widget()); -#endif - const bool use_gpu_compositing = !compositor->force_software_compositor() && !is_gpu_compositing_disabled_; if (use_gpu_compositing) { @@ -350,11 +295,6 @@ support_stencil = true; #endif -#if defined(OS_WIN) - gfx::RenderingWindowManager::GetInstance()->RegisterParent( - compositor->widget()); -#endif - scoped_refptr<ws::ContextProviderCommandBuffer> context_provider; if (!use_gpu_compositing) { @@ -455,16 +395,6 @@ context_provider, std::unique_ptr<viz::CompositorOverlayCandidateValidator>()); } else if (capabilities.surfaceless) { -#if defined(OS_MACOSX) - const auto& gpu_feature_info = context_provider->GetGpuFeatureInfo(); - bool disable_overlay_ca_layers = - gpu_feature_info.IsWorkaroundEnabled(gpu::DISABLE_OVERLAY_CA_LAYERS); - display_output_surface = std::make_unique<GpuOutputSurfaceMac>( - context_provider, data->surface_handle, - CreateOverlayCandidateValidator(compositor->widget(), - disable_overlay_ca_layers), - GetGpuMemoryBufferManager()); -#else DCHECK(capabilities.texture_format_bgra8888); auto gpu_output_surface = std::make_unique<GpuSurfacelessBrowserCompositorOutputSurface>( @@ -473,20 +403,9 @@ display::DisplaySnapshot::PrimaryFormat(), GetGpuMemoryBufferManager()); display_output_surface = std::move(gpu_output_surface); -#endif } else { - std::unique_ptr<viz::CompositorOverlayCandidateValidator> validator; -#if defined(OS_WIN) - const bool use_overlays_for_sw_protected_video = - base::FeatureList::IsEnabled( - features::kUseDCOverlaysForSoftwareProtectedVideo); - if (capabilities.dc_layers && (capabilities.use_dc_overlays_for_video || - use_overlays_for_sw_protected_video)) - validator = CreateOverlayCandidateValidator(compositor->widget()); -#elif !defined(OS_MACOSX) - // Overlays are only supported on surfaceless output surfaces on Mac. - validator = CreateOverlayCandidateValidator(compositor->widget()); -#endif + std::unique_ptr<viz::CompositorOverlayCandidateValidator> validator = + CreateOverlayCandidateValidator(compositor->widget()); auto gpu_output_surface = std::make_unique<GpuBrowserCompositorOutputSurface>( context_provider, std::move(validator)); @@ -684,10 +603,6 @@ for (auto& observer : observer_list_) observer.OnLostSharedContext(); } -#if defined(OS_WIN) - gfx::RenderingWindowManager::GetInstance()->UnregisterParent( - compositor->widget()); -#endif } gpu::GpuMemoryBufferManager*
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h index 4a0b346..4a78bde 100644 --- a/content/browser/compositor/gpu_process_transport_factory.h +++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -40,7 +40,6 @@ namespace viz { class CompositingModeReporterImpl; -class OutputDeviceBacking; class RasterContextProvider; class ServerSharedBitmapManager; class SoftwareOutputDevice; @@ -146,11 +145,6 @@ viz::FrameSinkIdAllocator frame_sink_id_allocator_; -#if defined(OS_WIN) - // Used by output surface, stored in PerCompositorData. - std::unique_ptr<viz::OutputDeviceBacking> software_backing_; -#endif - // Depends on SurfaceManager. typedef std::map<ui::Compositor*, std::unique_ptr<PerCompositorData>> PerCompositorDataMap;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index ab32cec..27187fd 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -769,7 +769,6 @@ frame_tree_(frame_tree), frame_tree_node_(frame_tree_node), parent_(nullptr), - render_widget_host_(nullptr), routing_id_(routing_id), is_waiting_for_swapout_ack_(false), render_frame_created_(false), @@ -837,12 +836,29 @@ mojom::WidgetPtr widget; GetRemoteInterfaces()->GetInterface(&widget); - // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main - // render frame should probably start owning the RenderWidgetHostImpl, - // so this logic checking for an already existing RWHI should be removed. - // https://crbug.com/545684 - render_widget_host_ = - RenderWidgetHostImpl::FromID(GetProcess()->GetID(), widget_routing_id); + if (!parent_) { + // For main frames, the RenderWidgetHost is owned by the RenderViewHost. + // TODO(https://crbug.com/545684): Once RenderViewHostImpl has-a + // RenderWidgetHostImpl, the main render frame should probably start + // owning the RenderWidgetHostImpl itself. + DCHECK(GetLocalRenderWidgetHost()); + DCHECK_EQ(GetLocalRenderWidgetHost()->GetRoutingID(), widget_routing_id); + DCHECK(!GetLocalRenderWidgetHost()->owned_by_render_frame_host()); + + // Make the RenderWidgetHostImpl able to call the mojo Widget interface + // (implemented by the RenderWidgetImpl). + GetLocalRenderWidgetHost()->SetWidget(std::move(widget)); + } else { + // For subframes, the RenderFrameHost directly creates and owns its + // RenderWidgetHost. + DCHECK_EQ(nullptr, GetLocalRenderWidgetHost()); + DCHECK_EQ(nullptr, RenderWidgetHostImpl::FromID(GetProcess()->GetID(), + widget_routing_id)); + owned_render_widget_host_ = RenderWidgetHostFactory::Create( + frame_tree_->render_widget_delegate(), GetProcess(), + widget_routing_id, std::move(widget), hidden); + owned_render_widget_host_->set_owned_by_render_frame_host(true); + } mojom::WidgetInputHandlerAssociatedPtr widget_handler; mojom::WidgetInputHandlerHostRequest host_request; @@ -852,22 +868,12 @@ frame_input_handler_->GetWidgetInputHandler( mojo::MakeRequest(&widget_handler), std::move(host)); } - if (!render_widget_host_) { - DCHECK(frame_tree_node->parent()); - render_widget_host_ = RenderWidgetHostFactory::Create( - frame_tree_->render_widget_delegate(), GetProcess(), - widget_routing_id, std::move(widget), hidden); - render_widget_host_->set_owned_by_render_frame_host(true); - } else { - DCHECK(!render_widget_host_->owned_by_render_frame_host()); - render_widget_host_->SetWidget(std::move(widget)); - } if (!frame_tree_node_->parent()) - render_widget_host_->SetIntersectsViewport(true); - render_widget_host_->SetFrameDepth(frame_tree_node_->depth()); - render_widget_host_->SetWidgetInputHandler(std::move(widget_handler), - std::move(host_request)); - render_widget_host_->input_router()->SetFrameTreeNodeId( + GetLocalRenderWidgetHost()->SetIntersectsViewport(true); + GetLocalRenderWidgetHost()->SetFrameDepth(frame_tree_node_->depth()); + GetLocalRenderWidgetHost()->SetWidgetInputHandler(std::move(widget_handler), + std::move(host_request)); + GetLocalRenderWidgetHost()->input_router()->SetFrameTreeNodeId( frame_tree_node_->frame_tree_node_id()); } ResetFeaturePolicy(); @@ -994,11 +1000,11 @@ for (auto& iter : visual_state_callbacks_) std::move(iter.second).Run(false); - if (render_widget_host_ && - render_widget_host_->owned_by_render_frame_host()) { - // Shutdown causes the RenderWidgetHost to delete itself. - render_widget_host_->ShutdownAndDestroyWidget(true); - } + // Note: The RenderWidgetHost of the main frame is owned by the RenderViewHost + // instead. In this case the RenderViewHost is responsible for shutting down + // its RenderViewHost. + if (owned_render_widget_host_) + owned_render_widget_host_->ShutdownAndDestroyWidget(false); // Notify the FrameTree that this RFH is going away, allowing it to shut down // the corresponding RenderViewHost if it is no longer needed. @@ -1333,7 +1339,7 @@ // https://crbug.com/615867. RenderFrameHostImpl* frame = this; while (frame) { - if (frame->render_widget_host_) + if (frame->GetLocalRenderWidgetHost()) break; frame = frame->GetParent(); } @@ -1567,10 +1573,8 @@ void RenderFrameHostImpl::RenderProcessGone(SiteInstanceImpl* site_instance) { DCHECK_EQ(site_instance_.get(), site_instance); - if (render_widget_host_ && - render_widget_host_->owned_by_render_frame_host()) { - render_widget_host_->RendererExited(); - } + if (owned_render_widget_host_) + owned_render_widget_host_->RendererExited(); // The renderer process is gone, so this frame can no longer be loading. if (GetNavigationHandle()) @@ -1710,9 +1714,10 @@ frame_tree_node()->has_committed_real_load(); params->widget_params = mojom::CreateFrameWidgetParams::New(); - if (render_widget_host_) { - params->widget_params->routing_id = render_widget_host_->GetRoutingID(); - params->widget_params->hidden = render_widget_host_->is_hidden(); + if (GetLocalRenderWidgetHost()) { + params->widget_params->routing_id = + GetLocalRenderWidgetHost()->GetRoutingID(); + params->widget_params->hidden = GetLocalRenderWidgetHost()->is_hidden(); } else { // MSG_ROUTING_NONE will prevent a new RenderWidget from being created in // the renderer process. @@ -1727,9 +1732,11 @@ // TODO(avi): This will need to change to initialize a // RenderWidgetHostViewAura for the main frame once RenderViewHostImpl has-a // RenderWidgetHostImpl. https://crbug.com/545684 - if (parent_routing_id != MSG_ROUTING_NONE && render_widget_host_) { + if (owned_render_widget_host_) { + DCHECK(parent_); + DCHECK_NE(parent_routing_id, MSG_ROUTING_NONE); RenderWidgetHostView* rwhv = - RenderWidgetHostViewChildFrame::Create(render_widget_host_); + RenderWidgetHostViewChildFrame::Create(owned_render_widget_host_.get()); rwhv->Hide(); } @@ -1795,10 +1802,10 @@ } } - if (created && render_widget_host_) { + if (created && GetLocalRenderWidgetHost()) { mojom::WidgetPtr widget; GetRemoteInterfaces()->GetInterface(&widget); - render_widget_host_->SetWidget(std::move(widget)); + GetLocalRenderWidgetHost()->SetWidget(std::move(widget)); if (frame_input_handler_) { mojom::WidgetInputHandlerAssociatedPtr widget_handler; @@ -1807,16 +1814,17 @@ mojo::MakeRequest(&host); frame_input_handler_->GetWidgetInputHandler( mojo::MakeRequest(&widget_handler), std::move(host)); - render_widget_host_->SetWidgetInputHandler(std::move(widget_handler), - std::move(host_request)); + GetLocalRenderWidgetHost()->SetWidgetInputHandler( + std::move(widget_handler), std::move(host_request)); } - render_widget_host_->input_router()->SetFrameTreeNodeId( + GetLocalRenderWidgetHost()->input_router()->SetFrameTreeNodeId( frame_tree_node_->frame_tree_node_id()); viz::mojom::InputTargetClientPtr input_target_client; remote_interfaces_->GetInterface(&input_target_client); input_target_client_ = input_target_client.get(); - render_widget_host_->SetInputTargetClient(std::move(input_target_client)); - render_widget_host_->InitForFrame(); + GetLocalRenderWidgetHost()->SetInputTargetClient( + std::move(input_target_client)); + GetLocalRenderWidgetHost()->InitForFrame(); } if (enabled_bindings_ && created) { @@ -2080,6 +2088,11 @@ // descendant frames to execute unload handlers. Start executing those // handlers now. StartPendingDeletionOnSubtree(); + + // Ensure that deleted subframes are not visible from the others processes + // anymore. + frame_tree_node_->render_manager()->ResetProxyHosts(); + // Some children with no unload handler may be eligible for immediate // deletion. Cut the dead branches now. This is a performance optimization. PendingDeletionCheckCompletedOnSubtree(); // Can delete |this|. @@ -2270,8 +2283,8 @@ RenderWidgetHostImpl* RenderFrameHostImpl::GetRenderWidgetHost() { RenderFrameHostImpl* frame = this; while (frame) { - if (frame->render_widget_host_) - return frame->render_widget_host_; + if (frame->GetLocalRenderWidgetHost()) + return frame->GetLocalRenderWidgetHost(); frame = frame->GetParent(); } @@ -2372,9 +2385,14 @@ // Start pending deletion on this frame and its children. DeleteRenderFrame(); StartPendingDeletionOnSubtree(); + + // Ensure that deleted subframes are not visible from the others processes + // anymore. + frame_tree_node()->render_manager()->ResetProxyHosts(); + // Some children with no unload handler may be eligible for immediate // deletion. Cut the dead branches now. This is a performance optimization. - PendingDeletionCheckCompletedOnSubtree(); + PendingDeletionCheckCompletedOnSubtree(); // May delete |this|. } void RenderFrameHostImpl::OnBeforeUnloadACK( @@ -4514,6 +4532,10 @@ ? UnloadState::InProgress : UnloadState::Completed; } + + // Ensure that deleted subframes are not visible from the others processes + // anymore. + node->render_manager()->ResetProxyHosts(); } } } @@ -6958,4 +6980,11 @@ return prefetched_signed_exchange_cache_; } +RenderWidgetHostImpl* RenderFrameHostImpl::GetLocalRenderWidgetHost() const { + if (!parent_) + return render_view_host_->GetWidget(); + else + return owned_render_widget_host_.get(); +} + } // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index ed3656c2..61a04da 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -435,7 +435,7 @@ // distinguished by owning a RenderWidgetHost, which manages input events // and painting for this frame and its contiguous local subtree in the // renderer process. - bool is_local_root() const { return !!render_widget_host_; } + bool is_local_root() const { return !!GetLocalRenderWidgetHost(); } // Returns the RenderWidgetHostImpl attached to this frame or the nearest // ancestor frame, which could potentially be the root. For most input @@ -1055,6 +1055,12 @@ NavigationCommitInIframePendingDeletionABC); FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, CommittedOriginIncompatibleWithOriginLock); + FRIEND_TEST_ALL_PREFIXES( + SitePerProcessBrowserTest, + IsDetachedSubframeObservableDuringUnloadHandlerSameProcess); + FRIEND_TEST_ALL_PREFIXES( + SitePerProcessBrowserTest, + IsDetachedSubframeObservableDuringUnloadHandlerCrossProcess); class DroppedInterfaceRequestLogger; @@ -1586,6 +1592,15 @@ FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params, mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params); + // If this RenderFrameHost is a local root (i.e., either the main frame or a + // subframe in a different process than its parent), this returns the + // RenderWidgetHost corresponding to this frame. Otherwise this returns null. + // See also GetRenderWidgetHost(), which walks up the tree to find the nearest + // local root. + // Main frame: RenderWidgetHost is owned by the RenderViewHost. + // Subframe: RenderWidgetHost is owned by this RenderFrameHost. + RenderWidgetHostImpl* GetLocalRenderWidgetHost() const; + // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a // refcount that calls Shutdown when it reaches zero. This allows each // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring @@ -1643,13 +1658,11 @@ std::map<uint64_t, VisualStateCallback> visual_state_callbacks_; - // RenderFrameHosts that need management of the rendering and input events - // for their frame subtrees require RenderWidgetHosts. This typically - // means frames that are rendered in different processes from their parent - // frames. + // Local root subframes directly own their RenderWidgetHost. + // Please see comments about the GetLocalRenderWidgetHost() function. // TODO(kenrb): Later this will also be used on the top-level frame, when // RenderFrameHost owns its RenderViewHost. - RenderWidgetHostImpl* render_widget_host_; + std::unique_ptr<RenderWidgetHostImpl> owned_render_widget_host_; const int routing_id_;
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc index 6bbc8dc..72d06e66 100644 --- a/content/browser/frame_host/render_frame_proxy_host.cc +++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -168,6 +168,13 @@ bool RenderFrameProxyHost::InitRenderFrameProxy() { DCHECK(!render_frame_proxy_created_); + // If the current RenderFrameHost is pending deletion, no new proxies should + // be created for it, since this frame should no longer be visible from other + // processes. We can get here with postMessage while trying to recreate + // proxies for the sender. + if (!frame_tree_node_->current_frame_host()->is_active()) + return false; + // It is possible to reach this when the process is dead (in particular, when // creating proxies from CreateProxiesForChildFrame). In that case, don't // create the proxy. The process shouldn't be resurrected just to create
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc index ef2d53d..71c6efd 100644 --- a/content/browser/media/capture/desktop_capture_device.cc +++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -428,6 +428,8 @@ } } + // TODO(julien.isorce): Pass color space of the captured raw data, see + // http://crbug.com/945468. base::TimeTicks now = NowTicks(); if (first_ref_time_.is_null()) first_ref_time_ = now; @@ -436,7 +438,7 @@ media::VideoCaptureFormat( gfx::Size(output_size.width(), output_size.height()), requested_frame_rate_, media::PIXEL_FORMAT_ARGB), - 0, now, now - first_ref_time_); + gfx::ColorSpace(), 0, now, now - first_ref_time_); } void DesktopCaptureDevice::Core::OnCaptureTimer() {
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc index 3a596dc..9c31de7 100644 --- a/content/browser/media/capture/desktop_capture_device_unittest.cc +++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -218,6 +218,7 @@ void CopyFrame(const uint8_t* frame, int size, const media::VideoCaptureFormat&, + const gfx::ColorSpace&, int, base::TimeTicks, base::TimeDelta, @@ -269,7 +270,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(SaveArg<1>(&frame_size), SaveArg<2>(&format), InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); @@ -308,7 +309,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -354,7 +355,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -404,7 +405,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(WithArg<2>(Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), @@ -456,7 +457,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame), SaveArg<1>(&frame_size), @@ -505,7 +506,7 @@ CreateMockVideoCaptureDeviceClient()); EXPECT_CALL(*client, OnError(_, _, _)).Times(0); EXPECT_CALL(*client, OnStarted()); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly( DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame), SaveArg<1>(&frame_size), @@ -571,12 +572,12 @@ task_runner, task_runner->GetMockTickClock()); })); - EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly(DoAll( WithArg<2>( Invoke(&format_checker, &FormatChecker::ExpectAcceptableSize)), - WithArg<5>(Invoke([&done_event, &nb_frames, &task_runner] - (base::TimeDelta timestamp) { + WithArg<6>(Invoke([&done_event, &nb_frames, + &task_runner](base::TimeDelta timestamp) { ++nb_frames; // Simulate real device capture time. Indeed the time spent
diff --git a/content/browser/media/capture/screen_capture_device_android_unittest.cc b/content/browser/media/capture/screen_capture_device_android_unittest.cc index ca204dd..30c5fe70 100644 --- a/content/browser/media/capture/screen_capture_device_android_unittest.cc +++ b/content/browser/media/capture/screen_capture_device_android_unittest.cc
@@ -17,10 +17,11 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client { public: - MOCK_METHOD7(OnIncomingCapturedData, + MOCK_METHOD8(OnIncomingCapturedData, void(const uint8_t* data, int length, const media::VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int rotation, base::TimeTicks reference_time, base::TimeDelta tiemstamp,
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc index 7579e0a..08b3797c 100644 --- a/content/browser/native_file_system/file_system_chooser_browsertest.cc +++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc index 190b319..1693e74 100644 --- a/content/browser/network_service_browsertest.cc +++ b/content/browser/network_service_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/files/file_util.h" #include "base/memory/ref_counted_memory.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/renderer_host/input/mouse_latency_browsertest.cc b/content/browser/renderer_host/input/mouse_latency_browsertest.cc index 3d6f61c..929ed1c 100644 --- a/content/browser/renderer_host/input/mouse_latency_browsertest.cc +++ b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
@@ -135,14 +135,14 @@ RenderWidgetHostFactory::UnregisterFactory(); } - RenderWidgetHostImpl* CreateRenderWidgetHost( + std::unique_ptr<RenderWidgetHostImpl> CreateRenderWidgetHost( RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id, mojom::WidgetPtr widget_interface, bool hidden) override { - return new TracingRenderWidgetHost(delegate, process, routing_id, - std::move(widget_interface), hidden); + return std::make_unique<TracingRenderWidgetHost>( + delegate, process, routing_id, std::move(widget_interface), hidden); } private:
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc index 9d8035c5..ad1583b 100644 --- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc +++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -91,6 +91,7 @@ int buffer_id, const media::mojom::VideoFrameInfoPtr& frame_info) override { EXPECT_EQ(expected_pixel_format_, frame_info->pixel_format); + EXPECT_EQ(expected_color_space_, frame_info->color_space); media::VideoFrameMetadata metadata; metadata.MergeInternalValuesFrom(frame_info->metadata); base::TimeTicks reference_time; @@ -115,6 +116,7 @@ VideoCaptureController* controller_; media::VideoPixelFormat expected_pixel_format_ = media::PIXEL_FORMAT_I420; + gfx::ColorSpace expected_color_space_ = gfx::ColorSpace::CreateREC709(); double resource_utilization_; bool enable_auto_return_buffer_on_buffer_ready_ = true; }; @@ -129,6 +131,7 @@ public: VideoCaptureControllerTest() : arbitrary_format_(gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420), + arbitrary_color_space_(gfx::ColorSpace::CreateREC709()), arbitrary_route_id_(0x99), arbitrary_session_id_(100) {} ~VideoCaptureControllerTest() override {} @@ -180,7 +183,8 @@ #endif // defined(OS_CHROMEOS) } - void SendStubFrameToDeviceClient(const media::VideoCaptureFormat format) { + void SendStubFrameToDeviceClient(const media::VideoCaptureFormat format, + const gfx::ColorSpace& color_space) { auto stub_frame = media::VideoFrame::CreateZeroInitializedFrame( format.pixel_format, format.frame_size, gfx::Rect(format.frame_size.width(), format.frame_size.height()), @@ -191,7 +195,7 @@ stub_frame->data(0), media::VideoFrame::AllocationSize(stub_frame->format(), stub_frame->coded_size()), - format, rotation, base::TimeTicks(), base::TimeDelta(), + format, color_space, rotation, base::TimeTicks(), base::TimeDelta(), frame_feedback_id); } @@ -206,6 +210,7 @@ const base::TimeTicks arbitrary_reference_time_ = base::TimeTicks(); const base::TimeDelta arbitrary_timestamp_ = base::TimeDelta(); const media::VideoCaptureFormat arbitrary_format_; + const gfx::ColorSpace arbitrary_color_space_; const VideoCaptureControllerID arbitrary_route_id_; const media::VideoCaptureSessionId arbitrary_session_id_; @@ -302,6 +307,10 @@ const media::VideoPixelFormat format = GetParam(); client_a_->expected_pixel_format_ = format; client_b_->expected_pixel_format_ = format; + // OnIncomingCapturedBuffer keeps the color space unset. If needed use + // OnIncomingCapturedBufferExt. + client_a_->expected_color_space_ = gfx::ColorSpace(); + client_b_->expected_color_space_ = gfx::ColorSpace(); session_100.requested_format = media::VideoCaptureFormat(gfx::Size(320, 240), 30, format); @@ -627,6 +636,9 @@ const int kTestFrameSequenceLength = 10; media::VideoCaptureFormat arbitrary_format( gfx::Size(320, 240), arbitrary_frame_rate_, media::PIXEL_FORMAT_I420); + // OnIncomingCapturedBuffer keeps the color space unset. If needed use + // OnIncomingCapturedBufferExt. + client_a_->expected_color_space_ = gfx::ColorSpace(); // Register |client_a_| at |controller_|. media::VideoCaptureParams session_100; @@ -707,7 +719,7 @@ EXPECT_CALL(*client_a_, DoBufferReady(_, _)).Times(1); } EXPECT_CALL(*client_a_, DoBufferDestroyed(_, _)).Times(0); - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -737,7 +749,7 @@ .WillOnce(SaveArg<1>(&buffer_id_reported_to_client)); EXPECT_CALL(*client_a_, DoBufferReady(_, _)).Times(1); } - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -777,7 +789,7 @@ .WillOnce(SaveArg<1>(&first_buffer_id)); EXPECT_CALL(*client_a_, DoBufferReady(_, _)).Times(1); } - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -799,7 +811,7 @@ .WillOnce(SaveArg<1>(&second_buffer_id)); EXPECT_CALL(*client_a_, DoBufferReady(_, _)).Times(1); } - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); Mock::VerifyAndClearExpectations(client_a_.get()); @@ -920,7 +932,7 @@ media::VideoCaptureFrameDropReason::kDeviceClientFrameHasInvalidFormat); } - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); for (int i = 0; @@ -948,7 +960,7 @@ media::VideoCaptureFrameDropReason::kDeviceClientFrameHasInvalidFormat); } - SendStubFrameToDeviceClient(arbitrary_format_); + SendStubFrameToDeviceClient(arbitrary_format_, arbitrary_color_space_); base::RunLoop().RunUntilIdle(); controller_->OnFrameDropped(
diff --git a/content/browser/renderer_host/render_view_host_factory.cc b/content/browser/renderer_host/render_view_host_factory.cc index a7532fa..9daf7a5 100644 --- a/content/browser/renderer_host/render_view_host_factory.cc +++ b/content/browser/renderer_host/render_view_host_factory.cc
@@ -53,9 +53,8 @@ } return new RenderViewHostImpl( instance, - base::WrapUnique(RenderWidgetHostFactory::Create( - widget_delegate, instance->GetProcess(), widget_routing_id, nullptr, - hidden)), + RenderWidgetHostFactory::Create(widget_delegate, instance->GetProcess(), + widget_routing_id, nullptr, hidden), delegate, routing_id, main_frame_routing_id, swapped_out, true /* has_initialized_audio_host */); }
diff --git a/content/browser/renderer_host/render_widget_host_factory.cc b/content/browser/renderer_host/render_widget_host_factory.cc index 2a0c4f1..bae8801 100644 --- a/content/browser/renderer_host/render_widget_host_factory.cc +++ b/content/browser/renderer_host/render_widget_host_factory.cc
@@ -12,7 +12,7 @@ RenderWidgetHostFactory* RenderWidgetHostFactory::factory_ = nullptr; // static -RenderWidgetHostImpl* RenderWidgetHostFactory::Create( +std::unique_ptr<RenderWidgetHostImpl> RenderWidgetHostFactory::Create( RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id, @@ -22,8 +22,8 @@ return factory_->CreateRenderWidgetHost( delegate, process, routing_id, std::move(widget_interface), hidden); } - return new RenderWidgetHostImpl(delegate, process, routing_id, - std::move(widget_interface), hidden); + return std::make_unique<RenderWidgetHostImpl>( + delegate, process, routing_id, std::move(widget_interface), hidden); } // static
diff --git a/content/browser/renderer_host/render_widget_host_factory.h b/content/browser/renderer_host/render_widget_host_factory.h index c691954..e197d53 100644 --- a/content/browser/renderer_host/render_widget_host_factory.h +++ b/content/browser/renderer_host/render_widget_host_factory.h
@@ -6,6 +6,7 @@ #define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_FACTORY_H_ #include <stdint.h> +#include <memory> #include "base/macros.h" #include "content/common/content_export.h" @@ -24,11 +25,12 @@ // Creates a RenderWidgetHostImpl using the currently registered factory, or // the default one if no factory is registered. Ownership of the returned // pointer will be passed to the caller. - static RenderWidgetHostImpl* Create(RenderWidgetHostDelegate* delegate, - RenderProcessHost* process, - int32_t routing_id, - mojom::WidgetPtr widget_interface, - bool hidden); + static std::unique_ptr<RenderWidgetHostImpl> Create( + RenderWidgetHostDelegate* delegate, + RenderProcessHost* process, + int32_t routing_id, + mojom::WidgetPtr widget_interface, + bool hidden); // Returns true if there is currently a globally-registered factory. static bool has_factory() { return !!factory_; } @@ -39,7 +41,7 @@ // You can derive from this class and specify an implementation for this // function to create a different kind of RenderWidgetHostImpl for testing. - virtual RenderWidgetHostImpl* CreateRenderWidgetHost( + virtual std::unique_ptr<RenderWidgetHostImpl> CreateRenderWidgetHost( RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id,
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 2fbc1201..1af409c 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -493,9 +493,6 @@ // Send updated vsync parameters to the top level display. void UpdateDisplayVSyncParameters(); - // Adds/Removes frame observer based on state. - void UpdateNeedsBeginFramesInternal(); - void SendSyntheticWheelEventWithPhaseEnded( blink::WebMouseWheelEvent wheel_event, bool should_route_event); @@ -575,9 +572,6 @@ // Display link for getting vsync info. scoped_refptr<ui::DisplayLinkMac> display_link_; - // Whether a request for begin frames has been issued. - bool needs_begin_frames_; - // Whether or not the background is opaque as determined by calls to // SetBackgroundColor. The default value is opaque. bool background_is_opaque_ = true;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index 1e853d5..01341832 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -87,7 +87,6 @@ // ProgressFling must get called for middle click autoscroll fling on Mac. if (host()) host()->ProgressFlingIfNeeded(frame_time); - UpdateNeedsBeginFramesInternal(); } void RenderWidgetHostViewMac::OnFrameTokenChanged(uint32_t frame_token) { @@ -212,8 +211,6 @@ GetFrameSinkId(), this); } - bool needs_begin_frames = true; - RenderWidgetHostOwnerDelegate* owner_delegate = host()->owner_delegate(); if (owner_delegate) { // TODO(mostynb): actually use prefs. Landing this as a separate CL @@ -221,29 +218,12 @@ // NOTE: This will not be run for child frame widgets, which do not have // an owner delegate and won't get a RenderViewHost here. ignore_result(owner_delegate->GetWebkitPreferencesForWidget()); - needs_begin_frames = !owner_delegate->IsNeverVisible(); } cursor_manager_.reset(new CursorManager(this)); if (GetTextInputManager()) GetTextInputManager()->AddObserver(this); - - // When Viz Display Compositor is not active, RenderWidgetHostViewMac is - // responsible for handling BeginFrames. - // - // Because of the way Mac pumps messages during resize, SetNeedsBeginFrame - // messages are not delayed on Mac. This leads to creation-time raciness - // where renderer sends a SetNeedsBeginFrame(true) before the renderer host is - // created to receive it. - // - // Any renderer that will produce frames needs to have begin frames sent to - // it. So unless it is never visible, start this value at true here to avoid - // startup raciness and decrease latency. - if (!features::IsVizDisplayCompositorEnabled()) { - needs_begin_frames_ = needs_begin_frames; - UpdateNeedsBeginFramesInternal(); - } } RenderWidgetHostViewMac::~RenderWidgetHostViewMac() { @@ -869,12 +849,7 @@ } void RenderWidgetHostViewMac::SetNeedsBeginFrames(bool needs_begin_frames) { - needs_begin_frames_ = needs_begin_frames; - UpdateNeedsBeginFramesInternal(); -} - -void RenderWidgetHostViewMac::UpdateNeedsBeginFramesInternal() { - browser_compositor_->SetNeedsBeginFrames(needs_begin_frames_); + NOTREACHED(); } void RenderWidgetHostViewMac::OnDidUpdateVisualPropertiesComplete(
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc index 18d61e1..01535ef 100644 --- a/content/browser/scheduler/browser_task_executor.cc +++ b/content/browser/scheduler/browser_task_executor.cc
@@ -12,6 +12,7 @@ #include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/browser_thread_impl.h" +#include "content/browser/scheduler/browser_task_queues.h" #include "content/browser/scheduler/browser_ui_thread_scheduler.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -23,7 +24,7 @@ namespace content { namespace { -using QueueType = content::BrowserUIThreadTaskQueue::QueueType; +using QueueType = content::BrowserTaskQueues::QueueType; // |g_browser_task_executor| is intentionally leaked on shutdown. BrowserTaskExecutor* g_browser_task_executor = nullptr; @@ -157,7 +158,8 @@ BrowserTaskExecutor::BrowserTaskExecutor( std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler) - : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) {} + : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)), + browser_ui_thread_handle_(browser_ui_thread_scheduler_->GetHandle()) {} BrowserTaskExecutor::~BrowserTaskExecutor() = default; @@ -190,7 +192,8 @@ // static void BrowserTaskExecutor::PostFeatureListSetup() { DCHECK(g_browser_task_executor); - g_browser_task_executor->browser_ui_thread_scheduler_->PostFeatureListSetup(); + g_browser_task_executor->browser_ui_thread_handle_ + .PostFeatureListInitializationSetup(); } // static @@ -199,15 +202,14 @@ return; DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_); - // We don't delete either |g_browser_task_executor| or the - // BrowserUIThreadScheduler it owns because other threads may PostTask or call - // BrowserTaskExecutor::GetTaskRunner while we're tearing things down. We - // don't want to add locks so we just leak instead of dealing with that. - // For similar reasons we don't need to call + // We don't delete either |g_browser_task_executor| because other threads may + // PostTask or call BrowserTaskExecutor::GetTaskRunner while we're tearing + // things down. We don't want to add locks so we just leak instead of dealing + // with that. For similar reasons we don't need to call // PostTaskAndroid::SignalNativeSchedulerShutdown on Android. In tests however // we need to clean up, so BrowserTaskExecutor::ResetForTesting should be // called. - g_browser_task_executor->browser_ui_thread_scheduler_->Shutdown(); + g_browser_task_executor->browser_ui_thread_scheduler_.reset(); } // static @@ -232,29 +234,27 @@ void BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting( BrowserThread::ID identifier) { DCHECK(g_browser_task_executor); - DCHECK(g_browser_task_executor->browser_ui_thread_scheduler_); - DCHECK_CURRENTLY_ON(BrowserThread::UI); + + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); switch (identifier) { case BrowserThread::UI: - g_browser_task_executor->browser_ui_thread_scheduler_ - ->RunAllPendingTasksForTesting(); + g_browser_task_executor->browser_ui_thread_handle_ + .ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); break; - case BrowserThread::IO: { + case BrowserThread::IO: // TODO(https://crbug/863341): Do something more clever once we have a // scheduler - base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO}, run_loop.QuitClosure()); - run_loop.Run(); break; - } case BrowserThread::ID_COUNT: NOTREACHED(); break; } + run_loop.Run(); } bool BrowserTaskExecutor::PostDelayedTaskWithTraits( @@ -328,13 +328,13 @@ switch (task_type) { case BrowserTaskType::kBootstrap: // Note we currently ignore the priority for bootstrap tasks. - return browser_ui_thread_scheduler_->GetTaskRunner(QueueType::kBootstrap); + return browser_ui_thread_handle_.task_runner(QueueType::kBootstrap); case BrowserTaskType::kNavigation: case BrowserTaskType::kPreconnect: // Note we currently ignore the priority for navigation and preconnection // tasks. - return browser_ui_thread_scheduler_->GetTaskRunner( + return browser_ui_thread_handle_.task_runner( QueueType::kNavigationAndPreconnection); case BrowserTaskType::kDefault: @@ -347,15 +347,13 @@ switch (traits.priority()) { case base::TaskPriority::BEST_EFFORT: - return browser_ui_thread_scheduler_->GetTaskRunner( - QueueType::kBestEffort); + return browser_ui_thread_handle_.task_runner(QueueType::kBestEffort); case base::TaskPriority::USER_VISIBLE: - return browser_ui_thread_scheduler_->GetTaskRunner(QueueType::kDefault); + return browser_ui_thread_handle_.task_runner(QueueType::kDefault); case base::TaskPriority::USER_BLOCKING: - return browser_ui_thread_scheduler_->GetTaskRunner( - QueueType::kUserBlocking); + return browser_ui_thread_handle_.task_runner(QueueType::kUserBlocking); } } @@ -374,8 +372,7 @@ // static void BrowserTaskExecutor::EnableBestEffortQueues() { DCHECK(g_browser_task_executor); - g_browser_task_executor->browser_ui_thread_scheduler_ - ->EnableBestEffortQueues(); + g_browser_task_executor->browser_ui_thread_handle_.EnableBestEffortQueues(); } } // namespace content
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h index 9c00991..d13dbf7f 100644 --- a/content/browser/scheduler/browser_task_executor.h +++ b/content/browser/scheduler/browser_task_executor.h
@@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/task/task_executor.h" #include "build/build_config.h" +#include "content/browser/scheduler/browser_ui_thread_scheduler.h" #include "content/common/content_export.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -18,7 +19,6 @@ namespace content { class BrowserTaskExecutorTest; -class BrowserUIThreadScheduler; // This class's job is to map base::TaskTraits to actual task queues for the // browser process. @@ -112,6 +112,7 @@ GetAfterStartupTaskRunnerForThread(BrowserThread::ID id); std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_; + BrowserUIThreadScheduler::Handle browser_ui_thread_handle_; DISALLOW_COPY_AND_ASSIGN(BrowserTaskExecutor); };
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc index 0d47c88..0e490f02 100644 --- a/content/browser/scheduler/browser_task_executor_unittest.cc +++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -155,7 +155,7 @@ BrowserUIThreadScheduler::CreateForTesting(sequence_manager(), GetTimeDomain()); DeferredInitFromSubclass( - browser_ui_thread_scheduler->GetTaskRunnerForTesting( + browser_ui_thread_scheduler->GetHandle().task_runner( QueueType::kDefault)); browser_ui_thread_scheduler_ = browser_ui_thread_scheduler.get(); BrowserTaskExecutor::CreateWithBrowserUIThreadSchedulerForTesting( @@ -171,7 +171,7 @@ }; public: - using QueueType = BrowserUIThreadTaskQueue::QueueType; + using QueueType = BrowserUIThreadScheduler::QueueType; ~BrowserTaskExecutorWithCustomSchedulerTest() override { BrowserTaskExecutor::ResetForTesting(); @@ -185,7 +185,8 @@ EnsureUIThreadTraitPointsToExpectedQueue) { EXPECT_EQ(base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}), scoped_task_environment_.browser_ui_thread_scheduler() - ->GetTaskRunnerForTesting(QueueType::kDefault)); + ->GetHandle() + .task_runner(QueueType::kDefault)); EXPECT_EQ(base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}), scoped_task_environment_.GetMainThreadTaskRunner()); }
diff --git a/content/browser/scheduler/browser_task_queues.cc b/content/browser/scheduler/browser_task_queues.cc new file mode 100644 index 0000000..e80f2686 --- /dev/null +++ b/content/browser/scheduler/browser_task_queues.cc
@@ -0,0 +1,234 @@ +// 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 "content/browser/scheduler/browser_task_queues.h" + +#include <iterator> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/feature_list.h" +#include "base/memory/scoped_refptr.h" +#include "base/sequenced_task_runner.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/common/content_features.h" + +namespace content { +namespace { +const char* GetControlTaskQueueName(BrowserThread::ID thread_id) { + switch (thread_id) { + case BrowserThread::UI: + return "ui_control_tq"; + case BrowserThread::IO: + return "io_control_tq"; + case BrowserThread::ID_COUNT: + break; + } + NOTREACHED(); + return ""; +} + +const char* GetRunAllPendingTaskQueueName(BrowserThread::ID thread_id) { + switch (thread_id) { + case BrowserThread::UI: + return "ui_run_all_pending_tq"; + case BrowserThread::IO: + return "io_run_all_pending_tq"; + case BrowserThread::ID_COUNT: + break; + } + NOTREACHED(); + return ""; +} + +const char* GetUITaskQueueName(BrowserTaskQueues::QueueType queue_type) { + switch (queue_type) { + case BrowserTaskQueues::QueueType::kBestEffort: + return "ui_best_effort_tq"; + case BrowserTaskQueues::QueueType::kBootstrap: + return "ui_bootstrap_tq"; + case BrowserTaskQueues::QueueType::kNavigationAndPreconnection: + return "ui_navigation_and_preconnection_tq"; + case BrowserTaskQueues::QueueType::kDefault: + return "ui_default_tq"; + case BrowserTaskQueues::QueueType::kUserBlocking: + return "ui_user_blocking_tq"; + } +} + +const char* GetIOTaskQueueName(BrowserTaskQueues::QueueType queue_type) { + switch (queue_type) { + case BrowserTaskQueues::QueueType::kBestEffort: + return "io_best_effort_tq"; + case BrowserTaskQueues::QueueType::kBootstrap: + return "io_bootstrap_tq"; + case BrowserTaskQueues::QueueType::kNavigationAndPreconnection: + return "io_navigation_and_preconnection_tq"; + case BrowserTaskQueues::QueueType::kDefault: + return "io_default_tq"; + case BrowserTaskQueues::QueueType::kUserBlocking: + return "io_user_blocking_tq"; + } +} + +const char* GetTaskQueueName(BrowserThread::ID thread_id, + BrowserTaskQueues::QueueType queue_type) { + switch (thread_id) { + case BrowserThread::UI: + return GetUITaskQueueName(queue_type); + case BrowserThread::IO: + return GetIOTaskQueueName(queue_type); + case BrowserThread::ID_COUNT: + break; + } + NOTREACHED(); + return ""; +} + +} // namespace + +BrowserTaskQueues::Handle::Handle(Handle&&) = default; +BrowserTaskQueues::Handle::Handle(const Handle&) = default; +BrowserTaskQueues::Handle::~Handle() = default; +BrowserTaskQueues::Handle& BrowserTaskQueues::Handle::operator=(Handle&&) = + default; +BrowserTaskQueues::Handle& BrowserTaskQueues::Handle::operator=(const Handle&) = + default; + +BrowserTaskQueues::Handle::Handle(BrowserTaskQueues* outer) + : outer_(outer), + control_task_runner_(outer_->control_queue_->task_runner()), + regular_task_runners_(outer_->CreateRegularTaskRunners()) {} + +void BrowserTaskQueues::Handle::PostFeatureListInitializationSetup() { + control_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&BrowserTaskQueues::PostFeatureListInitializationSetup, + base::Unretained(outer_))); +} + +void BrowserTaskQueues::Handle::EnableBestEffortQueues() { + control_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&BrowserTaskQueues::EnableBestEffortQueues, + base::Unretained(outer_))); +} + +void BrowserTaskQueues::Handle::ScheduleRunAllPendingTasksForTesting( + base::OnceClosure on_pending_task_ran) { + control_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &BrowserTaskQueues::StartRunAllPendingTasksForTesting, + base::Unretained(outer_), + base::ScopedClosureRunner(std::move(on_pending_task_ran)))); +} + +BrowserTaskQueues::BrowserTaskQueues( + BrowserThread::ID thread_id, + base::sequence_manager::SequenceManager* sequence_manager, + base::sequence_manager::TimeDomain* time_domain) { + for (size_t i = 0; i < regular_queues_.size(); ++i) { + regular_queues_[i] = sequence_manager->CreateTaskQueue( + base::sequence_manager::TaskQueue::Spec( + GetTaskQueueName(thread_id, static_cast<QueueType>(i))) + .SetTimeDomain(time_domain)); + } + + // Best effort queue + best_effort_voter_ = + regular_queue(QueueType::kBestEffort)->CreateQueueEnabledVoter(); + regular_queue(QueueType::kBestEffort) + ->SetQueuePriority( + base::sequence_manager::TaskQueue::kBestEffortPriority); + best_effort_voter_->SetVoteToEnable(false); + + // Control queue + control_queue_ = + sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec( + GetControlTaskQueueName(thread_id)) + .SetTimeDomain(time_domain)); + control_queue_->SetQueuePriority( + base::sequence_manager::TaskQueue::kControlPriority); + + // Run all pending queue + run_all_pending_tasks_queue_ = sequence_manager->CreateTaskQueue( + base::sequence_manager::TaskQueue::Spec( + GetRunAllPendingTaskQueueName(thread_id)) + .SetTimeDomain(time_domain)); + run_all_pending_tasks_queue_->SetQueuePriority( + base::sequence_manager::TaskQueue::kBestEffortPriority); +} + +BrowserTaskQueues::~BrowserTaskQueues() { + for (auto& queue : regular_queues_) { + queue->ShutdownTaskQueue(); + } + control_queue_->ShutdownTaskQueue(); + run_all_pending_tasks_queue_->ShutdownTaskQueue(); +} + +std::array<scoped_refptr<base::SingleThreadTaskRunner>, + BrowserTaskQueues::kNumQueueTypes> +BrowserTaskQueues::CreateRegularTaskRunners() const { + std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes> + task_runners; + for (size_t i = 0; i < regular_queues_.size(); ++i) { + task_runners[i] = regular_queues_[i]->task_runner(); + } + return task_runners; +} + +void BrowserTaskQueues::PostFeatureListInitializationSetup() { + if (base::FeatureList::IsEnabled(features::kPrioritizeBootstrapTasks)) { + regular_queue(QueueType::kBootstrap) + ->SetQueuePriority(base::sequence_manager::TaskQueue::kHighestPriority); + + // Navigation and preconnection tasks are also important during startup so + // prioritize them too. + regular_queue(QueueType::kNavigationAndPreconnection) + ->SetQueuePriority(base::sequence_manager::TaskQueue::kHighPriority); + } +} + +void BrowserTaskQueues::EnableBestEffortQueues() { + best_effort_voter_->SetVoteToEnable(true); +} + +// To run all pending tasks we do the following. We insert a fence in all queues +// and post a task to the |run_all_pending_queue_| which has the lowest priority +// possible. That makes sure that all tasks up to the fences will have run +// before this posted task runs. Note that among tasks with the same priority +// ties are broken by using the enqueue order, so all prior best effort tasks +// will have run before this one does. This task will then remove all the fences +// and call the user provided callback to signal that all pending tasks have +// run. This method is "reentrant" as in we can call it multiple times as the +// fences will just be moved back, but we need to make sure that only the last +// call removes the fences, for that we keep track of "nesting" with +// |run_all_pending_nesting_level_| +void BrowserTaskQueues::StartRunAllPendingTasksForTesting( + base::ScopedClosureRunner on_pending_task_ran) { + ++run_all_pending_nesting_level_; + for (const auto& queue : regular_queues_) { + queue->InsertFence( + base::sequence_manager::TaskQueue::InsertFencePosition::kNow); + } + run_all_pending_tasks_queue_->task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&BrowserTaskQueues::EndRunAllPendingTasksForTesting, + base::Unretained(this), std::move(on_pending_task_ran))); +} + +void BrowserTaskQueues::EndRunAllPendingTasksForTesting( + base::ScopedClosureRunner on_pending_task_ran) { + --run_all_pending_nesting_level_; + if (run_all_pending_nesting_level_ == 0) { + for (const auto& queue : regular_queues_) { + queue->RemoveFence(); + } + } +} + +} // namespace content \ No newline at end of file
diff --git a/content/browser/scheduler/browser_task_queues.h b/content/browser/scheduler/browser_task_queues.h new file mode 100644 index 0000000..e22c2bc5 --- /dev/null +++ b/content/browser/scheduler/browser_task_queues.h
@@ -0,0 +1,168 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SCHEDULER_BROWSER_TASK_QUEUES_H_ +#define CONTENT_BROWSER_SCHEDULER_BROWSER_TASK_QUEUES_H_ + +#include <array> + +#include "base/memory/scoped_refptr.h" +#include "base/task/sequence_manager/task_queue.h" +#include "content/common/content_export.h" +#include "content/public/browser/browser_thread.h" + +namespace base { +namespace sequence_manager { +class SequenceManager; +class TimeDomain; +} // namespace sequence_manager +} // namespace base + +namespace content { + +// Common task queues for browser threads. This class holds all the queues +// needed by browser threads. This makes it easy for all browser threads to have +// the same queues. Thic class also provides a Handler to act on the queues from +// any thread. +// +// Instances must be created and destroyed on the same thread as the +// underlying SequenceManager and instances are not allowed to outlive this +// SequenceManager. All methods of this class must be called from the +// associated thread unless noted otherwise. If you need to perform operations +// from a different thread use the a Handle instance instead. +// +// Best effort queues are initially disabled, that is, tasks will not be run for +// them. +class CONTENT_EXPORT BrowserTaskQueues { + public: + enum class QueueType { + // Catch all for tasks that don't fit other categories. + // TODO(alexclarke): Introduce new semantic types as needed to minimize the + // number of default tasks. + kDefault, + + // For non-urgent work, that will only execute if there's nothing else to + // do. Can theoretically be starved indefinitely although that's unlikely in + // practice. + kBestEffort, + + // For tasks on the critical path up to issuing the initial navigation. + kBootstrap, + + // For navigation and preconnection related tasks. + kNavigationAndPreconnection, + + // A generic high priority queue. Long term we should replace this with + // additional semantic annotations. + kUserBlocking, + + kMaxValue = kUserBlocking + }; + + static constexpr size_t kNumQueueTypes = + static_cast<size_t>(QueueType::kMaxValue) + 1; + + // Handle to a BrowserTaskQueues instance that can be used from any thread + // as all operations are thread safe. + // + // If the underlying BrowserTaskQueues is destroyed all methods of this + // class become no-ops, that is it is safe for this class to outlive its + // parent BrowserTaskQueues. + class CONTENT_EXPORT Handle { + public: + // Handles can be copied / moved around. + Handle(Handle&&) noexcept; + Handle(const Handle&); + ~Handle(); + Handle& operator=(Handle&&) noexcept; + Handle& operator=(const Handle&); + + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner( + QueueType queue_type) const { + return regular_task_runners_[static_cast<size_t>(queue_type)]; + } + + // Initializes any scheduler experiments. Should be called after + // FeatureLists have been initialized (which usually happens after task + // queues are set up). + void PostFeatureListInitializationSetup(); + + // Enables best effort tasks queues. Can be called multiple times. + void EnableBestEffortQueues(); + + // Schedules |on_pending_task_ran| to run when all pending tasks (at the + // time this method was invoked) have run. Only "runnable" tasks are taken + // into account, that is tasks from disabled queues are ignored, also this + // only works reliably for immediate tasks, delayed tasks might or might not + // run depending on timing. + // + // The callback will run on the thread associated with this Handle, unless + // that thread is no longer accepting tasks; in which case it will be run + // inline immediately. + // + // The recommended usage pattern is: + // RunLoop run_loop; + // handle.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + // run_loop.Run(); + void ScheduleRunAllPendingTasksForTesting( + base::OnceClosure on_pending_task_ran); + + private: + // Only BrowserTaskQueues can create new instances + friend class BrowserTaskQueues; + explicit Handle(BrowserTaskQueues* task_queues); + + // |outer_| can only be safely used from a task posted to one of the + // runners. + BrowserTaskQueues* outer_ = nullptr; + scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_; + std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes> + regular_task_runners_; + }; + + // |sequence_manager| and |time_domain| must outlive this instance. + explicit BrowserTaskQueues( + BrowserThread::ID thread_id, + base::sequence_manager::SequenceManager* sequence_manager, + base::sequence_manager::TimeDomain* time_domain); + + // Destroys all queues. + ~BrowserTaskQueues(); + + Handle CreateHandle() { return Handle(this); } + + private: + // All these methods can only be called from the associated thread. To make + // sure that is the case they will always be called from a task posted to the + // |control_queue_|. + void StartRunAllPendingTasksForTesting( + base::ScopedClosureRunner on_pending_task_ran); + void EndRunAllPendingTasksForTesting( + base::ScopedClosureRunner on_pending_task_ran); + void EnableBestEffortQueues(); + void PostFeatureListInitializationSetup(); + + base::sequence_manager::TaskQueue* regular_queue(QueueType type) const { + return regular_queues_[static_cast<size_t>(type)].get(); + } + + std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes> + CreateRegularTaskRunners() const; + + std::array<scoped_refptr<base::sequence_manager::TaskQueue>, kNumQueueTypes> + regular_queues_; + std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> + best_effort_voter_; + // Helper queue to make sure private methods run on the associated thread. the + // control queue has maximum priority and will never be disabled. + scoped_refptr<base::sequence_manager::TaskQueue> control_queue_; + + // Helper queue to run all pending tasks. + scoped_refptr<base::sequence_manager::TaskQueue> run_all_pending_tasks_queue_; + int run_all_pending_nesting_level_ = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SCHEDULER_BROWSER_TASK_QUEUES_H_
diff --git a/content/browser/scheduler/browser_task_queues_unittest.cc b/content/browser/scheduler/browser_task_queues_unittest.cc new file mode 100644 index 0000000..37b183c --- /dev/null +++ b/content/browser/scheduler/browser_task_queues_unittest.cc
@@ -0,0 +1,203 @@ +// 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 "content/browser/scheduler/browser_task_queues.h" + +#include <array> +#include <memory> + +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/task/sequence_manager/sequence_manager.h" +#include "base/test/bind_test_util.h" +#include "base/test/mock_callback.h" +#include "content/public/browser/browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace { + +using ::base::MessageLoop; +using ::base::RunLoop; +using ::base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump; +using ::base::sequence_manager::SequenceManager; +using ::testing::Invoke; +using ::testing::Mock; + +using MockTask = + testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>; + +using QueueType = BrowserTaskQueues::QueueType; + +class BrowserTaskQueuesTest : public testing::Test { + protected: + BrowserTaskQueuesTest() + : sequence_manager_(CreateSequenceManagerOnCurrentThreadWithPump( + MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT))), + queues_(std::make_unique<BrowserTaskQueues>( + BrowserThread::UI, + sequence_manager_.get(), + sequence_manager_->GetRealTimeDomain())), + handle_(queues_->CreateHandle()) { + sequence_manager_->SetDefaultTaskRunner( + handle_.task_runner(QueueType::kDefault)); + } + + std::unique_ptr<SequenceManager> sequence_manager_; + std::unique_ptr<BrowserTaskQueues> queues_; + BrowserTaskQueues::Handle handle_; +}; + +TEST_F(BrowserTaskQueuesTest, SimplePosting) { + scoped_refptr<base::SingleThreadTaskRunner> tq = + handle_.task_runner(QueueType::kDefault); + + MockTask task_1; + MockTask task_2; + MockTask task_3; + + { + testing::InSequence s; + EXPECT_CALL(task_1, Run); + EXPECT_CALL(task_2, Run); + EXPECT_CALL(task_3, Run); + } + + tq->PostTask(FROM_HERE, task_1.Get()); + tq->PostTask(FROM_HERE, task_2.Get()); + tq->PostTask(FROM_HERE, task_3.Get()); + + base::RunLoop().RunUntilIdle(); +} + +TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTesting) { + handle_.EnableBestEffortQueues(); + + MockTask task; + MockTask followup_task; + EXPECT_CALL(task, Run).WillOnce(Invoke([&]() { + for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { + handle_.task_runner(static_cast<QueueType>(i)) + ->PostTask(FROM_HERE, followup_task.Get()); + } + })); + + handle_.task_runner(QueueType::kDefault)->PostTask(FROM_HERE, task.Get()); + + { + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); + } + + Mock::VerifyAndClearExpectations(&task); + EXPECT_CALL(followup_task, Run).Times(BrowserTaskQueues::kNumQueueTypes); + + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingRunsAllTasks) { + constexpr size_t kTasksPerPriority = 100; + handle_.EnableBestEffortQueues(); + + MockTask task; + EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes * + kTasksPerPriority); + for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { + for (size_t j = 0; j < kTasksPerPriority; ++j) { + handle_.task_runner(static_cast<QueueType>(i)) + ->PostTask(FROM_HERE, task.Get()); + } + } + + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingIsReentrant) { + MockTask task_1; + MockTask task_2; + MockTask task_3; + + EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { + handle_.task_runner(QueueType::kDefault)->PostTask(FROM_HERE, task_2.Get()); + RunLoop run_loop(RunLoop::Type::kNestableTasksAllowed); + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); + })); + + EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() { + handle_.task_runner(QueueType::kDefault)->PostTask(FROM_HERE, task_3.Get()); + })); + + handle_.task_runner(QueueType::kDefault)->PostTask(FROM_HERE, task_1.Get()); + + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(BrowserTaskQueuesTest, + RunAllPendingTasksForTestingIgnoresBestEffortIfNotEnabled) { + MockTask best_effort_task; + MockTask default_task; + + handle_.task_runner(QueueType::kBestEffort) + ->PostTask(FROM_HERE, best_effort_task.Get()); + handle_.task_runner(QueueType::kDefault) + ->PostTask(FROM_HERE, default_task.Get()); + + EXPECT_CALL(default_task, Run); + + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(BrowserTaskQueuesTest, + RunAllPendingTasksForTestingRunsBestEffortTasksWhenEnabled) { + MockTask task_1; + MockTask task_2; + MockTask task_3; + + EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { + // This task should not run as it is posted after the + // RunAllPendingTasksForTesting() call + handle_.task_runner(QueueType::kBestEffort) + ->PostTask(FROM_HERE, task_3.Get()); + handle_.EnableBestEffortQueues(); + })); + EXPECT_CALL(task_2, Run); + + handle_.task_runner(QueueType::kDefault)->PostTask(FROM_HERE, task_1.Get()); + handle_.task_runner(QueueType::kBestEffort) + ->PostTask(FROM_HERE, task_2.Get()); + + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(BrowserTaskQueuesTest, HandleStillWorksWhenQueuesDestroyed) { + MockTask task; + queues_.reset(); + + for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { + EXPECT_FALSE( + handle_.task_runner(static_cast<QueueType>(i)) + ->PostTask(FROM_HERE, base::BindLambdaForTesting([]() {}))); + } + + handle_.EnableBestEffortQueues(); + RunLoop run_loop; + handle_.ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); + run_loop.Run(); +} + +} // namespace +} // namespace content \ No newline at end of file
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc index efbb0d9..733528d 100644 --- a/content/browser/scheduler/browser_ui_thread_scheduler.cc +++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -18,6 +18,7 @@ #include "base/task/sequence_manager/time_domain.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" +#include "content/public/browser/browser_thread.h" #include "content/public/common/content_features.h" namespace content { @@ -33,117 +34,35 @@ new BrowserUIThreadScheduler(sequence_manager, time_domain)); } -void BrowserUIThreadScheduler::PostFeatureListSetup() { - if (base::FeatureList::IsEnabled(features::kPrioritizeBootstrapTasks)) { - task_queues_[QueueType::kBootstrap]->SetQueuePriority( - base::sequence_manager::TaskQueue::kHighestPriority); - - // Navigation and preconnection tasks are also important during startup so - // prioritize them too. - task_queues_[QueueType::kNavigationAndPreconnection]->SetQueuePriority( - base::sequence_manager::TaskQueue::kHighPriority); - } -} - -void BrowserUIThreadScheduler::Shutdown() { - task_queues_.clear(); - owned_sequence_manager_.reset(); - sequence_manager_ = nullptr; -} - BrowserUIThreadScheduler::BrowserUIThreadScheduler() : owned_sequence_manager_( base::sequence_manager::CreateUnboundSequenceManager( base::sequence_manager::SequenceManager::Settings::Builder() .SetMessageLoopType(base::MessageLoop::TYPE_UI) .Build())), - sequence_manager_(owned_sequence_manager_.get()), - time_domain_(sequence_manager_->GetRealTimeDomain()) { - InitialiseTaskQueues(); + task_queues_(BrowserThread::UI, + owned_sequence_manager_.get(), + owned_sequence_manager_->GetRealTimeDomain()), + handle_(task_queues_.CreateHandle()) { + CommonSequenceManagerSetup(owned_sequence_manager_.get()); + owned_sequence_manager_->SetDefaultTaskRunner( + handle_.task_runner(QueueType::kDefault)); - sequence_manager_->SetDefaultTaskRunner(GetTaskRunner(QueueType::kDefault)); - - sequence_manager_->BindToMessagePump( + owned_sequence_manager_->BindToMessagePump( base::MessageLoop::CreateMessagePumpForType(base::MessageLoop::TYPE_UI)); } BrowserUIThreadScheduler::BrowserUIThreadScheduler( base::sequence_manager::SequenceManager* sequence_manager, base::sequence_manager::TimeDomain* time_domain) - : sequence_manager_(sequence_manager), time_domain_(time_domain) { - InitialiseTaskQueues(); + : task_queues_(BrowserThread::UI, sequence_manager, time_domain), + handle_(task_queues_.CreateHandle()) { + CommonSequenceManagerSetup(sequence_manager); } -void BrowserUIThreadScheduler::InitialiseTaskQueues() { - DCHECK(sequence_manager_); - sequence_manager_->EnableCrashKeys("ui_scheduler_async_stack"); - - // To avoid locks in BrowserUIThreadScheduler::GetTaskRunner, eagerly - // create all the well known task queues. - CreateTaskQueuesAndRunners(); - - InitialiseBestEffortQueue(); -} - -void BrowserUIThreadScheduler::CreateTaskQueuesAndRunners() { - for (int i = 0; - i < static_cast<int>(BrowserUIThreadTaskQueue::QueueType::kCount); i++) { - BrowserUIThreadTaskQueue::QueueType queue_type = - static_cast<BrowserUIThreadTaskQueue::QueueType>(i); - scoped_refptr<BrowserUIThreadTaskQueue> task_queue = - sequence_manager_->CreateTaskQueueWithType<BrowserUIThreadTaskQueue>( - base::sequence_manager::TaskQueue::Spec( - BrowserUIThreadTaskQueue::NameForQueueType(queue_type)) - .SetTimeDomain(time_domain_), - queue_type); - task_queues_.emplace(queue_type, task_queue); - task_runners_.emplace(queue_type, task_queue->task_runner()); - } -} - -void BrowserUIThreadScheduler::InitialiseBestEffortQueue() { - auto queue = task_queues_[BrowserUIThreadTaskQueue::QueueType::kBestEffort]; - queue->SetQueuePriority( - base::sequence_manager::TaskQueue::kBestEffortPriority); - best_effort_voter_ = queue->CreateQueueEnabledVoter(); - best_effort_voter_->SetVoteToEnable(false); -} - -scoped_refptr<base::SingleThreadTaskRunner> -BrowserUIThreadScheduler::GetTaskRunnerForTesting(QueueType queue_type) { - return GetTaskRunner(queue_type); -} - -scoped_refptr<base::SingleThreadTaskRunner> -BrowserUIThreadScheduler::GetTaskRunner(QueueType queue_type) { - auto it = task_runners_.find(queue_type); - if (it != task_runners_.end()) - return it->second; - NOTREACHED(); - return scoped_refptr<base::SingleThreadTaskRunner>(); -} - -void BrowserUIThreadScheduler::EnableBestEffortQueues() { - best_effort_voter_->SetVoteToEnable(true); -} - -void BrowserUIThreadScheduler::RunAllPendingTasksForTesting() { - std::vector<scoped_refptr<BrowserUIThreadTaskQueue>> fenced_queues; - for (const auto& queue : task_queues_) { - bool had_fence = queue.second->HasActiveFence(); - queue.second->InsertFence( - base::sequence_manager::TaskQueue::InsertFencePosition::kNow); - // If there was a fence already this must be a re-entrant call to this - // method. The previous statement just moved the fence further back. In this - // case we do not remove the fence as the parent run loop needs all queues - // to be fenced to be able to exit the run loop (i.e. become idle) - if (!had_fence) - fenced_queues.push_back(queue.second); - } - base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle(); - for (const auto& queue : fenced_queues) { - queue->RemoveFence(); - } +void BrowserUIThreadScheduler::CommonSequenceManagerSetup( + base::sequence_manager::SequenceManager* sequence_manager) { + sequence_manager->EnableCrashKeys("ui_scheduler_async_stack"); } } // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h index 5db6d42..565f0a8 100644 --- a/content/browser/scheduler/browser_ui_thread_scheduler.h +++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -10,7 +10,7 @@ #include "base/containers/flat_map.h" #include "base/message_loop/message_loop.h" #include "base/task/sequence_manager/task_queue.h" -#include "content/browser/scheduler/browser_ui_thread_task_queue.h" +#include "content/browser/scheduler/browser_task_queues.h" #include "content/common/content_export.h" namespace base { @@ -27,6 +27,8 @@ // implement scheduling policy. This class is never deleted in production. class CONTENT_EXPORT BrowserUIThreadScheduler { public: + using Handle = BrowserTaskQueues::Handle; + BrowserUIThreadScheduler(); ~BrowserUIThreadScheduler(); @@ -35,25 +37,9 @@ base::sequence_manager::SequenceManager* sequence_manager, base::sequence_manager::TimeDomain* time_domain); - // Initializes any scheduler experiments. - void PostFeatureListSetup(); + using QueueType = BrowserTaskQueues::QueueType; - // Releases the scheduler although GetTaskRunner() continues to operate - // however no tasks will be executed after this point. - void Shutdown(); - - void EnableBestEffortQueues(); - - using QueueType = BrowserUIThreadTaskQueue::QueueType; - - // Can be called from any thread. - scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForTesting( - QueueType queue_type); - - // Adds a fence to all queues, runs all tasks until idle, and finally removes - // the fences. Note that the run loop will eventually become idle, as new - // tasks will not be scheduled due to the fence. - void RunAllPendingTasksForTesting(); + Handle GetHandle() const { return handle_; } private: friend class BrowserTaskExecutor; @@ -62,36 +48,16 @@ base::sequence_manager::SequenceManager* sequence_manager, base::sequence_manager::TimeDomain* time_domain); - // Note this will be called before the FeatureList has been initialized. - void InitialiseTaskQueues(); - - // Creates all well known task queues (BrowserUIThreadTaskQueue::QueueType). - void CreateTaskQueuesAndRunners(); - - void InitialiseBestEffortQueue(); - - scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner( - QueueType queue_type); + void CommonSequenceManagerSetup( + base::sequence_manager::SequenceManager* sequence_manager); // In production the BrowserUIThreadScheduler will own its SequenceManager, // but in tests it may not. std::unique_ptr<base::sequence_manager::SequenceManager> owned_sequence_manager_; - base::sequence_manager::SequenceManager* sequence_manager_; - base::sequence_manager::TimeDomain* time_domain_; - - // The |task_queues_| and |task_runners_| are eagerly constructed and are - // immutable after InitialiseTaskQueues() has run. If we ever change that e.g. - // for per-frame scheduling then we will need to protect this with a lock. - // NB |task_runners_| outlive the SequenceManager, but |task_queues_| do not. - base::flat_map<QueueType, scoped_refptr<BrowserUIThreadTaskQueue>> - task_queues_; - base::flat_map<QueueType, scoped_refptr<base::SingleThreadTaskRunner>> - task_runners_; - - std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> - best_effort_voter_; + BrowserTaskQueues task_queues_; + Handle handle_; DISALLOW_COPY_AND_ASSIGN(BrowserUIThreadScheduler); };
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc b/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc index 9b47b06..5843c502 100644 --- a/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc +++ b/content/browser/scheduler/browser_ui_thread_scheduler_unittest.cc
@@ -4,6 +4,7 @@ #include "content/browser/scheduler/browser_ui_thread_scheduler.h" +#include <memory> #include <utility> #include <vector> @@ -19,14 +20,6 @@ namespace content { namespace { -using ::testing::ElementsAre; -using ::testing::Invoke; -using ::testing::Mock; - -void RecordRunOrder(std::vector<int>* run_order, int order) { - run_order->push_back(order); -} - base::OnceClosure RunOnDestruction(base::OnceClosure task) { return base::BindOnce( [](std::unique_ptr<base::ScopedClosureRunner>) {}, @@ -45,119 +38,11 @@ base::Passed(std::move(task)), task_queue)); } -} // namespace - -class BrowserUIThreadSchedulerTest : public testing::Test { - public: - BrowserUIThreadSchedulerTest() { - browser_ui_thread_scheduler_ = std::make_unique<BrowserUIThreadScheduler>(); - for (int i = 0; - i < static_cast<int>(BrowserUIThreadTaskQueue::QueueType::kCount); - i++) { - auto queue_type = static_cast<BrowserUIThreadTaskQueue::QueueType>(i); - task_runners_.emplace( - queue_type, - browser_ui_thread_scheduler_->GetTaskRunnerForTesting(queue_type)); - } - } - - protected: - using QueueType = BrowserUIThreadScheduler::QueueType; - using MockTask = - testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>; - - std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_; - base::flat_map<BrowserUIThreadScheduler::QueueType, - scoped_refptr<base::SingleThreadTaskRunner>> - task_runners_; -}; - -TEST_F(BrowserUIThreadSchedulerTest, RunAllPendingTasksForTesting) { - browser_ui_thread_scheduler_->EnableBestEffortQueues(); - MockTask task_1; - MockTask task_2; - EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_2.Get()); - task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_2.Get()); - })); - - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get()); - - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); - - Mock::VerifyAndClearExpectations(&task_1); - EXPECT_CALL(task_2, Run).Times(2); - - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); -} - -TEST_F(BrowserUIThreadSchedulerTest, - RunAllPendingTasksForTestingIgnoresBestEffortIfNotEnabled) { - MockTask best_effort_task; - MockTask default_task; - - task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, - best_effort_task.Get()); - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, default_task.Get()); - - EXPECT_CALL(default_task, Run); - - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); -} - -TEST_F(BrowserUIThreadSchedulerTest, - RunAllPendingTasksForTestingRunsBestEffortTasksWhenEnabled) { - MockTask task_1; - MockTask task_2; - MockTask task_3; - EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { - // This task should not run as it is posted after the - // RunAllPendingTasksForTesting() call - task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_3.Get()); - browser_ui_thread_scheduler_->EnableBestEffortQueues(); - })); - EXPECT_CALL(task_2, Run); - - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get()); - task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_2.Get()); - - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); -} - -TEST_F(BrowserUIThreadSchedulerTest, RunAllPendingTasksForTestingIsReentrant) { - MockTask task_1; - MockTask task_2; - MockTask task_3; - - EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_2.Get()); - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); - })); - EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() { - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_3.Get()); - })); - - task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get()); - browser_ui_thread_scheduler_->RunAllPendingTasksForTesting(); -} - -TEST_F(BrowserUIThreadSchedulerTest, SimplePosting) { - scoped_refptr<base::SingleThreadTaskRunner> tq = - task_runners_[QueueType::kDefault]; - - std::vector<int> order; - tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 1)); - tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 2)); - tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 3)); - - base::RunLoop().RunUntilIdle(); - - EXPECT_THAT(order, ElementsAre(1, 2, 3)); -} - -TEST_F(BrowserUIThreadSchedulerTest, DestructorPostChainDuringShutdown) { - scoped_refptr<base::SingleThreadTaskRunner> task_queue = - task_runners_[QueueType::kDefault]; +TEST(BrowserUIThreadSchedulerTest, DestructorPostChainDuringShutdown) { + auto browser_ui_thread_scheduler_ = + std::make_unique<BrowserUIThreadScheduler>(); + auto task_queue = browser_ui_thread_scheduler_->GetHandle().task_runner( + BrowserUIThreadScheduler::QueueType::kDefault); bool run = false; task_queue->PostTask( @@ -174,4 +59,6 @@ EXPECT_TRUE(run); } +} // namespace + } // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_task_queue.cc b/content/browser/scheduler/browser_ui_thread_task_queue.cc deleted file mode 100644 index 3f87e34..0000000 --- a/content/browser/scheduler/browser_ui_thread_task_queue.cc +++ /dev/null
@@ -1,42 +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 "content/browser/scheduler/browser_ui_thread_task_queue.h" - -#include <utility> - -#include "base/task/sequence_manager/task_queue_impl.h" - -namespace content { - -BrowserUIThreadTaskQueue::BrowserUIThreadTaskQueue( - std::unique_ptr<base::sequence_manager::internal::TaskQueueImpl> impl, - const TaskQueue::Spec& spec, - QueueType queue_type) - : base::sequence_manager::TaskQueue(std::move(impl), spec), - queue_type_(queue_type) {} - -BrowserUIThreadTaskQueue::~BrowserUIThreadTaskQueue() = default; - -// static -const char* BrowserUIThreadTaskQueue::NameForQueueType(QueueType queue_type) { - switch (queue_type) { - case QueueType::kBestEffort: - return "best_effort_tq"; - case QueueType::kBootstrap: - return "bootstrap_tq"; - case QueueType::kNavigationAndPreconnection: - return "navigation_and_preconnection_tq"; - case QueueType::kDefault: - return "default_tq"; - case QueueType::kUserBlocking: - return "user_blocking_tq"; - case QueueType::kCount: - break; - } - NOTREACHED(); - return nullptr; -} - -} // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_task_queue.h b/content/browser/scheduler/browser_ui_thread_task_queue.h deleted file mode 100644 index 389b288..0000000 --- a/content/browser/scheduler/browser_ui_thread_task_queue.h +++ /dev/null
@@ -1,76 +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. - -#ifndef CONTENT_BROWSER_SCHEDULER_BROWSER_UI_THREAD_TASK_QUEUE_H_ -#define CONTENT_BROWSER_SCHEDULER_BROWSER_UI_THREAD_TASK_QUEUE_H_ - -#include <memory> - -#include "base/task/sequence_manager/task_queue.h" -#include "content/common/content_export.h" - -namespace base { -namespace sequence_manager { -// TODO(carlscab): Refactor creation of task queues so we don't need to expose -// the internal namespace. -namespace internal { -class TaskQueueImpl; -} // namespace internal -} // namespace sequence_manager -} // namespace base - -namespace content { - -// There are a number of BrowserUIThreadTaskQueues and all UI thread tasks are -// posted to one of these. The scheduler manipulates these queues in order to -// implement scheduling policy. -class CONTENT_EXPORT BrowserUIThreadTaskQueue - : public base::sequence_manager::TaskQueue { - public: - enum class QueueType { - // Catch all for tasks that don't fit other categories. - // TODO(alexclarke): Introduce new semantic types as needed to minimize the - // number of default tasks. - kDefault, - - // For non-urgent work, that will only execute if there's nothing else to - // do. Can theoretically be starved indefinitely although that's unlikely in - // practice. - kBestEffort, - - // For tasks on the critical path up to issuing the initial navigation. - kBootstrap, - - // For navigation and preconnection related tasks. - kNavigationAndPreconnection, - - // A generic high priority queue. Long term we should replace this with - // additional semantic annotations. - kUserBlocking, - - kCount - }; - - BrowserUIThreadTaskQueue( - std::unique_ptr<base::sequence_manager::internal::TaskQueueImpl> impl, - const TaskQueue::Spec& spec, - QueueType queue_type); - - QueueType queue_type() const { return queue_type_; } - - // Returns name of the given queue type. Returned string has application - // lifetime. - static const char* NameForQueueType(QueueType queue_type); - - private: - ~BrowserUIThreadTaskQueue() override; - - QueueType queue_type_; - - DISALLOW_COPY_AND_ASSIGN(BrowserUIThreadTaskQueue); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_SCHEDULER_BROWSER_UI_THREAD_TASK_QUEUE_H_
diff --git a/content/browser/screen_orientation/screen_orientation_delegate_android.cc b/content/browser/screen_orientation/screen_orientation_delegate_android.cc index 7f88ed3..535fd96 100644 --- a/content/browser/screen_orientation/screen_orientation_delegate_android.cc +++ b/content/browser/screen_orientation/screen_orientation_delegate_android.cc
@@ -4,6 +4,7 @@ #include "content/browser/screen_orientation/screen_orientation_delegate_android.h" +#include "base/android/scoped_java_ref.h" #include "content/browser/screen_orientation/screen_orientation_provider.h" #include "jni/ScreenOrientationProviderImpl_jni.h" #include "ui/android/window_android.h" @@ -27,25 +28,33 @@ void ScreenOrientationDelegateAndroid::Lock( WebContents* web_contents, blink::WebScreenOrientationLockType lock_orientation) { + base::android::ScopedJavaLocalRef<jobject> java_instance = + Java_ScreenOrientationProviderImpl_getInstance( + base::android::AttachCurrentThread()); gfx::NativeWindow window = web_contents->GetTopLevelNativeWindow(); Java_ScreenOrientationProviderImpl_lockOrientation( - base::android::AttachCurrentThread(), - window ? window->GetJavaObject() : nullptr, - lock_orientation); + base::android::AttachCurrentThread(), java_instance, + window ? window->GetJavaObject() : nullptr, lock_orientation); } bool ScreenOrientationDelegateAndroid::ScreenOrientationProviderSupported() { // TODO(MLamouri): Consider moving isOrientationLockEnabled to a separate // function, so reported error messages can differentiate between the device // never supporting orientation or currently not support orientation. + base::android::ScopedJavaLocalRef<jobject> java_instance = + Java_ScreenOrientationProviderImpl_getInstance( + base::android::AttachCurrentThread()); return Java_ScreenOrientationProviderImpl_isOrientationLockEnabled( - base::android::AttachCurrentThread()); + base::android::AttachCurrentThread(), java_instance); } void ScreenOrientationDelegateAndroid::Unlock(WebContents* web_contents) { + base::android::ScopedJavaLocalRef<jobject> java_instance = + Java_ScreenOrientationProviderImpl_getInstance( + base::android::AttachCurrentThread()); gfx::NativeWindow window = web_contents->GetTopLevelNativeWindow(); Java_ScreenOrientationProviderImpl_unlockOrientation( - base::android::AttachCurrentThread(), + base::android::AttachCurrentThread(), java_instance, window ? window->GetJavaObject() : nullptr); }
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc index 3aade57..5a0ead7 100644 --- a/content/browser/site_per_process_unload_browsertest.cc +++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -1016,4 +1016,187 @@ } } +// Regression test for https://crbug.com/960006. +// +// 1. Navigate to a1(a2(b3),c4), +// 2. b3 has a slow unload handler. +// 3. a2 navigates same process. +// 4. When the new document is loaded, a message is sent to c4 to check it +// cannot see b3 anymore, even if b3 is still unloading. +IN_PROC_BROWSER_TEST_F( + SitePerProcessBrowserTest, + IsDetachedSubframeObservableDuringUnloadHandlerSameProcess) { + GURL page_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a(b),c)")); + EXPECT_TRUE(NavigateToURL(shell(), page_url)); + RenderFrameHostImpl* node1 = + static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root() + ->current_frame_host(); + RenderFrameHostImpl* node2 = node1->child_at(0)->current_frame_host(); + RenderFrameHostImpl* node3 = node2->child_at(0)->current_frame_host(); + RenderFrameHostImpl* node4 = node1->child_at(1)->current_frame_host(); + ASSERT_TRUE(ExecJs(node1, "window.name = 'node1'")); + ASSERT_TRUE(ExecJs(node2, "window.name = 'node2'")); + ASSERT_TRUE(ExecJs(node3, "window.name = 'node3'")); + ASSERT_TRUE(ExecJs(node4, "window.name = 'node4'")); + + // Test sanity check. + EXPECT_EQ(true, EvalJs(node1, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node2, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node3, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node4, "!!top.node2.node3")); + + // Simulate a long-running unload handler in |node3|. + auto detach_filter = base::MakeRefCounted<DropMessageFilter>( + FrameMsgStart, FrameHostMsg_Detach::ID); + node3->GetProcess()->AddFilter(detach_filter.get()); + node2->DisableSwapOutTimerForTesting(); + ASSERT_TRUE(ExecJs(node3, "window.onunload = ()=>{}")); + + // Prepare |node4| to respond to postMessage with a report of whether it can + // still find |node3|. + const char* kPostMessageHandlerScript = R"( + window.postMessageGotData == false; + window.postMessageCallback = function() {}; + function receiveMessage(event) { + console.log('node4 - receiveMessage...'); + + var can_node3_be_found = false; + try { + can_node3_be_found = !!top.node2.node3; + } catch(e) { + can_node3_be_found = false; + } + + window.postMessageGotData = true; + window.postMessageData = can_node3_be_found; + window.postMessageCallback(window.postMessageData); + } + window.addEventListener("message", receiveMessage, false); + )"; + ASSERT_TRUE(ExecJs(node4, kPostMessageHandlerScript)); + + // Make |node1| navigate |node2| same process and after the navigation + // succeeds, send a post message to |node4|. We expect that the effects of the + // commit should be visible to |node4| by the time it receives the posted + // message. + const char* kNavigationScript = R"( + var node2_frame = document.getElementsByTagName('iframe')[0]; + node2_frame.onload = function() { + console.log('node2_frame.onload ...'); + node4.postMessage('try to find node3', '*'); + }; + node2_frame.src = $1; + )"; + GURL url = embedded_test_server()->GetURL("a.com", "/title1.html"); + ASSERT_TRUE(ExecJs(node1, JsReplace(kNavigationScript, url))); + + // Check if |node4| has seen |node3| even after |node2| navigation finished + // (no other frame should see |node3| after the navigation of its parent). + const char* kPostMessageResultsScript = R"( + new Promise(function (resolve, reject) { + if (window.postMessageGotData) + resolve(window.postMessageData); + else + window.postMessageCallback = resolve; + }); + )"; + EXPECT_EQ(false, EvalJs(node4, kPostMessageResultsScript)); +} + +// Regression test for https://crbug.com/960006. +// +// 1. Navigate to a1(a2(b3),c4), +// 2. b3 has a slow unload handler. +// 3. a2 navigates cross process. +// 4. When the new document is loaded, a message is sent to c4 to check it +// cannot see b3 anymore, even if b3 is still unloading. +// +// Note: This test is the same as the above, except it uses a cross-process +// navigation at step 3. +IN_PROC_BROWSER_TEST_F( + SitePerProcessBrowserTest, + IsDetachedSubframeObservableDuringUnloadHandlerCrossProcess) { + GURL page_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a(b),c)")); + EXPECT_TRUE(NavigateToURL(shell(), page_url)); + RenderFrameHostImpl* node1 = + static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root() + ->current_frame_host(); + RenderFrameHostImpl* node2 = node1->child_at(0)->current_frame_host(); + RenderFrameHostImpl* node3 = node2->child_at(0)->current_frame_host(); + RenderFrameHostImpl* node4 = node1->child_at(1)->current_frame_host(); + ASSERT_TRUE(ExecJs(node1, "window.name = 'node1'")); + ASSERT_TRUE(ExecJs(node2, "window.name = 'node2'")); + ASSERT_TRUE(ExecJs(node3, "window.name = 'node3'")); + ASSERT_TRUE(ExecJs(node4, "window.name = 'node4'")); + + // Test sanity check. + EXPECT_EQ(true, EvalJs(node1, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node2, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node3, "!!top.node2.node3")); + EXPECT_EQ(true, EvalJs(node4, "!!top.node2.node3")); + + // Add a long-running unload handler to |node3|. + auto detach_filter = base::MakeRefCounted<DropMessageFilter>( + FrameMsgStart, FrameHostMsg_Detach::ID); + node3->GetProcess()->AddFilter(detach_filter.get()); + node2->DisableSwapOutTimerForTesting(); + ASSERT_TRUE(ExecJs(node3, "window.onunload = ()=>{}")); + + // Prepare |node4| to respond to postMessage with a report of whether it can + // still find |node3|. + const char* kPostMessageHandlerScript = R"( + window.postMessageGotData == false; + window.postMessageCallback = function() {}; + function receiveMessage(event) { + console.log('node4 - receiveMessage...'); + + var can_node3_be_found = false; + try { + can_node3_be_found = !!top.node2.node3; + } catch(e) { + can_node3_be_found = false; + } + + window.postMessageGotData = true; + window.postMessageData = can_node3_be_found; + window.postMessageCallback(window.postMessageData); + } + window.addEventListener("message", receiveMessage, false); + )"; + ASSERT_TRUE(ExecJs(node4, kPostMessageHandlerScript)); + + // Make |node1| navigate |node2| cross process and after the navigation + // succeeds, send a post message to |node4|. We expect that the effects of the + // commit should be visible to |node4| by the time it receives the posted + // message. + const char* kNavigationScript = R"( + var node2_frame = document.getElementsByTagName('iframe')[0]; + node2_frame.onload = function() { + console.log('node2_frame.onload ...'); + node4.postMessage('try to find node3', '*'); + }; + node2_frame.src = $1; + )"; + GURL url = embedded_test_server()->GetURL("d.com", "/title1.html"); + ASSERT_TRUE(ExecJs(node1, JsReplace(kNavigationScript, url))); + + // Check if |node4| has seen |node3| even after |node2| navigation finished + // (no other frame should see |node3| after the navigation of its parent). + const char* kPostMessageResultsScript = R"( + new Promise(function (resolve, reject) { + if (window.postMessageGotData) + resolve(window.postMessageData); + else + window.postMessageCallback = resolve; + }); + )"; + EXPECT_EQ(false, EvalJs(node4, kPostMessageResultsScript)); +} + } // namespace content
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index 51dcd0b..3c29aa5 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -461,30 +461,6 @@ // finally destroyed when deletion completes (and |callback| is invoked). class StoragePartitionImpl::DataDeletionHelper { public: - // An instance of this class is used instead of a callback to - // DecrementTaskCount when the callback may be destroyed - // rather than invoked. The destruction of this object (which also - // occurs if the null callback is called) will automatically decrement - // the task count. - // Note that this object may be destroyed on any thread, as - // DecrementTaskCount() is thread-neutral. - // Note that the DataDeletionHelper must outlive this object. This - // should be guaranteed by the fact that the object holds a reference - // to the DataDeletionHelper. - class OwnsReference { - public: - explicit OwnsReference(DataDeletionHelper* helper) : helper_(helper) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - helper->IncrementTaskCountOnUI(); - } - - ~OwnsReference() { helper_->DecrementTaskCount(); } - - static void Callback(std::unique_ptr<OwnsReference> reference) {} - - DataDeletionHelper* helper_; - }; - DataDeletionHelper(uint32_t remove_mask, uint32_t quota_storage_remove_mask, base::OnceClosure callback) @@ -495,9 +471,6 @@ ~DataDeletionHelper() {} - void IncrementTaskCountOnUI(); - void DecrementTaskCount(); // Callable on any thread. - void ClearDataOnUIThread( const GURL& storage_origin, const OriginMatcherFunction& origin_matcher, @@ -523,6 +496,19 @@ base::OnceClosure callback); private: + enum class TracingDataType { + kSynchronous = 1, + kCookies = 2, + kQuota = 3, + kLocalStorage = 4, + kSessionStorage = 5, + kShaderCache = 6, + kPluginPrivate = 7, + }; + + base::OnceClosure CreateTaskCompletionClosure(TracingDataType data_type); + void OnTaskComplete(int tracing_id); // Callable on any thread. + uint32_t remove_mask_; uint32_t quota_storage_remove_mask_; @@ -1166,21 +1152,32 @@ CheckQuotaManagedDataDeletionStatus(deletion_task_count, done_callback); } -void StoragePartitionImpl::DataDeletionHelper::IncrementTaskCountOnUI() { +base::OnceClosure +StoragePartitionImpl::DataDeletionHelper::CreateTaskCompletionClosure( + TracingDataType data_type) { DCHECK_CURRENTLY_ON(BrowserThread::UI); ++task_count_; + static int tracing_id = 0; + TRACE_EVENT_ASYNC_BEGIN1("browsing_data", "StoragePartitionImpl", + ++tracing_id, "data_type", + static_cast<int>(data_type)); + return base::BindOnce( + &StoragePartitionImpl::DataDeletionHelper::OnTaskComplete, + base::Unretained(this), tracing_id); } -void StoragePartitionImpl::DataDeletionHelper::DecrementTaskCount() { +void StoragePartitionImpl::DataDeletionHelper::OnTaskComplete(int tracing_id) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { base::PostTaskWithTraits( FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&DataDeletionHelper::DecrementTaskCount, - base::Unretained(this))); + base::BindOnce(&DataDeletionHelper::OnTaskComplete, + base::Unretained(this), tracing_id)); return; } DCHECK_GT(task_count_, 0); --task_count_; + TRACE_EVENT_ASYNC_END0("browsing_data", "StoragePartitionImpl", tracing_id); + if (!task_count_) { std::move(callback_).Run(); delete this; @@ -1203,9 +1200,8 @@ DCHECK_NE(remove_mask_, 0u); DCHECK(!callback_.is_null()); - IncrementTaskCountOnUI(); - base::RepeatingClosure decrement_callback = base::BindRepeating( - &DataDeletionHelper::DecrementTaskCount, base::Unretained(this)); + base::ScopedClosureRunner synchronous_clear_operations( + CreateTaskCompletionClosure(TracingDataType::kSynchronous)); if (remove_mask_ & REMOVE_DATA_MASK_COOKIES) { // The CookieDeletionFilter has a redundant time interval to |begin| and @@ -1223,11 +1219,10 @@ std::move(cookie_deletion_filter), base::BindOnce( &OnClearedCookies, - // Use OwnsReference instead of Increment/DecrementTaskCount* - // to handle the cookie store being destroyed and the callback - // thus not being called. - base::BindOnce(&OwnsReference::Callback, - std::make_unique<OwnsReference>(this)))); + // Handle the cookie store being destroyed and the callback thus not + // being called. + mojo::WrapCallbackWithDefaultInvokeIfNotRun( + CreateTaskCompletionClosure(TracingDataType::kCookies)))); } if (remove_mask_ & REMOVE_DATA_MASK_INDEXEDDB || @@ -1236,29 +1231,27 @@ remove_mask_ & REMOVE_DATA_MASK_FILE_SYSTEMS || remove_mask_ & REMOVE_DATA_MASK_SERVICE_WORKERS || remove_mask_ & REMOVE_DATA_MASK_CACHE_STORAGE) { - IncrementTaskCountOnUI(); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, base::BindOnce( &DataDeletionHelper::ClearQuotaManagedDataOnIOThread, base::Unretained(this), base::WrapRefCounted(quota_manager), begin, storage_origin, base::WrapRefCounted(special_storage_policy), - origin_matcher, perform_storage_cleanup, decrement_callback)); + origin_matcher, perform_storage_cleanup, + CreateTaskCompletionClosure(TracingDataType::kQuota))); } if (remove_mask_ & REMOVE_DATA_MASK_LOCAL_STORAGE) { - IncrementTaskCountOnUI(); - ClearLocalStorageOnUIThread(base::WrapRefCounted(dom_storage_context), - base::WrapRefCounted(special_storage_policy), - origin_matcher, storage_origin, - perform_storage_cleanup, begin, end, - decrement_callback); + ClearLocalStorageOnUIThread( + base::WrapRefCounted(dom_storage_context), + base::WrapRefCounted(special_storage_policy), origin_matcher, + storage_origin, perform_storage_cleanup, begin, end, + CreateTaskCompletionClosure(TracingDataType::kLocalStorage)); // ClearDataImpl cannot clear session storage data when a particular origin // is specified. Therefore we ignore clearing session storage in this case. // TODO(lazyboy): Fix. if (storage_origin.is_empty()) { - IncrementTaskCountOnUI(); // TODO(crbug.com/960325): Sometimes SessionStorage fails to call its // callback. Figure out why. ClearSessionStorageOnUIThread( @@ -1266,30 +1259,32 @@ base::WrapRefCounted(special_storage_policy), origin_matcher, perform_storage_cleanup, mojo::WrapCallbackWithDefaultInvokeIfNotRun( - static_cast<base::OnceClosure>(decrement_callback))); + CreateTaskCompletionClosure(TracingDataType::kSessionStorage))); } } if (remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) { - IncrementTaskCountOnUI(); - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&ClearShaderCacheOnIOThread, path, - begin, end, decrement_callback)); + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::IO}, + base::BindOnce( + &ClearShaderCacheOnIOThread, path, begin, end, + CreateTaskCompletionClosure(TracingDataType::kShaderCache))); } #if BUILDFLAG(ENABLE_PLUGINS) if (remove_mask_ & REMOVE_DATA_MASK_PLUGIN_PRIVATE_DATA) { - IncrementTaskCountOnUI(); filesystem_context->default_file_task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ClearPluginPrivateDataOnFileTaskRunner, - base::WrapRefCounted(filesystem_context), - storage_origin, origin_matcher, - base::WrapRefCounted(special_storage_policy), - begin, end, std::move(decrement_callback))); + FROM_HERE, + base::BindOnce( + &ClearPluginPrivateDataOnFileTaskRunner, + base::WrapRefCounted(filesystem_context), storage_origin, + origin_matcher, base::WrapRefCounted(special_storage_policy), begin, + end, + base::AdaptCallbackForRepeating( + CreateTaskCompletionClosure(TracingDataType::kPluginPrivate)))); } #endif // BUILDFLAG(ENABLE_PLUGINS) - DecrementTaskCount(); } void StoragePartitionImpl::ClearDataForOrigin(
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index ff1c458..b6a8d8a 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -69,6 +69,11 @@ if (base::FeatureList::IsEnabled(features::kBloatedRendererDetection)) WebRuntimeFeatures::EnableBloatedRendererDetection(true); + if (base::FeatureList::IsEnabled( + blink::features::kBlockingFocusWithoutUserActivation)) { + WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(true); + } + if (command_line.HasSwitch(switches::kDisableDatabases)) WebRuntimeFeatures::EnableDatabase(false);
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index 5e7e9f7..2adc95d 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -564,6 +564,7 @@ java_files = [ "junit/src/org/chromium/content/browser/BindingManagerTest.java", "junit/src/org/chromium/content/browser/ChildProcessRankingTest.java", + "junit/src/org/chromium/content/browser/ScreenOrientationProviderImplTest.java", "junit/src/org/chromium/content/browser/UiThreadTaskTraitsImplTest.java", "junit/src/org/chromium/content/browser/accessibility/BrowserAccessibilityStateTest.java", "junit/src/org/chromium/content/browser/SpareChildConnectionTest.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java index f149ca7..9f6aa1f34 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java
@@ -9,24 +9,53 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.support.annotation.Nullable; +import android.util.Pair; import android.view.Surface; +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; +import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.content_public.browser.ScreenOrientationDelegate; +import org.chromium.content_public.browser.ScreenOrientationProvider; import org.chromium.content_public.common.ScreenOrientationConstants; import org.chromium.content_public.common.ScreenOrientationValues; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.display.DisplayAndroid; +import java.util.Map; +import java.util.WeakHashMap; + /** * This is the implementation of the C++ counterpart ScreenOrientationProvider. */ @JNINamespace("content") -public class ScreenOrientationProviderImpl { +public class ScreenOrientationProviderImpl + implements ActivityStateListener, ScreenOrientationProvider { + private static class Holder { + private static ScreenOrientationProviderImpl sInstance = + new ScreenOrientationProviderImpl(); + } + private static final String TAG = "cr.ScreenOrientation"; - private static ScreenOrientationDelegate sDelegate; + + private ScreenOrientationDelegate mDelegate; + + /** + * The keys of the map are the activities for which screen orientation requests are + * delayed. + * The values of the map are the most recent screen orientation request for each activity. + * The map will contain an entry with a null value if screen orientation requests are delayed + * for an activity but no screen orientation requests have been made for the activity. + */ + private Map<Activity, Pair<Boolean, Integer>> mDelayedRequests = new WeakHashMap<>(); + + @CalledByNative + public static ScreenOrientationProviderImpl getInstance() { + return Holder.sInstance; + } private static int getOrientationFromWebScreenOrientations(byte orientation, @Nullable WindowAndroid window, Context context) { @@ -70,10 +99,16 @@ } } - @CalledByNative - public static void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation) { - if (sDelegate != null && !sDelegate.canLockOrientation()) return; + @Override + public void onActivityStateChange(Activity activity, @ActivityState int newState) { + if (newState == ActivityState.DESTROYED) { + mDelayedRequests.remove(activity); + } + } + @CalledByNative + @Override + public void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation) { // WindowAndroid may be null if the tab is being reparented. if (window == null) return; Activity activity = window.getActivity().get(); @@ -89,11 +124,12 @@ return; } - activity.setRequestedOrientation(orientation); + setMaybeDelayedRequestedOrientation(activity, true /* lock */, orientation); } @CalledByNative - public static void unlockOrientation(@Nullable WindowAndroid window) { + @Override + public void unlockOrientation(@Nullable WindowAndroid window) { // WindowAndroid may be null if the tab is being reparented. if (window == null) return; Activity activity = window.getActivity().get(); @@ -122,20 +158,71 @@ } catch (PackageManager.NameNotFoundException e) { // Do nothing, defaultOrientation should be SCREEN_ORIENTATION_UNSPECIFIED. } finally { - if (sDelegate == null || sDelegate.canUnlockOrientation(activity, defaultOrientation)) { - activity.setRequestedOrientation(defaultOrientation); - } + setMaybeDelayedRequestedOrientation(activity, false /* lock */, defaultOrientation); + } + } + + @Override + public void delayOrientationRequests(WindowAndroid window) { + Activity activity = window.getActivity().get(); + if ((activity == null || areRequestsDelayedForActivity(activity))) { + return; + } + + mDelayedRequests.put(activity, null); + ApplicationStatus.registerStateListenerForActivity(this, activity); + } + + @Override + public void runDelayedOrientationRequests(WindowAndroid window) { + Activity activity = window.getActivity().get(); + if ((activity == null || !areRequestsDelayedForActivity(activity))) { + return; + } + + Pair<Boolean, Integer> delayedRequest = mDelayedRequests.remove(activity); + if (delayedRequest != null) { + setRequestedOrientationNow(activity, delayedRequest.first, delayedRequest.second); + } + if (mDelayedRequests.isEmpty()) { + ApplicationStatus.unregisterActivityStateListener(this); } } @CalledByNative - private static boolean isOrientationLockEnabled() { - return sDelegate == null || sDelegate.canLockOrientation(); + public boolean isOrientationLockEnabled() { + return mDelegate == null || mDelegate.canLockOrientation(); } - public static void setOrientationDelegate(ScreenOrientationDelegate delegate) { - sDelegate = delegate; + @Override + public void setOrientationDelegate(ScreenOrientationDelegate delegate) { + mDelegate = delegate; } - private ScreenOrientationProviderImpl() {} + /** Returns whether screen orientation requests are delayed for the passed-in activity. */ + private boolean areRequestsDelayedForActivity(Activity activity) { + return mDelayedRequests.containsKey(activity); + } + + /** Sets the requested orientation for the activity delaying the request if needed. */ + private void setMaybeDelayedRequestedOrientation( + Activity activity, boolean lock, int orientation) { + if (areRequestsDelayedForActivity(activity)) { + mDelayedRequests.put(activity, Pair.create(lock, orientation)); + } else { + setRequestedOrientationNow(activity, lock, orientation); + } + } + + /** Sets the requested orientation for the activity. */ + private void setRequestedOrientationNow(Activity activity, boolean lock, int orientation) { + if (mDelegate != null) { + if ((lock && !mDelegate.canLockOrientation()) + || (!lock && !mDelegate.canUnlockOrientation(activity, orientation))) { + return; + } + } + + activity.setRequestedOrientation(orientation); + } }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ScreenOrientationProvider.java b/content/public/android/java/src/org/chromium/content_public/browser/ScreenOrientationProvider.java index fbada5c..5c8bdb4 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/ScreenOrientationProvider.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/ScreenOrientationProvider.java
@@ -12,23 +12,25 @@ /** * Interface providing the access to C++ ScreenOrientationProvider. */ -public final class ScreenOrientationProvider { - private ScreenOrientationProvider() {} +public interface ScreenOrientationProvider { + static ScreenOrientationProvider getInstance() { + return ScreenOrientationProviderImpl.getInstance(); + } /** * Locks screen rotation to a given orientation. * @param window Window to lock rotation on. * @param webScreenOrientation Screen orientation. */ - public static void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation) { - ScreenOrientationProviderImpl.lockOrientation(window, webScreenOrientation); - } + void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation); - public static void unlockOrientation(@Nullable WindowAndroid window) { - ScreenOrientationProviderImpl.unlockOrientation(window); - } + void unlockOrientation(@Nullable WindowAndroid window); - public static void setOrientationDelegate(ScreenOrientationDelegate delegate) { - ScreenOrientationProviderImpl.setOrientationDelegate(delegate); - } + /** Delays screen orientation requests for the given window. */ + void delayOrientationRequests(WindowAndroid window); + + /** Runs delayed screen orientation requests for the given window. */ + void runDelayedOrientationRequests(WindowAndroid window); + + void setOrientationDelegate(ScreenOrientationDelegate delegate); }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java index b4ec4af..dd7627b 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java
@@ -231,7 +231,7 @@ int callCount = mCallbackHelper.getCallCount(); TestThreadUtils.runOnUiThreadBlocking(() -> { - ScreenOrientationProvider.lockOrientation( + ScreenOrientationProvider.getInstance().lockOrientation( mActivityTestRule.getWebContents().getTopLevelNativeWindow(), (byte) orientationValue); });
diff --git a/content/public/android/junit/src/org/chromium/content/browser/ScreenOrientationProviderImplTest.java b/content/public/android/junit/src/org/chromium/content/browser/ScreenOrientationProviderImplTest.java new file mode 100644 index 0000000..64538e2 --- /dev/null +++ b/content/public/android/junit/src/org/chromium/content/browser/ScreenOrientationProviderImplTest.java
@@ -0,0 +1,108 @@ +// 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.content.browser; + +import android.app.Activity; +import android.content.pm.ActivityInfo; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.content_public.common.ScreenOrientationValues; +import org.chromium.ui.base.ActivityWindowAndroid; + +import java.lang.ref.WeakReference; + +/** Unit tests for {@link ScreenOrientationProviderImpl } */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public final class ScreenOrientationProviderImplTest { + /** + * Tests that when screen orientation requests are delayed that newer requests overwrite older + * requests for a given activity. + */ + @Test + public void testDelayRequests() { + final Activity activity = Robolectric.buildActivity(Activity.class).create().get(); + ActivityWindowAndroid window = buildMockWindowForActivity(activity); + + // Last orientation lock request should take precedence. + ScreenOrientationProviderImpl instance = ScreenOrientationProviderImpl.getInstance(); + instance.delayOrientationRequests(window); + instance.lockOrientation(window, (byte) ScreenOrientationValues.PORTRAIT_PRIMARY); + instance.lockOrientation(window, (byte) ScreenOrientationValues.LANDSCAPE_PRIMARY); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.getRequestedOrientation()); + + instance.runDelayedOrientationRequests(window); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, activity.getRequestedOrientation()); + + // Lock then unlock screen orientation while requests are delayed. + instance.delayOrientationRequests(window); + instance.lockOrientation(window, (byte) ScreenOrientationValues.PORTRAIT_PRIMARY); + instance.unlockOrientation(window); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, activity.getRequestedOrientation()); + + instance.runDelayedOrientationRequests(window); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.getRequestedOrientation()); + } + + /** + * Tests that whether screen orientation requests are delayed can be toggled for each activity + * independently. + */ + @Test + public void testDelayRequestsAppliesOnlyToActivity() { + final Activity activity1 = Robolectric.buildActivity(Activity.class).create().get(); + ActivityWindowAndroid window1 = buildMockWindowForActivity(activity1); + final Activity activity2 = Robolectric.buildActivity(Activity.class).create().get(); + ActivityWindowAndroid window2 = buildMockWindowForActivity(activity2); + + ScreenOrientationProviderImpl instance = ScreenOrientationProviderImpl.getInstance(); + instance.delayOrientationRequests(window1); + instance.lockOrientation(window1, (byte) ScreenOrientationValues.PORTRAIT_PRIMARY); + instance.lockOrientation(window2, (byte) ScreenOrientationValues.LANDSCAPE_PRIMARY); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity1.getRequestedOrientation()); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, activity2.getRequestedOrientation()); + + instance.runDelayedOrientationRequests(window1); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, activity1.getRequestedOrientation()); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, activity2.getRequestedOrientation()); + } + + /** + * Tests that removing the screen orientation request delay is a no-op if there are no pending + * screen orientation requests. + */ + @Test + public void testRemoveDelayNoPendingRequests() { + final Activity activity = Robolectric.buildActivity(Activity.class).create().get(); + ActivityWindowAndroid window = buildMockWindowForActivity(activity); + + ScreenOrientationProviderImpl instance = ScreenOrientationProviderImpl.getInstance(); + instance.delayOrientationRequests(window); + instance.runDelayedOrientationRequests(window); + Assert.assertEquals( + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, activity.getRequestedOrientation()); + } + + private ActivityWindowAndroid buildMockWindowForActivity(Activity activity) { + ActivityWindowAndroid window = Mockito.mock(ActivityWindowAndroid.class); + Mockito.when(window.getActivity()).thenReturn(new WeakReference<>(activity)); + return window; + } +}
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc index 352d8ee..170b619 100644 --- a/content/public/test/test_browser_thread_bundle.cc +++ b/content/public/test/test_browser_thread_bundle.cc
@@ -91,7 +91,7 @@ BrowserUIThreadScheduler::CreateForTesting(sequence_manager(), GetTimeDomain()); scoped_refptr<base::SingleThreadTaskRunner> default_task_runner = - browser_ui_thread_scheduler->GetTaskRunnerForTesting( + browser_ui_thread_scheduler->GetHandle().task_runner( BrowserUIThreadScheduler::QueueType::kDefault); BrowserTaskExecutor::CreateWithBrowserUIThreadSchedulerForTesting( std::move(browser_ui_thread_scheduler)); @@ -119,6 +119,11 @@ // Consider startup complete such that after-startup-tasks always run in // the scope of the test they were posted from (http://crbug.com/732018). SetBrowserStartupIsCompleteForTesting(); + // Some unittests check the number of pending tasks, which will include the + // one that enables the best effort queues, so run everything pending before + // we hand control over to the test. + // TODO(carlscab): Maybe find a better way to not expose control tasks + BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI); } void TestBrowserThreadBundle::RunIOThreadUntilIdle() {
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 9b9f518..6f336de 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -653,7 +653,9 @@ "//third_party/webrtc/api/audio_codecs/g722:audio_encoder_g722", "//third_party/webrtc/api/audio_codecs/isac:audio_decoder_isac", "//third_party/webrtc/api/audio_codecs/isac:audio_encoder_isac", + "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_multiopus", "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus", + "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_multiopus", "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus", "//third_party/webrtc/api/video:video_bitrate_allocation", "//third_party/webrtc/api/video:video_frame",
diff --git a/content/renderer/accessibility/ax_image_annotator.cc b/content/renderer/accessibility/ax_image_annotator.cc index ef669e56..07e88d51 100644 --- a/content/renderer/accessibility/ax_image_annotator.cc +++ b/content/renderer/accessibility/ax_image_annotator.cc
@@ -178,7 +178,7 @@ blink::WebAXObject parent = image.ParentObject(); for (int ancestor_count = 0; !parent.IsDetached() && ancestor_count < 2; parent = parent.ParentObject()) { - if (!parent.AccessibilityIsIgnored()) { + if (parent.AccessibilityIsIncludedInTree()) { ++ancestor_count; if (parent.Role() == ax::mojom::Role::kLink || parent.Role() == ax::mojom::Role::kRootWebArea) {
diff --git a/content/renderer/accessibility/blink_ax_enum_conversion.cc b/content/renderer/accessibility/blink_ax_enum_conversion.cc index cca4b0d..7892d1f5 100644 --- a/content/renderer/accessibility/blink_ax_enum_conversion.cc +++ b/content/renderer/accessibility/blink_ax_enum_conversion.cc
@@ -71,8 +71,7 @@ else if (o.Orientation() == blink::kWebAXOrientationHorizontal) dst->AddState(ax::mojom::State::kHorizontal); - if (o.IsVisited()) - dst->AddState(ax::mojom::State::kVisited); + DCHECK(!o.AccessibilityIsIgnored()); } } // namespace content.
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index 0ebee3e..cc38608 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -161,7 +161,7 @@ WebAXObject ParentObjectUnignored(WebAXObject child) { WebAXObject parent = child.ParentObject(); - while (!parent.IsDetached() && parent.AccessibilityIsIgnored()) + while (!parent.IsDetached() && !parent.AccessibilityIsIncludedInTree()) parent = parent.ParentObject(); return parent; } @@ -488,7 +488,7 @@ if (node.Equals(root())) return WebAXObject(); node = node.ParentObject(); - } while (!node.IsDetached() && node.AccessibilityIsIgnored()); + } while (!node.IsDetached() && !node.AccessibilityIsIncludedInTree()); return node; } @@ -1211,8 +1211,15 @@ // Skip images that are too small to label. This also catches // unloaded images where the size is unknown. - if (dst->relative_bounds.bounds.width() < kMinImageAnnotationWidth || - dst->relative_bounds.bounds.height() < kMinImageAnnotationHeight) { + + WebAXObject offset_container; + WebFloatRect bounds; + SkMatrix44 container_transform; + bool clips_children = false; + src.GetRelativeBounds(offset_container, bounds, container_transform, + &clips_children); + if (bounds.width < kMinImageAnnotationWidth || + bounds.height < kMinImageAnnotationHeight) { dst->SetImageAnnotationStatus( ax::mojom::ImageAnnotationStatus::kIneligibleForAnnotation); return;
diff --git a/content/renderer/media/webrtc/audio_codec_factory.cc b/content/renderer/media/webrtc/audio_codec_factory.cc index 73abab1..0a6a3c4 100644 --- a/content/renderer/media/webrtc/audio_codec_factory.cc +++ b/content/renderer/media/webrtc/audio_codec_factory.cc
@@ -17,7 +17,9 @@ #include "third_party/webrtc/api/audio_codecs/g722/audio_encoder_g722.h" #include "third_party/webrtc/api/audio_codecs/isac/audio_decoder_isac.h" #include "third_party/webrtc/api/audio_codecs/isac/audio_encoder_isac.h" +#include "third_party/webrtc/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h" #include "third_party/webrtc/api/audio_codecs/opus/audio_decoder_opus.h" +#include "third_party/webrtc/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h" #include "third_party/webrtc/api/audio_codecs/opus/audio_encoder_opus.h" namespace content { @@ -73,7 +75,8 @@ return webrtc::CreateAudioEncoderFactory< webrtc::AudioEncoderOpus, webrtc::AudioEncoderIsac, webrtc::AudioEncoderG722, webrtc::AudioEncoderG711, - NotAdvertisedEncoder<webrtc::AudioEncoderL16>>(); + NotAdvertisedEncoder<webrtc::AudioEncoderL16>, + NotAdvertisedEncoder<webrtc::AudioEncoderMultiChannelOpus>>(); } rtc::scoped_refptr<webrtc::AudioDecoderFactory> @@ -81,7 +84,8 @@ return webrtc::CreateAudioDecoderFactory< webrtc::AudioDecoderOpus, webrtc::AudioDecoderIsac, webrtc::AudioDecoderG722, webrtc::AudioDecoderG711, - NotAdvertisedDecoder<webrtc::AudioDecoderL16>>(); + NotAdvertisedDecoder<webrtc::AudioDecoderL16>, + NotAdvertisedDecoder<webrtc::AudioDecoderMultiChannelOpus>>(); } } // namespace content
diff --git a/content/renderer/pepper/ppb_image_data_impl.cc b/content/renderer/pepper/ppb_image_data_impl.cc index fa2b937..00119ae 100644 --- a/content/renderer/pepper/ppb_image_data_impl.cc +++ b/content/renderer/pepper/ppb_image_data_impl.cc
@@ -12,6 +12,7 @@ #include "content/common/pepper_file_util.h" #include "content/common/view_messages.h" #include "content/renderer/render_thread_impl.h" +#include "mojo/public/cpp/base/shared_memory_utils.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_resource.h" @@ -107,9 +108,9 @@ void PPB_ImageData_Impl::Unmap() { backend_->Unmap(); } -int32_t PPB_ImageData_Impl::GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) { - return backend_->GetSharedMemory(shm, byte_count); +int32_t PPB_ImageData_Impl::GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) { + return backend_->GetSharedMemoryRegion(region); } SkCanvas* PPB_ImageData_Impl::GetCanvas() { return backend_->GetCanvas(); } @@ -135,25 +136,16 @@ int width, int height, bool init_to_zero) { - // TODO(brettw) use init_to_zero when we implement caching. + // TODO(brettw): use init_to_zero when we implement caching. width_ = width; height_ = height; uint32_t buffer_size = width_ * height_ * 4; - std::unique_ptr<base::SharedMemory> shared_memory = - RenderThread::Get()->HostAllocateSharedMemoryBuffer(buffer_size); - if (!shared_memory) + base::UnsafeSharedMemoryRegion region = + mojo::CreateUnsafeSharedMemoryRegion(buffer_size); + if (!region.IsValid()) return false; - // The TransportDIB is always backed by shared memory, so give the shared - // memory handle to it. - base::SharedMemoryHandle handle = shared_memory->handle().Duplicate(); - shared_memory->Unmap(); - shared_memory->Close(); - if (!handle.IsValid()) - return false; - - dib_.reset(TransportDIB::CreateWithHandle(handle)); - + dib_ = TransportDIB::CreateWithHandle(std::move(region)); return !!dib_; } @@ -188,10 +180,9 @@ // in the future to save some memory. } -int32_t ImageDataPlatformBackend::GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) { - *byte_count = dib_->size(); - *shm = dib_->shared_memory(); +int32_t ImageDataPlatformBackend::GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) { + *region = dib_->shared_memory_region(); return PP_OK; } @@ -224,41 +215,42 @@ bool init_to_zero) { skia_bitmap_.setInfo( SkImageInfo::MakeN32Premul(impl->width(), impl->height())); - shared_memory_.reset( - RenderThread::Get() - ->HostAllocateSharedMemoryBuffer(skia_bitmap_.computeByteSize()) - .release()); - return !!shared_memory_.get(); + shm_region_ = + mojo::CreateUnsafeSharedMemoryRegion(skia_bitmap_.computeByteSize()); + return shm_region_.IsValid(); } -bool ImageDataSimpleBackend::IsMapped() const { return map_count_ > 0; } +bool ImageDataSimpleBackend::IsMapped() const { + return shm_mapping_.IsValid(); +} TransportDIB* ImageDataSimpleBackend::GetTransportDIB() const { return nullptr; } void* ImageDataSimpleBackend::Map() { - DCHECK(shared_memory_.get()); + DCHECK(shm_region_.IsValid()); if (map_count_++ == 0) { - shared_memory_->Map(skia_bitmap_.computeByteSize()); - skia_bitmap_.setPixels(shared_memory_->memory()); + shm_mapping_ = shm_region_.Map(); + if (!shm_mapping_.IsValid()) + return nullptr; + + skia_bitmap_.setPixels(shm_mapping_.memory()); // Our platform bitmaps are set to opaque by default, which we don't want. skia_bitmap_.setAlphaType(kPremul_SkAlphaType); skia_canvas_ = std::make_unique<SkCanvas>(skia_bitmap_); - return skia_bitmap_.getAddr32(0, 0); } - return shared_memory_->memory(); + return skia_bitmap_.isNull() ? nullptr : skia_bitmap_.getAddr32(0, 0); } void ImageDataSimpleBackend::Unmap() { if (--map_count_ == 0) - shared_memory_->Unmap(); + shm_mapping_ = base::WritableSharedMemoryMapping(); } -int32_t ImageDataSimpleBackend::GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) { - *byte_count = skia_bitmap_.computeByteSize(); - *shm = shared_memory_.get(); +int32_t ImageDataSimpleBackend::GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) { + *region = &shm_region_; return PP_OK; }
diff --git a/content/renderer/pepper/ppb_image_data_impl.h b/content/renderer/pepper/ppb_image_data_impl.h index 757ec176..edaeeab 100644 --- a/content/renderer/pepper/ppb_image_data_impl.h +++ b/content/renderer/pepper/ppb_image_data_impl.h
@@ -10,7 +10,8 @@ #include <memory> #include "base/macros.h" -#include "base/memory/shared_memory.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "content/common/content_export.h" #include "ppapi/c/ppb_image_data.h" #include "ppapi/shared_impl/ppb_image_data_shared.h" @@ -21,10 +22,6 @@ class SkCanvas; class TransportDIB; -namespace base { -class SharedMemory; -} - namespace content { class CONTENT_EXPORT PPB_ImageData_Impl @@ -48,8 +45,8 @@ virtual TransportDIB* GetTransportDIB() const = 0; virtual void* Map() = 0; virtual void Unmap() = 0; - virtual int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) = 0; + virtual int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) = 0; virtual SkCanvas* GetCanvas() = 0; virtual SkBitmap GetMappedBitmap() const = 0; }; @@ -93,8 +90,8 @@ PP_Bool Describe(PP_ImageDataDesc* desc) override; void* Map() override; void Unmap() override; - int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) override; + int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) override; SkCanvas* GetCanvas() override; void SetIsCandidateForReuse() override; @@ -132,8 +129,8 @@ TransportDIB* GetTransportDIB() const override; void* Map() override; void Unmap() override; - int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) override; + int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) override; SkCanvas* GetCanvas() override; SkBitmap GetMappedBitmap() const override; @@ -165,13 +162,14 @@ TransportDIB* GetTransportDIB() const override; void* Map() override; void Unmap() override; - int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) override; + int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) override; SkCanvas* GetCanvas() override; SkBitmap GetMappedBitmap() const override; private: - std::unique_ptr<base::SharedMemory> shared_memory_; + base::UnsafeSharedMemoryRegion shm_region_; + base::WritableSharedMemoryMapping shm_mapping_; // skia_bitmap_ is backed by shared_memory_. SkBitmap skia_bitmap_; std::unique_ptr<SkCanvas> skia_canvas_;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 439c15c..82494a5 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -5010,8 +5010,11 @@ browser_side_navigation_pending_ = false; sync_navigation_callback_.Cancel(); mhtml_body_loader_client_.reset(); - if (!IsPerNavigationMojoInterfaceEnabled()) + if (!IsPerNavigationMojoInterfaceEnabled()) { Send(new FrameHostMsg_AbortNavigation(routing_id_)); + } else { + navigation_client_impl_.reset(); + } } void RenderFrameImpl::DidChangeSelection(bool is_empty_selection) {
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 432cd72..36f2ec5d7 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn
@@ -166,6 +166,8 @@ "browser/web_test/devtools_protocol_test_bindings.h", "browser/web_test/fake_bluetooth_chooser.cc", "browser/web_test/fake_bluetooth_chooser.h", + "browser/web_test/fake_bluetooth_chooser_factory.cc", + "browser/web_test/fake_bluetooth_chooser_factory.h", "browser/web_test/fake_bluetooth_scanning_prompt.cc", "browser/web_test/fake_bluetooth_scanning_prompt.h", "browser/web_test/leak_detector.cc",
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 4de34db..be6bb16 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -156,21 +156,21 @@ #endif // defined(OS_ANDROID) const service_manager::Manifest& GetContentBrowserOverlayManifest() { - static base::NoDestructor<service_manager::Manifest> manifest { - service_manager::ManifestBuilder() - .ExposeCapability( - "renderer", - service_manager::Manifest::InterfaceList< - mojom::MojoWebTestHelper, mojom::FakeBluetoothChooser, - mojom::WebTestBluetoothFakeAdapterSetter, - bluetooth::mojom::FakeBluetooth>()) - .RequireCapability(echo::mojom::kServiceName, "echo") - .ExposeInterfaceFilterCapability_Deprecated( - "navigation:frame", "renderer", - service_manager::Manifest::InterfaceList< - mojom::MojoWebTestHelper>()) - .Build() - }; + static base::NoDestructor<service_manager::Manifest> manifest{ + service_manager::ManifestBuilder() + .ExposeCapability( + "renderer", + service_manager::Manifest::InterfaceList< + mojom::MojoWebTestHelper, mojom::FakeBluetoothChooser, + mojom::FakeBluetoothChooserFactory, + mojom::WebTestBluetoothFakeAdapterSetter, + bluetooth::mojom::FakeBluetooth>()) + .RequireCapability(echo::mojom::kServiceName, "echo") + .ExposeInterfaceFilterCapability_Deprecated( + "navigation:frame", "renderer", + service_manager::Manifest::InterfaceList< + mojom::MojoWebTestHelper>()) + .Build()}; return *manifest; }
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc index e820ba1..5ba6c88 100644 --- a/content/shell/browser/web_test/blink_test_controller.cc +++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -776,12 +776,16 @@ return bluetooth_chooser_factory_->RunBluetoothChooser(frame, event_handler); } + auto next_fake_bluetooth_chooser = WebTestContentBrowserClient::Get()->GetNextFakeBluetoothChooser(); if (next_fake_bluetooth_chooser) { - next_fake_bluetooth_chooser->SetEventHandler(event_handler); + const url::Origin origin = frame->GetLastCommittedOrigin(); + DCHECK(!origin.opaque()); + next_fake_bluetooth_chooser->OnRunBluetoothChooser(event_handler, origin); return next_fake_bluetooth_chooser; } + return std::make_unique<WebTestFirstDeviceBluetoothChooser>(event_handler); }
diff --git a/content/shell/browser/web_test/fake_bluetooth_chooser.cc b/content/shell/browser/web_test/fake_bluetooth_chooser.cc index d99451e..b38ba43a 100644 --- a/content/shell/browser/web_test/fake_bluetooth_chooser.cc +++ b/content/shell/browser/web_test/fake_bluetooth_chooser.cc
@@ -13,66 +13,89 @@ namespace content { +FakeBluetoothChooser::FakeBluetoothChooser( + mojom::FakeBluetoothChooserRequest request, + mojom::FakeBluetoothChooserClientAssociatedPtrInfo client_ptr_info) + : binding_(this, std::move(request)), + client_ptr_(std::move(client_ptr_info)) { + SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting::kNeverTimeout); +} + FakeBluetoothChooser::~FakeBluetoothChooser() { SetTestBluetoothScanDuration( BluetoothTestScanDurationSetting::kImmediateTimeout); - // TODO(https://crbug.com/719826): Send a - // mojom::ChooserEventType::CHOOSER_CLOSED event to the client. + + client_ptr_->OnEvent(mojom::FakeBluetoothChooserEvent::New( + mojom::ChooserEventType::CHOOSER_CLOSED, /*origin=*/base::nullopt, + /*peripheral_address=*/base::nullopt)); } -// static -std::unique_ptr<FakeBluetoothChooser> FakeBluetoothChooser::Create( - mojom::FakeBluetoothChooserRequest request) { - SetTestBluetoothScanDuration(BluetoothTestScanDurationSetting::kNeverTimeout); - return std::unique_ptr<FakeBluetoothChooser>( - new FakeBluetoothChooser(std::move(request))); -} - -void FakeBluetoothChooser::SetEventHandler(const EventHandler& event_handler) { +void FakeBluetoothChooser::OnRunBluetoothChooser( + const EventHandler& event_handler, + const url::Origin& origin) { event_handler_ = event_handler; + client_ptr_->OnEvent(mojom::FakeBluetoothChooserEvent::New( + mojom::ChooserEventType::CHOOSER_OPENED, origin, + /*peripheral_address=*/base::nullopt)); } // mojom::FakeBluetoothChooser overrides - -void FakeBluetoothChooser::WaitForEvents(uint32_t num_of_events, - WaitForEventsCallback callback) { - // TODO(https://crbug.com/719826): Implement this function according to the - // Web Bluetooth Test Scanning design document. - // https://docs.google.com/document/d/1XFl_4ZAgO8ddM6U53A9AfUuZeWgJnlYD5wtbXqEpzeg - NOTREACHED(); -} - void FakeBluetoothChooser::SelectPeripheral( const std::string& peripheral_address) { + DCHECK(event_handler_); event_handler_.Run(BluetoothChooser::Event::SELECTED, peripheral_address); } void FakeBluetoothChooser::Cancel() { - // TODO(https://crbug.com/719826): Send a BluetoothChooser::CANCELLED event to - // |event_handler_| and a mojom::ChooserEventType::CHOOSER_CLOSED to the - // client. - NOTREACHED(); + DCHECK(event_handler_); + event_handler_.Run(BluetoothChooser::Event::CANCELLED, std::string()); + client_ptr_->OnEvent(mojom::FakeBluetoothChooserEvent::New( + mojom::ChooserEventType::CHOOSER_CLOSED, /*origin=*/base::nullopt, + /*peripheral_address=*/base::nullopt)); } void FakeBluetoothChooser::Rescan() { - // TODO(https://crbug.com/719826): Send a BluetoothChooser::RESCAN event to - // |event_handler_| and the appropriate mojom::ChooserEventType to describe - // the discovery session. - NOTREACHED(); + DCHECK(event_handler_); + event_handler_.Run(BluetoothChooser::Event::RESCAN, std::string()); + client_ptr_->OnEvent(mojom::FakeBluetoothChooserEvent::New( + mojom::ChooserEventType::DISCOVERING, /*origin=*/base::nullopt, + /*peripheral_address=*/base::nullopt)); } // BluetoothChooser overrides void FakeBluetoothChooser::SetAdapterPresence(AdapterPresence presence) { - // TODO(https://crbug.com/719826): Send the appropriate - // mojom::ChooserEventType to the client. - NOTREACHED(); + mojom::FakeBluetoothChooserEventPtr event_ptr = + mojom::FakeBluetoothChooserEvent::New(); + switch (presence) { + case AdapterPresence::ABSENT: + event_ptr->type = mojom::ChooserEventType::ADAPTER_REMOVED; + break; + case AdapterPresence::POWERED_OFF: + event_ptr->type = mojom::ChooserEventType::ADAPTER_DISABLED; + break; + case AdapterPresence::POWERED_ON: + event_ptr->type = mojom::ChooserEventType::ADAPTER_ENABLED; + break; + } + client_ptr_->OnEvent(std::move(event_ptr)); } void FakeBluetoothChooser::ShowDiscoveryState(DiscoveryState state) { - // TODO(https://crbug.com/719826): Send the appropriate - // mojom::ChooserEventType to the client. - NOTREACHED(); + mojom::FakeBluetoothChooserEventPtr event_ptr = + mojom::FakeBluetoothChooserEvent::New(); + switch (state) { + case DiscoveryState::FAILED_TO_START: + event_ptr->type = mojom::ChooserEventType::DISCOVERY_FAILED_TO_START; + break; + case DiscoveryState::DISCOVERING: + event_ptr->type = mojom::ChooserEventType::DISCOVERING; + break; + case DiscoveryState::IDLE: + event_ptr->type = mojom::ChooserEventType::DISCOVERY_IDLE; + break; + } + client_ptr_->OnEvent(std::move(event_ptr)); } void FakeBluetoothChooser::AddOrUpdateDevice(const std::string& device_id, @@ -81,15 +104,9 @@ bool is_gatt_connected, bool is_paired, int signal_strength_level) { - // TODO(https://crbug.com/719826): Send a - // mojom::ChooserEventType::ADD_OR_UPDATE_DEVICE to the client. - NOTREACHED(); + client_ptr_->OnEvent(mojom::FakeBluetoothChooserEvent::New( + mojom::ChooserEventType::ADD_OR_UPDATE_DEVICE, + /*origin=*/base::nullopt, /*peripheral_address=*/device_id)); } -// private - -FakeBluetoothChooser::FakeBluetoothChooser( - mojom::FakeBluetoothChooserRequest request) - : binding_(this, std::move(request)) {} - } // namespace content
diff --git a/content/shell/browser/web_test/fake_bluetooth_chooser.h b/content/shell/browser/web_test/fake_bluetooth_chooser.h index fe7f6cbf5..a08fd47 100644 --- a/content/shell/browser/web_test/fake_bluetooth_chooser.h +++ b/content/shell/browser/web_test/fake_bluetooth_chooser.h
@@ -9,7 +9,9 @@ #include "content/public/browser/bluetooth_chooser.h" #include "content/shell/common/web_test/fake_bluetooth_chooser.mojom.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/binding.h" +#include "url/origin.h" namespace content { @@ -28,24 +30,25 @@ class FakeBluetoothChooser : public mojom::FakeBluetoothChooser, public BluetoothChooser { public: - // Resets the test scan duration to timeout immediately. + // FakeBluetoothChooserFactory will create an instance of this class when its + // CreateFakeBluetoothChooser() method is called. It will maintain ownership + // of the instance temporarily until the chooser is opened. When the chooser + // is opened, ownership of this instance will shift to the caller of + // WebContentsDelegate::RunBluetoothChooser. + FakeBluetoothChooser( + mojom::FakeBluetoothChooserRequest request, + mojom::FakeBluetoothChooserClientAssociatedPtrInfo client_ptr_info); + + // Resets the test scan duration to timeout immediately and sends a + // |CHOOSER_CLOSED| event to the client. ~FakeBluetoothChooser() override; - // WebTestContentBrowserClient will create an instance of this class when a - // request is bound. It will maintain ownership of the instance temporarily - // until the chooser is opened. When the chooser is opened, ownership of this - // instance will shift to the caller of - // WebContentsDelegate::RunBluetoothChooser. - static std::unique_ptr<FakeBluetoothChooser> Create( - mojom::FakeBluetoothChooserRequest request); - - // Sets the EventHandler that will handle events produced by the chooser. - void SetEventHandler(const EventHandler& event_handler); + // Sets the EventHandler that will handle events produced by the chooser, and + // sends a |CHOOSER_OPENED| event to the client with the |origin|. + void OnRunBluetoothChooser(const EventHandler& event_handler, + const url::Origin& origin); // mojom::FakeBluetoothChooser overrides: - - void WaitForEvents(uint32_t num_of_events, - WaitForEventsCallback callback) override; void SelectPeripheral(const std::string& peripheral_address) override; void Cancel() override; void Rescan() override; @@ -62,13 +65,15 @@ int signal_strength_level) override; private: - explicit FakeBluetoothChooser(mojom::FakeBluetoothChooserRequest request); - // Stores the callback function that handles chooser events. EventHandler event_handler_; mojo::Binding<mojom::FakeBluetoothChooser> binding_; + // Stores the associated pointer to the client that will be receiving events + // from FakeBluetoothChooser. + mojom::FakeBluetoothChooserClientAssociatedPtr client_ptr_; + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothChooser); };
diff --git a/content/shell/browser/web_test/fake_bluetooth_chooser_factory.cc b/content/shell/browser/web_test/fake_bluetooth_chooser_factory.cc new file mode 100644 index 0000000..1bd85d1 --- /dev/null +++ b/content/shell/browser/web_test/fake_bluetooth_chooser_factory.cc
@@ -0,0 +1,30 @@ +// 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 "content/shell/browser/web_test/fake_bluetooth_chooser_factory.h" + +#include "content/shell/browser/web_test/fake_bluetooth_chooser.h" + +namespace content { + +FakeBluetoothChooserFactory::~FakeBluetoothChooserFactory() {} + +void FakeBluetoothChooserFactory::CreateFakeBluetoothChooser( + mojom::FakeBluetoothChooserRequest request, + mojom::FakeBluetoothChooserClientAssociatedPtrInfo client_ptr_info) { + DCHECK(!next_fake_bluetooth_chooser_); + next_fake_bluetooth_chooser_ = std::make_unique<FakeBluetoothChooser>( + std::move(request), std::move(client_ptr_info)); +} + +std::unique_ptr<FakeBluetoothChooser> +FakeBluetoothChooserFactory::GetNextFakeBluetoothChooser() { + return std::move(next_fake_bluetooth_chooser_); +} + +FakeBluetoothChooserFactory::FakeBluetoothChooserFactory( + mojom::FakeBluetoothChooserFactoryRequest request) + : binding_(this, std::move(request)) {} + +} // namespace content
diff --git a/content/shell/browser/web_test/fake_bluetooth_chooser_factory.h b/content/shell/browser/web_test/fake_bluetooth_chooser_factory.h new file mode 100644 index 0000000..2ffeebcd --- /dev/null +++ b/content/shell/browser/web_test/fake_bluetooth_chooser_factory.h
@@ -0,0 +1,61 @@ +// 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 CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_CHOOSER_FACTORY_H_ +#define CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_CHOOSER_FACTORY_H_ + +#include <memory> + +#include "content/shell/common/web_test/fake_bluetooth_chooser.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" + +namespace content { + +class FakeBluetoothChooser; + +// Implementation of FakeBluetoothChooserFactory in +// src/content/shell/common/web_test/fake_bluetooth_chooser.mojom to create +// FakeBluetoothChoosers with a FakeBluetoothChooserClientAssociatedPtr that +// they can use to send events to the client. +// +// The implementation details for FakeBluetoothChooser can be found in the Web +// Bluetooth Test Scanning design document. +// https://docs.google.com/document/d/1XFl_4ZAgO8ddM6U53A9AfUuZeWgJnlYD5wtbXqEpzeg +// +// Intended to only be used through the FakeBluetoothChooser Mojo interface. +class FakeBluetoothChooserFactory : public mojom::FakeBluetoothChooserFactory { + public: + ~FakeBluetoothChooserFactory() override; + + // WebTestContentBrowserClient will create an instance of this class when a + // request is bound. It will maintain ownership of the instance. + static std::unique_ptr<FakeBluetoothChooserFactory> Create( + mojom::FakeBluetoothChooserFactoryRequest request) { + return std::unique_ptr<FakeBluetoothChooserFactory>( + new FakeBluetoothChooserFactory(std::move(request))); + } + + // Creates an instance of FakeBluetoothChooser and stores it in + // |next_fake_bluetooth_chooser_|. This will DCHECK if + // |next_fake_bluetooth_chooser_| is not null. + void CreateFakeBluetoothChooser( + mojom::FakeBluetoothChooserRequest request, + mojom::FakeBluetoothChooserClientAssociatedPtrInfo client_ptr_info) + override; + + // Transfers ownership of |next_fake_bluetooth_chooser_| to the caller. + std::unique_ptr<FakeBluetoothChooser> GetNextFakeBluetoothChooser(); + + private: + explicit FakeBluetoothChooserFactory( + mojom::FakeBluetoothChooserFactoryRequest request); + + mojo::Binding<mojom::FakeBluetoothChooserFactory> binding_; + + std::unique_ptr<FakeBluetoothChooser> next_fake_bluetooth_chooser_; +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_CHOOSER_FACTORY_H_
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc index d0c3c392..3e3f251 100644 --- a/content/shell/browser/web_test/web_test_content_browser_client.cc +++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -28,6 +28,7 @@ #include "content/shell/browser/shell_browser_context.h" #include "content/shell/browser/web_test/blink_test_controller.h" #include "content/shell/browser/web_test/fake_bluetooth_chooser.h" +#include "content/shell/browser/web_test/fake_bluetooth_chooser_factory.h" #include "content/shell/browser/web_test/mojo_web_test_helper.h" #include "content/shell/browser/web_test/web_test_bluetooth_fake_adapter_setter_impl.h" #include "content/shell/browser/web_test/web_test_browser_context.h" @@ -114,7 +115,9 @@ std::unique_ptr<FakeBluetoothChooser> WebTestContentBrowserClient::GetNextFakeBluetoothChooser() { - return std::move(next_fake_bluetooth_chooser_); + if (!fake_bluetooth_chooser_factory_) + return nullptr; + return fake_bluetooth_chooser_factory_->GetNextFakeBluetoothChooser(); } void WebTestContentBrowserClient::RenderProcessWillLaunch( @@ -148,7 +151,7 @@ // base::Unretained in all binders. registry->AddInterface( base::BindRepeating( - &WebTestContentBrowserClient::CreateFakeBluetoothChooser, + &WebTestContentBrowserClient::CreateFakeBluetoothChooserFactory, base::Unretained(this)), ui_task_runner); registry->AddInterface(base::BindRepeating(&MojoWebTestHelper::Create)); @@ -327,11 +330,11 @@ } // private -void WebTestContentBrowserClient::CreateFakeBluetoothChooser( - mojom::FakeBluetoothChooserRequest request) { - DCHECK(!next_fake_bluetooth_chooser_); - next_fake_bluetooth_chooser_ = - FakeBluetoothChooser::Create(std::move(request)); +void WebTestContentBrowserClient::CreateFakeBluetoothChooserFactory( + mojom::FakeBluetoothChooserFactoryRequest request) { + DCHECK(!fake_bluetooth_chooser_factory_); + fake_bluetooth_chooser_factory_ = + FakeBluetoothChooserFactory::Create(std::move(request)); } } // namespace content
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h index 6413e5f..8b9ae118 100644 --- a/content/shell/browser/web_test/web_test_content_browser_client.h +++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -14,6 +14,7 @@ namespace content { class FakeBluetoothChooser; +class FakeBluetoothChooserFactory; class WebTestBrowserContext; class MockClipboardHost; class MockPlatformNotificationService; @@ -88,17 +89,17 @@ LoginAuthRequiredCallback auth_required_callback) override; private: - // Creates and stores a FakeBluetoothChooser instance. - void CreateFakeBluetoothChooser(mojom::FakeBluetoothChooserRequest request); + // Creates and stores a FakeBluetoothChooserFactory instance. + void CreateFakeBluetoothChooserFactory( + mojom::FakeBluetoothChooserFactoryRequest request); void BindClipboardHost(blink::mojom::ClipboardHostRequest request); std::unique_ptr<MockPlatformNotificationService> mock_platform_notification_service_; bool block_popups_ = false; - // Stores the next instance of FakeBluetoothChooser that is to be returned - // when GetNextFakeBluetoothChooser is called. - std::unique_ptr<FakeBluetoothChooser> next_fake_bluetooth_chooser_; + // Stores the FakeBluetoothChooserFactory that produces FakeBluetoothChoosers. + std::unique_ptr<FakeBluetoothChooserFactory> fake_bluetooth_chooser_factory_; std::unique_ptr<MockClipboardHost> mock_clipboard_host_; };
diff --git a/content/shell/common/web_test/fake_bluetooth_chooser.mojom b/content/shell/common/web_test/fake_bluetooth_chooser.mojom index 2c16b75..a093ca91 100644 --- a/content/shell/common/web_test/fake_bluetooth_chooser.mojom +++ b/content/shell/common/web_test/fake_bluetooth_chooser.mojom
@@ -39,11 +39,6 @@ // FakeBluetoothChooser allows clients to control the global state of the // Bluetooth chooser during a web test. interface FakeBluetoothChooser { - // Waits until at least |num_of_events| have been recorded before returning - // |num_of_events| FakeBluetoothChooserEvents. - WaitForEvents( - uint32 num_of_events) => (array<FakeBluetoothChooserEvent> events); - // Simulates a user selecting the given |peripheral_address| in the Bluetooth // chooser. SelectPeripheral(string peripheral_address); @@ -55,6 +50,13 @@ Rescan(); }; +// FakeBluetoothChooserFactory ensures that FakeBluetoothChoosers are created +// with an associated FakeBluetoothChooserClient. +interface FakeBluetoothChooserFactory { + CreateFakeBluetoothChooser(FakeBluetoothChooser& fake_chooser, + associated FakeBluetoothChooserClient client); +}; + // FakeBluetoothChooserEvent describes the type of chooser event that has been // produced by the FakeBluetoothChooser. struct FakeBluetoothChooserEvent { @@ -67,3 +69,8 @@ // Describes the MAC address of the Bluetooth device. string? peripheral_address; }; + +// Classes that implement this interface will be notified of chooser events. +interface FakeBluetoothChooserClient { + OnEvent(FakeBluetoothChooserEvent event); +};
diff --git a/content/shell/test_runner/web_ax_object_proxy.cc b/content/shell/test_runner/web_ax_object_proxy.cc index 0e03fabc..abf42f7 100644 --- a/content/shell/test_runner/web_ax_object_proxy.cc +++ b/content/shell/test_runner/web_ax_object_proxy.cc
@@ -1657,7 +1657,8 @@ v8::Local<v8::Object> WebAXObjectProxy::ParentElement() { accessibility_object_.UpdateLayoutAndCheckValidity(); blink::WebAXObject parent_object = accessibility_object_.ParentObject(); - while (parent_object.AccessibilityIsIgnored()) + while (!parent_object.IsNull() && + !parent_object.AccessibilityIsIncludedInTree()) parent_object = parent_object.ParentObject(); return factory_->GetOrCreate(parent_object); }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index b5bbdb7..60e3dfc 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1677,6 +1677,7 @@ "../browser/resolve_proxy_msg_helper_unittest.cc", "../browser/sandbox_mac_unittest.mm", "../browser/scheduler/browser_task_executor_unittest.cc", + "../browser/scheduler/browser_task_queues_unittest.cc", "../browser/scheduler/browser_ui_thread_scheduler_unittest.cc", "../browser/scheduler/responsiveness/calculator_unittest.cc", "../browser/scheduler/responsiveness/watcher_unittest.cc",
diff --git a/content/test/test_render_widget_host_factory.cc b/content/test/test_render_widget_host_factory.cc index 29f1b88..3c63658 100644 --- a/content/test/test_render_widget_host_factory.cc +++ b/content/test/test_render_widget_host_factory.cc
@@ -16,14 +16,14 @@ RenderWidgetHostFactory::UnregisterFactory(); } -RenderWidgetHostImpl* TestRenderWidgetHostFactory::CreateRenderWidgetHost( +std::unique_ptr<RenderWidgetHostImpl> +TestRenderWidgetHostFactory::CreateRenderWidgetHost( RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id, mojom::WidgetPtr widget_interface, bool hidden) { - return TestRenderWidgetHost::Create(delegate, process, routing_id, hidden) - .release(); + return TestRenderWidgetHost::Create(delegate, process, routing_id, hidden); } } // namespace content
diff --git a/content/test/test_render_widget_host_factory.h b/content/test/test_render_widget_host_factory.h index a7c58f42..88daf95 100644 --- a/content/test/test_render_widget_host_factory.h +++ b/content/test/test_render_widget_host_factory.h
@@ -22,7 +22,7 @@ TestRenderWidgetHostFactory(); ~TestRenderWidgetHostFactory() override; - RenderWidgetHostImpl* CreateRenderWidgetHost( + std::unique_ptr<RenderWidgetHostImpl> CreateRenderWidgetHost( RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id,
diff --git a/docs/enterprise/kiosk_public_session.md b/docs/enterprise/kiosk_public_session.md new file mode 100644 index 0000000..e10517f --- /dev/null +++ b/docs/enterprise/kiosk_public_session.md
@@ -0,0 +1,39 @@ +#Kiosk mode and public sessions (ChromeOS) + +When ChromeOS device is enterprise enrolled, organization admins can add two +special types of users on the device. Those are Public sessions and Kiosk apps. + +## Public sessions + +Public session can be described as managed guest session: guest mode that is +controlled by user policy. + +It does not have any real google account, it is ephemeral (no data would be +persisted on the device after session is ended), but organization admins +still have some control over that: they can pre-install extensions, set +policies, install certificates. + +To set up public session, go to [Admin console](https://admin.google.com) under +```Device Management > Chrome Management > Device Settings```, ```Kiosk +settings``` section, and set ```Public session kiosk``` setting to +```Allow```. You can also configure additional settings. + +## Kiosk mode + +Kiosk mode is a session that runs a single Chrome/Android app. + +It does not have any real google account, it is persistent (data will be +persisted between kiosk sessions) by default. + +Multiple kiosk apps are allowed per device, and they can be launched from system +shelf on the login screen. Additionally, the administrator can set up one app to +launch automatically on start-up, in which case login screen is not shown unless +the user cancels the app launch from the app launch splash screen. + +To set up kiosk mode, go to [Admin console](https://admin.google.com) under +```Device Management > Chrome Management > Device Settings```, ```Kiosk +settings``` section, add available apps under ```Kiosk apps``` / ```Manage +Kiosk Applications```. + +You can configure kiosk app settings under ```Device Management > Chrome +Management > Apps & Extensions```
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc index 7cfcfc4..b4332ad 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
@@ -22,6 +22,7 @@ #include "components/app_modal/native_app_modal_dialog.h" #include "components/guest_view/browser/test_guest_view_manager.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" @@ -574,3 +575,41 @@ } EXPECT_EQ(GetMouseCaptureWidget(embedder_web_contents), guest_widget); } + +// Helper class to wait for document load event in the main frame. +class DocumentLoadComplete : public content::WebContentsObserver { + public: + explicit DocumentLoadComplete(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + ~DocumentLoadComplete() override {} + + void DocumentOnLoadCompletedInMainFrame() override { + did_load_ = true; + run_loop_.Quit(); + } + + void Wait() { + if (!did_load_) + run_loop_.Run(); + } + + private: + bool did_load_ = false; + base::RunLoop run_loop_; +}; + +IN_PROC_BROWSER_TEST_P(MimeHandlerViewCrossProcessTest, + ActivatePostMessageSupportOnce) { + RunTest("test_embedded.html"); + // Attach a second <embed>. + ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), + "const e = document.createElement('embed');" + "e.src = './testEmbedded.csv'; e.type='text/csv';" + "document.body.appendChild(e);")); + DocumentLoadComplete(GetGuestViewManager()->WaitForNextGuestCreated()).Wait(); + // After load, an IPC has been sent to the renderer to update routing IDs for + // the guest frame and the content frame (and activate the + // PostMessageSupport). Run some JS to Ensure no DCHECKs have fired in the + // embedder process. + ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), "foo = 0;")); +} \ No newline at end of file
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn index ccae9b3..acdd450 100644 --- a/extensions/renderer/BUILD.gn +++ b/extensions/renderer/BUILD.gn
@@ -14,6 +14,10 @@ "activity_log_converter_strategy.h", "api/automation/automation_api_helper.cc", "api/automation/automation_api_helper.h", + "api/automation/automation_ax_tree_wrapper.cc", + "api/automation/automation_ax_tree_wrapper.h", + "api/automation/automation_internal_custom_bindings.cc", + "api/automation/automation_internal_custom_bindings.h", "api/display_source/display_source_session.cc", "api/display_source/display_source_session.h", "api_activity_logger.cc", @@ -180,6 +184,7 @@ "resource_bundle_source_map.h", "resources/app_runtime_custom_bindings.js", "resources/app_window_custom_bindings.js", + "resources/automation/automation_custom_bindings.js", "resources/context_menus_custom_bindings.js", "resources/declarative_webrequest_custom_bindings.js", "resources/entry_id_manager.js",
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS index 13a758e..381d3c2 100644 --- a/extensions/renderer/DEPS +++ b/extensions/renderer/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+components/translate/core/language_detection", + "+content/app/strings/grit", "+content/public/child", "+content/public/renderer", @@ -22,6 +23,8 @@ "+tools/json_schema_compiler", + "+ui/accessibility", + "-v8", "+v8/include",
diff --git a/extensions/renderer/api/automation/OWNERS b/extensions/renderer/api/automation/OWNERS new file mode 100644 index 0000000..292ee758 --- /dev/null +++ b/extensions/renderer/api/automation/OWNERS
@@ -0,0 +1,3 @@ +file://ui/accessibility/OWNERS + +# COMPONENT: UI>Accessibility
diff --git a/chrome/renderer/extensions/automation_ax_tree_wrapper.cc b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc similarity index 98% rename from chrome/renderer/extensions/automation_ax_tree_wrapper.cc rename to extensions/renderer/api/automation/automation_ax_tree_wrapper.cc index fd34397..0a4f855df 100644 --- a/chrome/renderer/extensions/automation_ax_tree_wrapper.cc +++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
@@ -3,9 +3,8 @@ // found in the LICENSE file. #include "base/no_destructor.h" -#include "chrome/common/extensions/chrome_extension_messages.h" -#include "chrome/renderer/extensions/automation_internal_custom_bindings.h" #include "extensions/common/extension_messages.h" +#include "extensions/renderer/api/automation/automation_internal_custom_bindings.h" #include "ui/accessibility/ax_language_info.h" #include "ui/accessibility/ax_node.h"
diff --git a/chrome/renderer/extensions/automation_ax_tree_wrapper.h b/extensions/renderer/api/automation/automation_ax_tree_wrapper.h similarity index 93% rename from chrome/renderer/extensions/automation_ax_tree_wrapper.h rename to extensions/renderer/api/automation/automation_ax_tree_wrapper.h index 0d4f12d..b49266fd 100644 --- a/chrome/renderer/extensions/automation_ax_tree_wrapper.h +++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.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 CHROME_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_ -#define CHROME_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_ +#ifndef EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_AX_TREE_WRAPPER_H_ +#define EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_AX_TREE_WRAPPER_H_ #include "extensions/common/api/automation.h" #include "ui/accessibility/ax_event_generator.h" @@ -81,4 +81,4 @@ } // namespace extensions -#endif // CHROME_RENDERER_EXTENSIONS_AUTOMATION_AX_TREE_WRAPPER_H_ +#endif // EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_AX_TREE_WRAPPER_H_
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc similarity index 99% rename from chrome/renderer/extensions/automation_internal_custom_bindings.cc rename to extensions/renderer/api/automation/automation_internal_custom_bindings.cc index 5f8e9aaf..6b55c54 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc +++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.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 "chrome/renderer/extensions/automation_internal_custom_bindings.h" +#include "extensions/renderer/api/automation/automation_internal_custom_bindings.h" #include <stddef.h> #include <stdint.h> @@ -19,7 +19,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" -#include "chrome/common/extensions/chrome_extension_messages.h" #include "content/app/strings/grit/content_strings.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" @@ -1243,16 +1242,12 @@ AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, const std::string& search_str, bool backward) { base::string16 search_str_16 = base::UTF8ToUTF16(search_str); - auto func = + auto next = backward ? &AutomationInternalCustomBindings::GetPreviousInTreeOrder : &AutomationInternalCustomBindings::GetNextInTreeOrder; - std::function<ui::AXNode*(ui::AXNode*, AutomationAXTreeWrapper**)> next( - std::bind(func, this, std::placeholders::_1, - std::placeholders::_2)); - AutomationAXTreeWrapper** target_tree_wrapper = &tree_wrapper; while (true) { - node = next(node, target_tree_wrapper); + node = (this->*next)(node, target_tree_wrapper); // We explicitly disallow searches in the desktop tree. if ((*target_tree_wrapper)->IsDesktopTree())
diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.h b/extensions/renderer/api/automation/automation_internal_custom_bindings.h similarity index 95% rename from chrome/renderer/extensions/automation_internal_custom_bindings.h rename to extensions/renderer/api/automation/automation_internal_custom_bindings.h index 4ad225b8..db2433c 100644 --- a/chrome/renderer/extensions/automation_internal_custom_bindings.h +++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.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 CHROME_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ -#define CHROME_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ +#ifndef EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ +#define EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ #include <map> #include <string> @@ -11,8 +11,8 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "chrome/renderer/extensions/automation_ax_tree_wrapper.h" #include "extensions/common/api/automation.h" +#include "extensions/renderer/api/automation/automation_ax_tree_wrapper.h" #include "extensions/renderer/object_backed_native_handler.h" #include "ipc/ipc_message.h" #include "ui/accessibility/ax_tree.h" @@ -221,4 +221,4 @@ } // namespace extensions -#endif // CHROME_RENDERER_EXTENSIONS_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_ +#endif // EXTENSIONS_RENDERER_API_AUTOMATION_AUTOMATION_INTERNAL_CUSTOM_BINDINGS_H_
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc index d7cd4dd..8948910 100644 --- a/extensions/renderer/dispatcher.cc +++ b/extensions/renderer/dispatcher.cc
@@ -53,6 +53,7 @@ #include "extensions/common/switches.h" #include "extensions/common/view_type.h" #include "extensions/grit/extensions_renderer_resources.h" +#include "extensions/renderer/api/automation/automation_internal_custom_bindings.h" #include "extensions/renderer/api_activity_logger.h" #include "extensions/renderer/api_definitions_natives.h" #include "extensions/renderer/app_window_custom_bindings.h" @@ -688,6 +689,9 @@ {"extensions/common/mojo/keep_alive.mojom", IDR_KEEP_ALIVE_MOJOM_JS}, // Custom bindings. + {"automation", IDR_AUTOMATION_CUSTOM_BINDINGS_JS}, + {"automationEvent", IDR_AUTOMATION_EVENT_JS}, + {"automationNode", IDR_AUTOMATION_NODE_JS}, {"app.runtime", IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS}, {"app.window", IDR_APP_WINDOW_CUSTOM_BINDINGS_JS}, {"declarativeWebRequest", IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS}, @@ -784,6 +788,10 @@ module_system->RegisterNativeHandler( "display_source", std::make_unique<DisplaySourceCustomBindings>(context, bindings_system)); + module_system->RegisterNativeHandler( + "automationInternal", + std::make_unique<extensions::AutomationInternalCustomBindings>( + context, bindings_system)); } bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) {
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc index 07e7fa6..948e0ed2 100644 --- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc +++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
@@ -154,6 +154,8 @@ return; } for (auto& frame_container : frame_containers_) { + if (frame_container->post_message_support()->is_active()) + continue; if (frame_container->resource_url() != resource_url) continue; // To ensure the postMessages will be sent to the right target frame, we
diff --git a/extensions/renderer/resources/automation/OWNERS b/extensions/renderer/resources/automation/OWNERS new file mode 100644 index 0000000..292ee758 --- /dev/null +++ b/extensions/renderer/resources/automation/OWNERS
@@ -0,0 +1,3 @@ +file://ui/accessibility/OWNERS + +# COMPONENT: UI>Accessibility
diff --git a/chrome/renderer/resources/extensions/automation_custom_bindings.js b/extensions/renderer/resources/automation/automation_custom_bindings.js similarity index 100% rename from chrome/renderer/resources/extensions/automation_custom_bindings.js rename to extensions/renderer/resources/automation/automation_custom_bindings.js
diff --git a/chrome/renderer/resources/extensions/automation/automation_event.js b/extensions/renderer/resources/automation/automation_event.js similarity index 100% rename from chrome/renderer/resources/extensions/automation/automation_event.js rename to extensions/renderer/resources/automation/automation_event.js
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/extensions/renderer/resources/automation/automation_node.js similarity index 100% rename from chrome/renderer/resources/extensions/automation/automation_node.js rename to extensions/renderer/resources/automation/automation_node.js
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd index ecc12528..5824ac5 100644 --- a/extensions/renderer/resources/extensions_renderer_resources.grd +++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -55,6 +55,9 @@ <include name="IDR_WEB_VIEW_ELEMENT_JS" file="guest_view/web_view/web_view_element.js" type="BINDATA" /> <!-- Custom bindings for APIs. --> + <include name="IDR_AUTOMATION_CUSTOM_BINDINGS_JS" file="automation\automation_custom_bindings.js" type="BINDATA" /> + <include name="IDR_AUTOMATION_EVENT_JS" file="automation\automation_event.js" type="BINDATA" /> + <include name="IDR_AUTOMATION_NODE_JS" file="automation\automation_node.js" type="BINDATA" /> <include name="IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS" file="app_runtime_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="app_window_custom_bindings.js" type="BINDATA" /> <include name="IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS" file="context_menus_custom_bindings.js" type="BINDATA" />
diff --git a/fuchsia/runners/cast/named_message_port_connector.cc b/fuchsia/runners/cast/named_message_port_connector.cc index 156473c8..4b59300 100644 --- a/fuchsia/runners/cast/named_message_port_connector.cc +++ b/fuchsia/runners/cast/named_message_port_connector.cc
@@ -129,4 +129,6 @@ } port_connected_handlers_[port_name].Run( std::move(transferable.message_port())); + + ReceiveNextConnectRequest(); }
diff --git a/fuchsia/runners/cast/named_message_port_connector_browsertest.cc b/fuchsia/runners/cast/named_message_port_connector_browsertest.cc index 7dd729aa..a68abec 100644 --- a/fuchsia/runners/cast/named_message_port_connector_browsertest.cc +++ b/fuchsia/runners/cast/named_message_port_connector_browsertest.cc
@@ -72,8 +72,7 @@ DISALLOW_COPY_AND_ASSIGN(NamedMessagePortConnectorTest); }; -IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, - NamedMessagePortConnectorEndToEnd) { +IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, EndToEnd) { ASSERT_TRUE(embedded_test_server()->Start()); GURL test_url(embedded_test_server()->GetURL("/connector.html")); @@ -135,3 +134,67 @@ connector_->Unregister("hello"); } + +// Tests that the NamedMessagePortConnector can receive more than one port over +// its lifetime. +IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL test_url( + embedded_test_server()->GetURL("/connector_multiple_ports.html")); + + fuchsia::web::NavigationControllerPtr controller; + frame_->GetNavigationController(controller.NewRequest()); + + base::RunLoop receive_port_1_run_loop; + base::RunLoop receive_port_2_run_loop; + cr_fuchsia::ResultReceiver<fidl::InterfaceHandle<fuchsia::web::MessagePort>> + port_1_receiver(receive_port_1_run_loop.QuitClosure()); + cr_fuchsia::ResultReceiver<fidl::InterfaceHandle<fuchsia::web::MessagePort>> + port_2_receiver(receive_port_2_run_loop.QuitClosure()); + connector_->Register( + "port1", + base::BindRepeating( + &cr_fuchsia::ResultReceiver< + fidl::InterfaceHandle<fuchsia::web::MessagePort>>::ReceiveResult, + base::Unretained(&port_1_receiver))); + connector_->Register( + "port2", + base::BindRepeating( + &cr_fuchsia::ResultReceiver< + fidl::InterfaceHandle<fuchsia::web::MessagePort>>::ReceiveResult, + base::Unretained(&port_2_receiver))); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec())); + navigation_listener_.RunUntilUrlEquals(test_url); + + receive_port_1_run_loop.Run(); + receive_port_2_run_loop.Run(); + + fuchsia::web::MessagePortPtr port_1 = (*port_1_receiver).Bind(); + fuchsia::web::MessagePortPtr port_2 = (*port_2_receiver).Bind(); + + for (fuchsia::web::MessagePortPtr* port : {&port_1, &port_2}) { + fuchsia::web::WebMessage msg; + msg.set_data(cr_fuchsia::MemBufferFromString("ping")); + cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> + post_result; + (*port)->PostMessage(std::move(msg), cr_fuchsia::CallbackToFitFunction( + post_result.GetReceiveCallback())); + + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> message_receiver( + run_loop.QuitClosure()); + (*port)->ReceiveMessage(cr_fuchsia::CallbackToFitFunction( + message_receiver.GetReceiveCallback())); + run_loop.Run(); + + std::string data; + ASSERT_TRUE(message_receiver->has_data()); + ASSERT_TRUE( + cr_fuchsia::StringFromMemBuffer(message_receiver->data(), &data)); + EXPECT_EQ(data, "ack ping"); + } + + connector_->Unregister("port1"); + connector_->Unregister("port2"); +}
diff --git a/fuchsia/runners/cast/testdata/connector_multiple_ports.html b/fuchsia/runners/cast/testdata/connector_multiple_ports.html new file mode 100644 index 0000000..69e3a46a --- /dev/null +++ b/fuchsia/runners/cast/testdata/connector_multiple_ports.html
@@ -0,0 +1,17 @@ +<html> + <head><title>so many ports wow</title></head> + <body> + <script> + var port1 = cast.__platform__.PortConnector.bind('port1'); + var port2 = cast.__platform__.PortConnector.bind('port2'); + + function ackMsg(msg) { + this.postMessage('ack ' + msg.data); + return true; + } + + port1.onmessage = ackMsg.bind(port1); + port2.onmessage = ackMsg.bind(port2); + </script> + </body> +</html>
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn index b46a0f23..6098f3b 100644 --- a/google_apis/BUILD.gn +++ b/google_apis/BUILD.gn
@@ -84,6 +84,8 @@ template("google_apis_tmpl") { source_set(target_name) { sources = [ + "gaia/core_account_id.cc", + "gaia/core_account_id.h", "gaia/gaia_auth_consumer.cc", "gaia/gaia_auth_consumer.h", "gaia/gaia_auth_fetcher.cc",
diff --git a/google_apis/gaia/core_account_id.cc b/google_apis/gaia/core_account_id.cc new file mode 100644 index 0000000..d808082 --- /dev/null +++ b/google_apis/gaia/core_account_id.cc
@@ -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. + +#include "google_apis/gaia/core_account_id.h" + +CoreAccountId::CoreAccountId() = default; + +CoreAccountId::~CoreAccountId() = default; + +CoreAccountId::CoreAccountId(const char* id) : id(id) {} + +CoreAccountId::CoreAccountId(std::string&& id) : id(std::move(id)) {} + +CoreAccountId::CoreAccountId(const std::string& id) : id(id) {} + +CoreAccountId::operator std::string() const { + return id; +} + +bool CoreAccountId::empty() const { + return id.empty(); +} + +bool operator<(const CoreAccountId& lhs, const CoreAccountId& rhs) { + return lhs.id < rhs.id; +} + +bool operator==(const CoreAccountId& lhs, const CoreAccountId& rhs) { + return lhs.id == rhs.id; +} + +bool operator!=(const CoreAccountId& lhs, const CoreAccountId& rhs) { + return lhs.id != rhs.id; +} + +std::ostream& operator<<(std::ostream& out, const CoreAccountId& a) { + return out << a.id; +}
diff --git a/google_apis/gaia/core_account_id.h b/google_apis/gaia/core_account_id.h new file mode 100644 index 0000000..5ea602a --- /dev/null +++ b/google_apis/gaia/core_account_id.h
@@ -0,0 +1,43 @@ +// 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 GOOGLE_APIS_GAIA_CORE_ACCOUNT_ID_H_ +#define GOOGLE_APIS_GAIA_CORE_ACCOUNT_ID_H_ + +#include <ostream> +#include <string> + +// Represent the id of an account for interaction with GAIA. It is +// currently implicitly convertible to and from std::string to allow +// progressive migration of the code (see https://crbug.com/959157 +// for design and tracking). +struct CoreAccountId { + CoreAccountId(); + ~CoreAccountId(); + + // Those implicit constructor and conversion operator allow to + // progressively migrate the code to use this struct. Removing + // them is tracked by https://crbug.com/959161 + CoreAccountId(const char* id); + CoreAccountId(std::string&& id); + CoreAccountId(const std::string& id); + operator std::string() const; + + // Checks if the account is valid or not. + // TODO(triploblastic): Possibly rename of remove this after + // refactoring. + bool empty() const; + + std::string id; +}; + +bool operator<(const CoreAccountId& lhs, const CoreAccountId& rhs); + +bool operator==(const CoreAccountId& lhs, const CoreAccountId& rhs); + +bool operator!=(const CoreAccountId& lhs, const CoreAccountId& rhs); + +std::ostream& operator<<(std::ostream& out, const CoreAccountId& a); + +#endif // GOOGLE_APIS_GAIA_CORE_ACCOUNT_ID_H_
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index 53cb426..6630774 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -983,6 +983,13 @@ } builders { + name: "android-asan" + mixins: "builderless" + mixins: "memory-ci" + dimensions: "os:Ubuntu-16.04" + } + + builders { name: "android-cronet-arm-dbg" mixins: "android-ci" dimensions: "os:Ubuntu-14.04" @@ -3510,8 +3517,7 @@ mixins: "goma-j300" name: "android-marshmallow-arm64-rel" dimensions: "os:Ubuntu-14.04" - # TODO(hinoka): Make this 16 core after crbug.com/875708 - dimensions: "cores:" + dimensions: "cores:16" } builders { mixins: "android-try" @@ -4034,6 +4040,7 @@ builders { mixins: "win-try" mixins: "goma-j300" + # TODO(crbug/881609): Add 'dimensions: "ssd:1"'. name: "win7-rel" execution_timeout_secs: 16200 # 4.5h }
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg index cfe001b1..4177633 100644 --- a/infra/config/luci-milo.cfg +++ b/infra/config/luci-milo.cfg
@@ -755,6 +755,11 @@ short_name: "tst" } builders { + name: "buildbucket/luci.chromium.ci/android-asan" + category: "chromium.memory|android" + short_name: "asn" + } + builders { name: "buildbot/chromium.memory/Android CFI" name: "buildbucket/luci.chromium.ci/Android CFI" category: "chromium.memory|cfi" @@ -1176,6 +1181,11 @@ short_name: "tst" } builders { + name: "buildbucket/luci.chromium.ci/android-asan" + category: "android" + short_name: "asn" + } + builders { name: "buildbot/chromium.memory/Android CFI" name: "buildbucket/luci.chromium.ci/Android CFI" category: "cfi"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg index 416e388..0562b374 100644 --- a/infra/config/luci-scheduler.cfg +++ b/infra/config/luci-scheduler.cfg
@@ -309,6 +309,7 @@ triggers: "WinMSVC64 Goma Canary" triggers: "WinMSVC64 Goma Latest Client" triggers: "Windows deterministic" + triggers: "android-asan" triggers: "android-cronet-arm-dbg" triggers: "android-cronet-arm-rel" triggers: "android-cronet-arm64-dbg" @@ -723,6 +724,16 @@ } job { + id: "android-asan" + acl_sets: "default" + buildbucket: { + server: "cr-buildbucket.appspot.com" + bucket: "luci.chromium.ci" + builder: "android-asan" + } +} + +job { id: "android-cronet-arm-dbg" acl_sets: "default" buildbucket: {
diff --git a/ios/build/bots/tests/eg_tests.json b/ios/build/bots/tests/eg_tests.json index a3ec77b..c0b4f123 100644 --- a/ios/build/bots/tests/eg_tests.json +++ b/ios/build/bots/tests/eg_tests.json
@@ -41,6 +41,10 @@ "app": "ios_showcase_egtests", "xctest": true, "xcode parallelization": true + }, + { + "app": "ios_chrome_unified_consent_egtests", + "xctest": true } ] }
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index b4bf6a4..0b96734 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -837,6 +837,9 @@ <message name="IDS_IOS_LONG_PRESS_TOOLBAR_IPH_PROMOTION_TEXT" desc="Text for the LongPress Toolbar Tip in-product help promotion, explaining that the user can long press on the toolbar's button to display more options. [iOS only]"> Press and hold for more tab options </message> + <message name="IDS_IOS_MANAGE_YOUR_GOOGLE_ACCOUNT_TITLE" desc="Title for the view in the Settings to open 'Google Account' web page."> + Manage Your Google Account + </message> <message name="IDS_IOS_MANAGE_SYNC_SETTINGS_TITLE" desc="Title for the view in the Settings for enabling/disabling Sync. [iOS only]"> Manage Sync </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MANAGE_YOUR_GOOGLE_ACCOUNT_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MANAGE_YOUR_GOOGLE_ACCOUNT_TITLE.png.sha1 new file mode 100644 index 0000000..54b035c --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_MANAGE_YOUR_GOOGLE_ACCOUNT_TITLE.png.sha1
@@ -0,0 +1 @@ +52e0e646914c86c0381be5645a0bc8b4abba3c1a \ No newline at end of file
diff --git a/ios/chrome/browser/chrome_url_constants.cc b/ios/chrome/browser/chrome_url_constants.cc index bcb79ab6..36421fc 100644 --- a/ios/chrome/browser/chrome_url_constants.cc +++ b/ios/chrome/browser/chrome_url_constants.cc
@@ -77,6 +77,8 @@ const char kSyncGoogleDashboardURL[] = "https://www.google.com/settings/chrome/sync/"; +const char kManageYourGoogleAccountURL[] = "https://myaccount.google.com/"; + const char kPageInfoHelpCenterURL[] = "https://support.google.com/chrome?p=ui_security_indicator&ios=1";
diff --git a/ios/chrome/browser/chrome_url_constants.h b/ios/chrome/browser/chrome_url_constants.h index 96a19ca4..f3b71763 100644 --- a/ios/chrome/browser/chrome_url_constants.h +++ b/ios/chrome/browser/chrome_url_constants.h
@@ -71,6 +71,9 @@ // URL to the sync google dashboard. extern const char kSyncGoogleDashboardURL[]; +// URL to the Google account configuration page. +extern const char kManageYourGoogleAccountURL[]; + // "What do these mean?" URL for the Page Info bubble. extern const char kPageInfoHelpCenterURL[];
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm index 7b34acf..b2837d9 100644 --- a/ios/chrome/browser/signin/authentication_service.mm +++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -274,7 +274,7 @@ identity_manager_->GetAccountsWithRefreshTokens()); std::vector<base::Value> accounts_pref_value; for (const CoreAccountInfo& account_info : accounts) - accounts_pref_value.emplace_back(account_info.account_id); + accounts_pref_value.emplace_back(account_info.account_id.id); pref_service_->Set(prefs::kSigninLastAccounts, base::Value(std::move(accounts_pref_value))); }
diff --git a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm index 997a92ba..0dcb43af 100644 --- a/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm +++ b/ios/chrome/browser/ui/alert_view_controller/alert_view_controller.mm
@@ -66,7 +66,8 @@ } // namespace -@interface AlertViewController () <UITextFieldDelegate> +@interface AlertViewController () <UITextFieldDelegate, + UIGestureRecognizerDelegate> // The actions for to this alert. |copy| for safety against mutable objects. @property(nonatomic, copy) NSArray<AlertAction*>* actions; @@ -90,6 +91,17 @@ // The text fields that had been added to this alert. @property(nonatomic, strong) NSArray<UITextField*>* textFields; +// Recognizer used to dismiss the keyboard when tapping outside the container +// view. +@property(nonatomic, strong) UITapGestureRecognizer* tapRecognizer; + +// Recognizer used to dismiss the keyboard swipping down the alert. +@property(nonatomic, strong) UISwipeGestureRecognizer* swipeRecognizer; + +// This is the last focused text field, the gestures to dismiss the keyboard +// will end up calling |resignFirstResponder| on this. +@property(nonatomic, weak) UITextField* lastFocusedTextField; + @end @implementation AlertViewController @@ -101,6 +113,13 @@ self.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:kBackgroundAlpha]; + self.tapRecognizer = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(dismissKeyboard)]; + self.tapRecognizer.enabled = NO; + self.tapRecognizer.delegate = self; + [self.view addGestureRecognizer:self.tapRecognizer]; + self.contentView = [[UIView alloc] init]; self.contentView.clipsToBounds = YES; self.contentView.backgroundColor = [UIColor whiteColor]; @@ -112,6 +131,13 @@ self.contentView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:self.contentView]; + self.swipeRecognizer = [[UISwipeGestureRecognizer alloc] + initWithTarget:self + action:@selector(dismissKeyboard)]; + self.swipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown; + self.swipeRecognizer.enabled = NO; + [self.contentView addGestureRecognizer:self.swipeRecognizer]; + BOOL isAccessibilityContentSize = UIContentSizeCategoryIsAccessibilityCategory( [UIApplication sharedApplication].preferredContentSizeCategory); @@ -126,9 +152,9 @@ // Centering [self.contentView.centerXAnchor - constraintEqualToAnchor:self.view.centerXAnchor], + constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor], [self.contentView.centerYAnchor - constraintEqualToAnchor:self.view.centerYAnchor], + constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerYAnchor], // Minimum Size [self.contentView.heightAnchor @@ -378,8 +404,23 @@ return results; } +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldReceiveTouch:(UITouch*)touch { + if (self.tapRecognizer != gestureRecognizer) { + return YES; + } + CGPoint locationInContentView = [touch locationInView:self.contentView]; + return !CGRectContainsPoint(self.contentView.bounds, locationInContentView); +} + #pragma mark - UITextFieldDelegate +- (void)textFieldDidBeginEditing:(UITextField*)textField { + self.lastFocusedTextField = textField; +} + - (BOOL)textFieldShouldReturn:(UITextField*)textField { NSUInteger index = [self.textFields indexOfObject:textField]; if (index + 1 < self.textFields.count) { @@ -397,10 +438,14 @@ [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; self.additionalSafeAreaInsets = UIEdgeInsetsMake(0, 0, keyboardFrame.size.height, 0); + self.tapRecognizer.enabled = YES; + self.swipeRecognizer.enabled = YES; } - (void)handleKeyboardWillHide:(NSNotification*)notification { self.additionalSafeAreaInsets = UIEdgeInsetsZero; + self.tapRecognizer.enabled = NO; + self.swipeRecognizer.enabled = NO; } - (void)didSelectActionForButton:(UIButton*)button { @@ -410,4 +455,8 @@ } } +- (void)dismissKeyboard { + [self.lastFocusedTextField resignFirstResponder]; +} + @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h index e3e540c..95e1c5d89 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h
@@ -55,8 +55,6 @@ // Image view for the leading image. @property(nonatomic, strong, readonly) UIImageView* leadingImageView; -// Trailing image view for images from suggestions (e.g. weather). -@property(nonatomic, strong, readonly) UIImageView* answerImageView; @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm index 4296dbb..c30c6f6 100644 --- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm +++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
@@ -26,7 +26,6 @@ #endif namespace { -const CGFloat kImageViewCornerRadius = 7; const CGFloat kTextTopMargin = 6; const CGFloat kTrailingButtonSize = 24; const CGFloat kTrailingButtonTrailingMargin = 14; @@ -89,7 +88,6 @@ _leadingImageView = [[UIImageView alloc] initWithImage:nil]; _leadingImageView.translatesAutoresizingMaskIntoConstraints = NO; _leadingImageView.contentMode = UIViewContentModeCenter; - _leadingImageView.layer.cornerRadius = kImageViewCornerRadius; _trailingButton = [ExtendedTouchTargetButton buttonWithType:UIButtonTypeCustom]; @@ -99,18 +97,6 @@ action:@selector(trailingButtonTapped) forControlEvents:UIControlEventTouchUpInside]; - _answerImageView = [[UIImageView alloc] initWithImage:nil]; - _answerImageView.translatesAutoresizingMaskIntoConstraints = NO; - [_answerImageView - setContentHuggingPriority:UILayoutPriorityDefaultHigh - forAxis:UILayoutConstraintAxisHorizontal]; - [_answerImageView setContentHuggingPriority:UILayoutPriorityDefaultHigh - forAxis:UILayoutConstraintAxisVertical]; - [_answerImageView - setContentCompressionResistancePriority:UILayoutPriorityRequired - forAxis: - UILayoutConstraintAxisHorizontal]; - _separator = [[UIView alloc] initWithFrame:CGRectZero]; _separator.translatesAutoresizingMaskIntoConstraints = NO; _separator.backgroundColor = [MDCPalette.cr_greyPalette tint200]; @@ -198,20 +184,6 @@ ]]; } -// Add the answer image view as a subview and setup its constraints. -- (void)setupAnswerImageViewLayout { - [self.contentView addSubview:self.answerImageView]; - [NSLayoutConstraint activateConstraints:@[ - [self.answerImageView.centerYAnchor - constraintEqualToAnchor:self.contentView.centerYAnchor], - [self.contentView.trailingAnchor - constraintEqualToAnchor:self.answerImageView.trailingAnchor - constant:kTrailingButtonTrailingMargin], - [self.answerImageView.leadingAnchor - constraintEqualToAnchor:self.textStackView.trailingAnchor], - ]]; -} - - (void)attachToLayoutGuides { NamedGuide* imageLayoutGuide = [NamedGuide guideWithName:kOmniboxLeadingImageGuide view:self]; @@ -291,11 +263,11 @@ self.detailTruncatingLabel.attributedText = nil; self.detailAnswerLabel.attributedText = nil; + self.leadingImageView.image = nil; + // Remove optional views. [self.trailingButton setImage:nil forState:UIControlStateNormal]; [self.trailingButton removeFromSuperview]; - self.answerImageView.image = nil; - [self.answerImageView removeFromSuperview]; [self.detailTruncatingLabel removeFromSuperview]; [self.detailAnswerLabel removeFromSuperview]; @@ -337,18 +309,45 @@ [self setupLeadingImageView]; - if (self.suggestion.hasImage) { - [self setupAnswerImageView]; - } else if (self.suggestion.isAppendable || self.suggestion.isTabMatch) { + if (self.suggestion.isAppendable || self.suggestion.isTabMatch) { [self setupTrailingButton]; } } -// Populate the leading image view with the correct icon and color. +// Populate the leading image view with the correct image and color. - (void)setupLeadingImageView { + if (self.suggestion.hasImage) { + [self setupLeadingImageViewForAnswerImage]; + } else { + [self setupLeadingImageViewForIconAndFavicon]; + } +} + +// Populate the leading image view in the case where the image should be an +// image from answers-in-suggest or rich entity. +- (void)setupLeadingImageViewForAnswerImage { + self.leadingImageView.contentMode = UIViewContentModeScaleAspectFill; + __weak OmniboxPopupRowCell* weakSelf = self; + GURL imageURL = self.suggestion.imageURL; + [self.imageRetriever fetchImage:imageURL + completion:^(UIImage* image) { + // Make sure cell is still displaying the same + // suggestion. + if (weakSelf.suggestion.imageURL != imageURL) { + return; + } + weakSelf.leadingImageView.image = image; + }]; + self.leadingImageView.backgroundColor = nil; +} + +// Populate the leading image view in the case where the image should be a +// standard suggestion icon or a favicon. +- (void)setupLeadingImageViewForIconAndFavicon { + self.leadingImageView.contentMode = UIViewContentModeCenter; self.leadingImageView.image = self.suggestion.suggestionTypeIcon; - // Load favicon. + // Attempt to load favicon. GURL pageURL = self.suggestion.faviconPageURL; __weak OmniboxPopupRowCell* weakSelf = self; [self.faviconRetriever fetchFavicon:pageURL @@ -365,23 +364,6 @@ : [UIColor colorWithWhite:0 alpha:0.33]; } -// Setup the answer image view. This includes both setting up its layout and -// populating the image correctly. -- (void)setupAnswerImageView { - [self setupAnswerImageViewLayout]; - __weak OmniboxPopupRowCell* weakSelf = self; - GURL imageURL = self.suggestion.imageURL; - [self.imageRetriever fetchImage:imageURL - completion:^(UIImage* image) { - // Make sure cell is still displaying the same - // suggestion. - if (weakSelf.suggestion.imageURL != imageURL) { - return; - } - weakSelf.answerImageView.image = image; - }]; -} - // Setup the trailing button. This includes both setting up the button's layout // and popuplating it with the correct image and color. - (void)setupTrailingButton {
diff --git a/ios/chrome/browser/ui/settings/google_services/BUILD.gn b/ios/chrome/browser/ui/settings/google_services/BUILD.gn index 462a1bd..7dee5dc 100644 --- a/ios/chrome/browser/ui/settings/google_services/BUILD.gn +++ b/ios/chrome/browser/ui/settings/google_services/BUILD.gn
@@ -117,6 +117,7 @@ "//base", "//base/test:test_support", "//components/prefs", + "//components/unified_consent", "//ios/chrome/app:app_internal", "//ios/chrome/app/strings:ios_chromium_strings_grit", "//ios/chrome/app/strings:ios_strings_grit",
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h b/ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h index c5bba14c..a654f9c 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h
@@ -26,6 +26,9 @@ // Opens the manage sync settings view. - (void)openManageSyncSettings; +// Open the "Manage Your Google Account" web page +- (void)openManageGoogleAccountWebPage; + @end #endif // IOS_CHROME_BROWSER_UI_SETTINGS_GOOGLE_SERVICES_GOOGLE_SERVICES_SETTINGS_COMMAND_HANDLER_H_
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm index 3a4ef41..9e30da4 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.mm
@@ -5,8 +5,10 @@ #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_coordinator.h" #include "base/mac/foundation_util.h" +#include "components/google/core/common/google_util.h" #include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#include "ios/chrome/browser/chrome_url_constants.h" #include "ios/chrome/browser/signin/authentication_service.h" #import "ios/chrome/browser/signin/authentication_service_factory.h" #include "ios/chrome/browser/signin/identity_manager_factory.h" @@ -15,6 +17,7 @@ #include "ios/chrome/browser/sync/sync_setup_service_factory.h" #import "ios/chrome/browser/ui/authentication/authentication_flow.h" #import "ios/chrome/browser/ui/commands/application_commands.h" +#import "ios/chrome/browser/ui/commands/open_new_tab_command.h" #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.h" #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h" #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.h" @@ -239,6 +242,14 @@ [self.manageSyncSettingsCoordinator start]; } +- (void)openManageGoogleAccountWebPage { + GURL url = google_util::AppendGoogleLocaleParam( + GURL(kManageYourGoogleAccountURL), + GetApplicationContext()->GetApplicationLocale()); + OpenNewTabCommand* command = [OpenNewTabCommand commandWithURLFromChrome:url]; + [self.dispatcher closeSettingsUIAndOpenURL:command]; +} + #pragma mark - GoogleServicesSettingsViewControllerPresentationDelegate - (void)googleServicesSettingsViewControllerDidRemove:
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm index 48a889f..599e76ddf 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
@@ -6,6 +6,7 @@ #import <XCTest/XCTest.h> #include "components/prefs/pref_service.h" +#include "components/unified_consent/feature.h" #import "ios/chrome/app/main_controller.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/tabs/tab_model.h" @@ -63,6 +64,13 @@ @implementation GoogleServicesSettingsTestCase +- (void)setUp { + [super setUp]; + + CHECK(unified_consent::IsUnifiedConsentFeatureEnabled()) + << "This test suite must be run with Unified Consent feature enabled."; +} + // Opens the Google services settings view, and closes it. - (void)testOpenGoogleServicesSettings { [self openGoogleServicesSettings];
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm index 8ddc74d..1a9fe1c 100644 --- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm +++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -60,6 +60,7 @@ typedef NS_ENUM(NSInteger, ItemType) { // IdentitySectionIdentifier section. IdentityItemType = kItemTypeEnumZero, + ManageGoogleAccountItemType, // SyncSectionIdentifier section. SignInItemType, RestartAuthenticationFlowErrorItemType, @@ -192,7 +193,7 @@ [self configureIdentityAccountItem]; } -// Creates the identity sections. +// Creates the identity section. - (void)createIdentitySection { TableViewModel* model = self.consumer.tableViewModel; [model insertSectionWithIdentifier:IdentitySectionIdentifier atIndex:0]; @@ -207,6 +208,12 @@ } [model addItem:self.accountItem toSectionWithIdentifier:IdentitySectionIdentifier]; + TableViewImageItem* manageGoogleAccount = + [[TableViewImageItem alloc] initWithType:ManageGoogleAccountItemType]; + manageGoogleAccount.title = + GetNSString(IDS_IOS_MANAGE_YOUR_GOOGLE_ACCOUNT_TITLE); + [model addItem:manageGoogleAccount + toSectionWithIdentifier:IdentitySectionIdentifier]; } // Creates, removes or updates the identity section as needed. And notifies the @@ -484,6 +491,7 @@ switchItem.on = self.anonymizedDataCollectionPreference.value; break; case IdentityItemType: + case ManageGoogleAccountItemType: case SignInItemType: case RestartAuthenticationFlowErrorItemType: case ReauthDialogAsSyncIsInAuthErrorItemType: @@ -662,6 +670,7 @@ } break; case IdentityItemType: + case ManageGoogleAccountItemType: case SignInItemType: case RestartAuthenticationFlowErrorItemType: case ReauthDialogAsSyncIsInAuthErrorItemType: @@ -680,6 +689,9 @@ case IdentityItemType: [self.commandHandler openAccountSettings]; break; + case ManageGoogleAccountItemType: + [self.commandHandler openManageGoogleAccountWebPage]; + break; case SignInItemType: [self.commandHandler showSignIn]; break;
diff --git a/ios/chrome/browser/ui/signin_interaction/BUILD.gn b/ios/chrome/browser/ui/signin_interaction/BUILD.gn index 1a39be6d..a3c529c 100644 --- a/ios/chrome/browser/ui/signin_interaction/BUILD.gn +++ b/ios/chrome/browser/ui/signin_interaction/BUILD.gn
@@ -76,3 +76,16 @@ ] libs = [ "XCTest.framework" ] } + +source_set("unified_consent_enabled_hooks") { + configs += [ "//build/config/compiler:enable_arc" ] + testonly = true + sources = [ + "unified_consent_enabled_egtests_hook.mm", + ] + deps = [ + "//components/unified_consent", + "//components/unified_consent:test_support", + "//ios/chrome/app:tests_hook", + ] +}
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_unity_egtest.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_unity_egtest.mm index 006ff6e4..41b0956 100644 --- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_unity_egtest.mm +++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_unity_egtest.mm
@@ -47,17 +47,13 @@ // Returns a matcher for |userEmail| in IdentityChooserViewController. id<GREYMatcher> identityChooserButtonMatcherWithEmail(NSString* userEmail) { - if (base::FeatureList::IsEnabled(unified_consent::kUnifiedConsent)) { - return grey_allOf(grey_accessibilityID(userEmail), - grey_kindOfClass([IdentityChooserCell class]), - grey_sufficientlyVisible(), nil); - } - return chrome_test_util::ButtonWithAccessibilityLabel(userEmail); + return grey_allOf(grey_accessibilityID(userEmail), + grey_kindOfClass([IdentityChooserCell class]), + grey_sufficientlyVisible(), nil); } // Returns a matcher for "ADD ACCOUNT" in IdentityChooserViewController. id<GREYMatcher> addIdentityButtonInIdentityChooser() { - DCHECK(base::FeatureList::IsEnabled(unified_consent::kUnifiedConsent)); return chrome_test_util::ButtonWithAccessibilityLabel( l10n_util::GetNSStringWithFixup( IDS_IOS_ACCOUNT_UNIFIED_CONSENT_ADD_ACCOUNT)); @@ -70,6 +66,13 @@ @implementation SigninInteractionControllerTestCase +- (void)setUp { + [super setUp]; + + CHECK(unified_consent::IsUnifiedConsentFeatureEnabled()) + << "This test suite must be run with Unified Consent feature enabled."; +} + // Tests that opening the sign-in screen from the Settings and signing in works // correctly when there is already an identity on the device. - (void)testSignInOneUser {
diff --git a/ios/chrome/browser/ui/signin_interaction/unified_consent_enabled_egtests_hook.mm b/ios/chrome/browser/ui/signin_interaction/unified_consent_enabled_egtests_hook.mm new file mode 100644 index 0000000..b5a7e81 --- /dev/null +++ b/ios/chrome/browser/ui/signin_interaction/unified_consent_enabled_egtests_hook.mm
@@ -0,0 +1,57 @@ +// 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 "components/unified_consent/scoped_unified_consent.h" +#include "ios/chrome/app/tests_hook.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +unified_consent::ScopedUnifiedConsent* gScopedUnifiedConsent = nullptr; +} + +namespace tests_hook { + +bool DisableAppGroupAccess() { + return true; +} + +bool DisableContentSuggestions() { + return true; +} + +bool DisableContextualSearch() { + return true; +} + +bool DisableFirstRun() { + return true; +} + +bool DisableGeolocation() { + return true; +} + +bool DisableSigninRecallPromo() { + return true; +} + +bool DisableUpdateService() { + return true; +} + +void SetUpTestsIfPresent() { + // Enables unified consent feature. + CHECK(!gScopedUnifiedConsent); + gScopedUnifiedConsent = new unified_consent::ScopedUnifiedConsent( + unified_consent::UnifiedConsentFeatureState::kEnabled); +} + +void RunTestsIfPresent() { + // No-op for Earl Grey. +} + +} // namespace tests_hook
diff --git a/ios/chrome/test/app/signin_test_util.mm b/ios/chrome/test/app/signin_test_util.mm index 85aeefb..0c1bcc10 100644 --- a/ios/chrome/test/app/signin_test_util.mm +++ b/ios/chrome/test/app/signin_test_util.mm
@@ -149,6 +149,11 @@ browser_state->GetPrefs()->ClearPref(prefs::kGoogleServicesLastAccountId); browser_state->GetPrefs()->ClearPref(prefs::kGoogleServicesLastUsername); + // |SignOutAndClearAccounts()| is called during shutdown. Commit all pref + // changes to ensure that clearing the last signed in account is saved on disk + // in case Chrome crashes during shutdown. + browser_state->GetPrefs()->CommitPendingWrite(); + // Clear known identities. ios::ChromeIdentityService* identity_service = ios::GetChromeBrowserProvider()->GetChromeIdentityService();
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn index d95ab4f5..fb9ec841 100644 --- a/ios/chrome/test/earl_grey/BUILD.gn +++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -213,6 +213,8 @@ "//ios/chrome/browser/ui/settings/google_services:unified_consent_eg_tests", "//ios/chrome/browser/ui/signin_interaction:unity_eg_tests", ] + hooks_target = + "//ios/chrome/browser/ui/signin_interaction:unified_consent_enabled_hooks" } source_set("test_support") {
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm index e4e925f..34345d6 100644 --- a/ios/chrome/test/earl_grey/chrome_test_case.mm +++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -200,6 +200,11 @@ chrome_test_util::ResetSigninPromoPreferences(); chrome_test_util::ResetMockAuthentication(); + + // Reset any remaining sign-in state from previous tests. + GREYAssert(chrome_test_util::SignOutAndClearAccounts(), + @"Failed to clean up the sign-in state before starting the test."); + CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewTab]); }
diff --git a/ios/web/navigation/crw_wk_navigation_handler.h b/ios/web/navigation/crw_wk_navigation_handler.h index 20b5b620..f308662 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.h +++ b/ios/web/navigation/crw_wk_navigation_handler.h
@@ -11,6 +11,10 @@ @class CRWPendingNavigationInfo; @class CRWWKNavigationStates; +namespace base { +class RepeatingTimer; +} + // CRWWKNavigationHandler uses this protocol to interact with its owner. @protocol CRWWKNavigationHandlerDelegate <NSObject> @@ -30,6 +34,14 @@ // flight. @property(nonatomic, readonly, strong) CRWWKNavigationStates* navigationStates; +// The SafeBrowsingDetection timer. +// TODO(crbug.com/956511): Remove this once refactor is done. +@property(nonatomic, readonly, assign) + base::RepeatingTimer* safeBrowsingWarningDetectionTimer; + +// Instructs this handler to stop loading. +- (void)stopLoading; + @end #endif // IOS_WEB_NAVIGATION_CRW_WK_NAVIGATION_HANDLER_H_
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm index 72cff27..258364c 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.mm +++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -3,13 +3,22 @@ // found in the LICENSE file. #import "ios/web/navigation/crw_wk_navigation_handler.h" + +#include "base/timer/timer.h" +#import "ios/web/navigation/crw_pending_navigation_info.h" #import "ios/web/navigation/crw_wk_navigation_states.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@implementation CRWWKNavigationHandler +@implementation CRWWKNavigationHandler { + // Used to poll for a SafeBrowsing warning being displayed. This is created in + // |decidePolicyForNavigationAction| and destroyed once any of the following + // happens: 1) a SafeBrowsing warning is detected; 2) any WKNavigationDelegate + // method is called; 3) |stopLoading| is called. + base::RepeatingTimer _safeBrowsingWarningDetectionTimer; +} - (instancetype)init { if (self = [super init]) { @@ -18,4 +27,15 @@ return self; } +#pragma mark - Public methods + +- (void)stopLoading { + self.pendingNavigationInfo.cancelled = YES; + _safeBrowsingWarningDetectionTimer.Stop(); +} + +- (base::RepeatingTimer*)safeBrowsingWarningDetectionTimer { + return &_safeBrowsingWarningDetectionTimer; +} + @end
diff --git a/ios/web/navigation/wk_navigation_util.h b/ios/web/navigation/wk_navigation_util.h index c0e8d85..cb5c684 100644 --- a/ios/web/navigation/wk_navigation_util.h +++ b/ios/web/navigation/wk_navigation_util.h
@@ -18,6 +18,7 @@ #ifndef IOS_WEB_NAVIGATION_WK_NAVIGATION_UTIL_H_ #define IOS_WEB_NAVIGATION_WK_NAVIGATION_UTIL_H_ +#import <Foundation/Foundation.h> #include <memory> #include <vector> @@ -39,6 +40,9 @@ // URL fragment prefix used to encode target URL in a restore_session.html URL. extern const char kRestoreSessionTargetUrlHashPrefix[]; +// The "Referer" [sic] HTTP header. +extern NSString* const kReferrerHeaderName; + // Returns true if |url| is a placeholder URL or restore_session.html URL. bool IsWKInternalUrl(const GURL& url);
diff --git a/ios/web/navigation/wk_navigation_util.mm b/ios/web/navigation/wk_navigation_util.mm index 05b7a0e..56a03d1a 100644 --- a/ios/web/navigation/wk_navigation_util.mm +++ b/ios/web/navigation/wk_navigation_util.mm
@@ -32,6 +32,7 @@ const char kRestoreSessionSessionHashPrefix[] = "session="; const char kRestoreSessionTargetUrlHashPrefix[] = "targetUrl="; const char kOriginalUrlKey[] = "for"; +NSString* const kReferrerHeaderName = @"Referer"; namespace { // Returns begin and end iterators and an updated last committed index for the
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index ee073ab0..42028f7 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -136,6 +136,8 @@ using web::wk_navigation_util::ExtractUrlFromPlaceholderUrl; using web::wk_navigation_util::IsRestoreSessionUrl; using web::wk_navigation_util::IsWKInternalUrl; +using web::wk_navigation_util::kReferrerHeaderName; +using web::wk_navigation_util::URLNeedsUserAgentType; namespace { @@ -163,8 +165,6 @@ // Message command sent when a frame is unloading. NSString* const kFrameBecameUnavailableMessageName = @"FrameBecameUnavailable"; -NSString* const kReferrerHeaderName = @"Referer"; // [sic] - // The duration of the period following a screen touch during which the user is // still considered to be interacting with the page. const NSTimeInterval kMaximumDelayForUserInteractionInSeconds = 2; @@ -312,12 +312,6 @@ // helps with diagnosing a navigation related crash (crbug.com/565457). __weak WKNavigation* _stoppedWKNavigation; - // Used to poll for a SafeBrowsing warning being displayed. This is created in - // |decidePolicyForNavigationAction| and destroyed once any of the following - // happens: 1) a SafeBrowsing warning is detected; 2) any WKNavigationDelegate - // method is called; 3) |abortLoad| is called. - base::RepeatingTimer _safeBrowsingWarningDetectionTimer; - // Updates SSLStatus for current navigation item. CRWSSLStatusUpdater* _SSLStatusUpdater; @@ -416,9 +410,6 @@ // unless the request was a POST. @property(weak, nonatomic, readonly) NSDictionary* currentHTTPHeaders; -// Extracts "Referer" [sic] value from WKNavigationAction request header. -- (NSString*)referrerFromNavigationAction:(WKNavigationAction*)action; - // Returns the current URL of the web view, and sets |trustLevel| accordingly // based on the confidence in the verification. - (GURL)webURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel; @@ -1279,7 +1270,7 @@ item->SetURL(URL); navigationContext->SetMimeType(MIMEType); if (item->GetUserAgentType() == web::UserAgentType::NONE && - web::wk_navigation_util::URLNeedsUserAgentType(URL)) { + URLNeedsUserAgentType(URL)) { item->SetUserAgentType(web::UserAgentType::MOBILE); } @@ -2187,10 +2178,9 @@ - (void)abortLoad { [self.webView stopLoading]; - self.navigationHandler.pendingNavigationInfo.cancelled = YES; + [self.navigationHandler stopLoading]; _certVerificationErrors->Clear(); [self loadCancelled]; - _safeBrowsingWarningDetectionTimer.Stop(); } - (void)loadCancelled { @@ -3869,7 +3859,8 @@ return nil; } - NSString* referrer = [self referrerFromNavigationAction:action]; + NSString* referrer = + [action.request valueForHTTPHeaderField:kReferrerHeaderName]; GURL openerURL = referrer.length ? GURL(base::SysNSStringToUTF8(referrer)) : _documentURL; @@ -4294,7 +4285,7 @@ __weak CRWWebController* weakSelf = self; const base::TimeDelta kDelayUntilSafeBrowsingWarningCheck = base::TimeDelta::FromMilliseconds(20); - _safeBrowsingWarningDetectionTimer.Start( + self.navigationHandler.safeBrowsingWarningDetectionTimer->Start( FROM_HERE, kDelayUntilSafeBrowsingWarningCheck, base::BindRepeating(^{ __strong __typeof(weakSelf) strongSelf = weakSelf; if ([strongSelf isSafeBrowsingWarningDisplayedInWebView]) { @@ -4317,7 +4308,8 @@ strongSelf.navigationManagerImpl->AddTransientItem(requestURL); strongSelf.webStateImpl->OnNavigationStarted(context.get()); strongSelf.webStateImpl->OnNavigationFinished(context.get()); - strongSelf->_safeBrowsingWarningDetectionTimer.Stop(); + strongSelf.navigationHandler.safeBrowsingWarningDetectionTimer + ->Stop(); if (!existingContext) { // If there's an existing context, observers will already be aware // of a load in progress. Otherwise, observers need to be notified @@ -4438,7 +4430,7 @@ // performing characters escaping). web::NavigationItem* item = web::GetItemWithUniqueID(self.navigationManagerImpl, context); - if (!web::wk_navigation_util::IsWKInternalUrl(webViewURL)) { + if (!IsWKInternalUrl(webViewURL)) { if (item) { item->SetURL(webViewURL); } @@ -5386,7 +5378,7 @@ if (_isBeingDestroyed) { UMA_HISTOGRAM_BOOLEAN("Renderer.WKWebViewCallbackAfterDestroy", true); } - _safeBrowsingWarningDetectionTimer.Stop(); + self.navigationHandler.safeBrowsingWarningDetectionTimer->Stop(); } // Returns YES if response should be rendered in WKWebView. @@ -5459,7 +5451,7 @@ self.navigationHandler.pendingNavigationInfo = [[CRWPendingNavigationInfo alloc] init]; self.navigationHandler.pendingNavigationInfo.referrer = - [self referrerFromNavigationAction:action]; + [action.request valueForHTTPHeaderField:kReferrerHeaderName]; self.navigationHandler.pendingNavigationInfo.navigationType = action.navigationType; self.navigationHandler.pendingNavigationInfo.HTTPMethod = @@ -5562,7 +5554,7 @@ // navigation. WKWebView allows multiple provisional navigations, while // Navigation Manager has only one pending navigation. if (item) { - if (!web::wk_navigation_util::IsWKInternalUrl(URL)) { + if (!IsWKInternalUrl(URL)) { item->SetVirtualURL(URL); item->SetURL(URL); } @@ -6104,8 +6096,4 @@ _containerView = nil; } -- (NSString*)referrerFromNavigationAction:(WKNavigationAction*)action { - return [action.request valueForHTTPHeaderField:kReferrerHeaderName]; -} - @end
diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc index 777e8229..e51c0f40 100644 --- a/media/capture/video/android/video_capture_device_android.cc +++ b/media/capture/video/android/video_capture_device_android.cc
@@ -145,6 +145,11 @@ return; } + // TODO(julien.isorce): Use Camera.SENSOR_COLOR_TRANSFORM2 to build a + // gfx::ColorSpace, and rename VideoCaptureDeviceAndroid::GetColorspace() + // to GetPixelFormat, see http://crbug.com/959901. + capture_color_space_ = gfx::ColorSpace(); + capture_format_.frame_size.SetSize( Java_VideoCapture_queryWidth(env, j_capture_), Java_VideoCapture_queryHeight(env, j_capture_)); @@ -624,7 +629,8 @@ base::AutoLock lock(lock_); if (!client_) return; - client_->OnIncomingCapturedData(data, length, capture_format_, rotation, + client_->OnIncomingCapturedData(data, length, capture_format_, + capture_color_space_, rotation, reference_time, timestamp); }
diff --git a/media/capture/video/android/video_capture_device_android.h b/media/capture/video/android/video_capture_device_android.h index d234be83..587ec58 100644 --- a/media/capture/video/android/video_capture_device_android.h +++ b/media/capture/video/android/video_capture_device_android.h
@@ -201,6 +201,7 @@ const VideoCaptureDeviceDescriptor device_descriptor_; VideoCaptureFormat capture_format_; + gfx::ColorSpace capture_color_space_; // Java VideoCaptureAndroid instance. base::android::ScopedJavaLocalRef<jobject> j_capture_;
diff --git a/media/capture/video/chromeos/mock_video_capture_client.cc b/media/capture/video/chromeos/mock_video_capture_client.cc index 04e26a48..23feb6e1 100644 --- a/media/capture/video/chromeos/mock_video_capture_client.cc +++ b/media/capture/video/chromeos/mock_video_capture_client.cc
@@ -42,6 +42,7 @@ const uint8_t* data, int length, const VideoCaptureFormat& format, + const gfx::ColorSpace& color_space, int rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/chromeos/mock_video_capture_client.h b/media/capture/video/chromeos/mock_video_capture_client.h index 7ab44a08..bb03e54 100644 --- a/media/capture/video/chromeos/mock_video_capture_client.h +++ b/media/capture/video/chromeos/mock_video_capture_client.h
@@ -42,6 +42,7 @@ void OnIncomingCapturedData(const uint8_t* data, int length, const VideoCaptureFormat& format, + const gfx::ColorSpace& color_space, int rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc index 25087db..300f836e 100644 --- a/media/capture/video/fake_video_capture_device.cc +++ b/media/capture/video/fake_video_capture_device.cc
@@ -109,6 +109,44 @@ return supported_formats[best_index]; } +gfx::ColorSpace GetDefaultColorSpace(VideoPixelFormat format) { + switch (format) { + case PIXEL_FORMAT_UYVY: + case PIXEL_FORMAT_YUY2: + case PIXEL_FORMAT_YV12: + case PIXEL_FORMAT_I420: + case PIXEL_FORMAT_I422: + case PIXEL_FORMAT_I420A: + case PIXEL_FORMAT_I444: + case PIXEL_FORMAT_NV12: + case PIXEL_FORMAT_NV21: + case PIXEL_FORMAT_MT21: + case PIXEL_FORMAT_YUV420P9: + case PIXEL_FORMAT_YUV420P10: + case PIXEL_FORMAT_YUV422P9: + case PIXEL_FORMAT_YUV422P10: + case PIXEL_FORMAT_YUV444P9: + case PIXEL_FORMAT_YUV444P10: + case PIXEL_FORMAT_YUV420P12: + case PIXEL_FORMAT_YUV422P12: + case PIXEL_FORMAT_YUV444P12: + case PIXEL_FORMAT_P016LE: + case PIXEL_FORMAT_Y16: + return gfx::ColorSpace::CreateREC601(); + case PIXEL_FORMAT_ARGB: + case PIXEL_FORMAT_XRGB: + case PIXEL_FORMAT_RGB24: + case PIXEL_FORMAT_RGB32: + case PIXEL_FORMAT_MJPEG: + case PIXEL_FORMAT_ABGR: + case PIXEL_FORMAT_XBGR: + return gfx::ColorSpace::CreateSRGB(); + case PIXEL_FORMAT_UNKNOWN: + return gfx::ColorSpace(); + } + return gfx::ColorSpace(); +} + } // anonymous namespace // Paints and delivers frames to a client, which is set via Initialize(). @@ -592,9 +630,10 @@ memset(buffer_.get(), 0, frame_size); frame_painter()->PaintFrame(timestamp_to_paint, buffer_.get()); base::TimeTicks now = base::TimeTicks::Now(); - client()->OnIncomingCapturedData(buffer_.get(), frame_size, - device_state()->format, 0 /* rotation */, - now, CalculateTimeSinceFirstInvocation(now)); + client()->OnIncomingCapturedData( + buffer_.get(), frame_size, device_state()->format, + GetDefaultColorSpace(device_state()->format.pixel_format), + 0 /* rotation */, now, CalculateTimeSinceFirstInvocation(now)); } ClientBufferFrameDeliverer::ClientBufferFrameDeliverer( @@ -665,9 +704,10 @@ const size_t frame_size = jpeg_buffer_.size(); base::TimeTicks now = base::TimeTicks::Now(); - client()->OnIncomingCapturedData(&jpeg_buffer_[0], frame_size, - device_state()->format, 0 /* rotation */, - now, CalculateTimeSinceFirstInvocation(now)); + client()->OnIncomingCapturedData( + &jpeg_buffer_[0], frame_size, device_state()->format, + gfx::ColorSpace::CreateJpeg(), 0 /* rotation */, now, + CalculateTimeSinceFirstInvocation(now)); } void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
diff --git a/media/capture/video/fake_video_capture_device_unittest.cc b/media/capture/video/fake_video_capture_device_unittest.cc index c5b3d806..54cd768 100644 --- a/media/capture/video/fake_video_capture_device_unittest.cc +++ b/media/capture/video/fake_video_capture_device_unittest.cc
@@ -157,12 +157,12 @@ *buffer = CreateStubBuffer(0, frame_format.ImageAllocationSize()); return VideoCaptureDevice::Client::ReserveResult::kSucceeded; })); - ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _)) - .WillByDefault( - Invoke([this](const uint8_t*, int, - const media::VideoCaptureFormat& frame_format, int, - base::TimeTicks, base::TimeDelta, - int) { OnFrameCaptured(frame_format); })); + ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) + .WillByDefault(Invoke( + [this](const uint8_t*, int, + const media::VideoCaptureFormat& frame_format, + const gfx::ColorSpace&, int, base::TimeTicks, + base::TimeDelta, int) { OnFrameCaptured(frame_format); })); ON_CALL(*result, OnIncomingCapturedGfxBuffer(_, _, _, _, _, _)) .WillByDefault( Invoke([this](gfx::GpuMemoryBuffer*,
diff --git a/media/capture/video/file_video_capture_device.cc b/media/capture/video/file_video_capture_device.cc index c67c9a2..06f4527 100644 --- a/media/capture/video/file_video_capture_device.cc +++ b/media/capture/video/file_video_capture_device.cc
@@ -431,8 +431,11 @@ const base::TimeTicks current_time = base::TimeTicks::Now(); if (first_ref_time_.is_null()) first_ref_time_ = current_time; - client_->OnIncomingCapturedData(frame_ptr, frame_size, capture_format_, 0, - current_time, current_time - first_ref_time_); + // Leave the color space unset for compatibility purposes but this + // information should be retrieved from the container when possible. + client_->OnIncomingCapturedData(frame_ptr, frame_size, capture_format_, + gfx::ColorSpace(), 0, current_time, + current_time - first_ref_time_); // Process waiting photo callbacks while (!take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/linux/v4l2_capture_delegate.cc b/media/capture/video/linux/v4l2_capture_delegate.cc index d4842a8..839165ba 100644 --- a/media/capture/video/linux/v4l2_capture_delegate.cc +++ b/media/capture/video/linux/v4l2_capture_delegate.cc
@@ -891,9 +891,15 @@ client_->OnFrameDropped( VideoCaptureFrameDropReason::kV4L2InvalidNumberOfBytesInBuffer); } else { + // TODO(julien.isorce): build gfx color space from v4l2 color space. + // primary = v4l2_format->fmt.pix.colorspace; + // range = v4l2_format->fmt.pix.quantization; + // matrix = v4l2_format->fmt.pix.ycbcr_enc; + // transfer = v4l2_format->fmt.pix.xfer_func; + // See http://crbug.com/959919. client_->OnIncomingCapturedData( buffer_tracker->start(), buffer_tracker->payload_size(), - capture_format_, rotation_, now, timestamp); + capture_format_, gfx::ColorSpace(), rotation_, now, timestamp); } while (!take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc index 34075f6..c186e14 100644 --- a/media/capture/video/linux/v4l2_capture_delegate_unittest.cc +++ b/media/capture/video/linux/v4l2_capture_delegate_unittest.cc
@@ -234,7 +234,7 @@ base::RunLoop run_loop; base::Closure quit_closure = run_loop.QuitClosure(); - EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .Times(1) .WillOnce(RunClosure(quit_closure)); run_loop.Run();
diff --git a/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc b/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc index bb16228..c2992ff 100644 --- a/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc +++ b/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc
@@ -128,7 +128,7 @@ base::RunLoop wait_loop; static const int kFrameToReceive = 3; - EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _)) + EXPECT_CALL(*client_ptr, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillRepeatedly(InvokeWithoutArgs([&wait_loop]() { static int received_frame_count = 0; received_frame_count++;
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm index 132a1c3d..6e7d5ba 100644 --- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm +++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -490,6 +490,7 @@ const media::VideoCaptureFormat captureFormat( gfx::Size(dimensions.width, dimensions.height), frameRate_, FourCCToChromiumPixelFormat(fourcc)); + gfx::ColorSpace colorSpace; // We have certain format expectation for capture output: // For MJPEG, |sampleBuffer| is expected to always be a CVBlockBuffer. @@ -510,6 +511,11 @@ baseAddress = static_cast<char*>(CVPixelBufferGetBaseAddress(videoFrame)); frameSize = CVPixelBufferGetHeight(videoFrame) * CVPixelBufferGetBytesPerRow(videoFrame); + + // TODO(julien.isorce): move GetImageBufferColorSpace(CVImageBufferRef) + // from media::VTVideoDecodeAccelerator to media/base/mac and call it + // here to get the color space. See https://crbug.com/959962. + // colorSpace = media::GetImageBufferColorSpace(videoFrame); } else { videoFrame = nil; } @@ -531,7 +537,8 @@ if (frameReceiver_ && baseAddress) { frameReceiver_->ReceiveFrame(reinterpret_cast<uint8_t*>(baseAddress), - frameSize, captureFormat, 0, 0, timestamp); + frameSize, captureFormat, colorSpace, 0, 0, + timestamp); } }
diff --git a/media/capture/video/mac/video_capture_device_decklink_mac.h b/media/capture/video/mac/video_capture_device_decklink_mac.h index e642354..98bc8fd2 100644 --- a/media/capture/video/mac/video_capture_device_decklink_mac.h +++ b/media/capture/video/mac/video_capture_device_decklink_mac.h
@@ -57,6 +57,7 @@ void OnIncomingCapturedData(const uint8_t* data, size_t length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int rotation, // Clockwise. base::TimeTicks reference_time, base::TimeDelta timestamp);
diff --git a/media/capture/video/mac/video_capture_device_decklink_mac.mm b/media/capture/video/mac/video_capture_device_decklink_mac.mm index 53e9649..f49981c 100644 --- a/media/capture/video/mac/video_capture_device_decklink_mac.mm +++ b/media/capture/video/mac/video_capture_device_decklink_mac.mm
@@ -292,9 +292,12 @@ } else { timestamp = now - first_ref_time_; } + // TODO(julien.isorce): Build a gfx::ColorSpace from DeckLink API, .i.e + // using BMDDisplayModeFlags or BMDDeckLinkFrameMetadataID. See + // http://crbug.com/959953. frame_receiver_->OnIncomingCapturedData( video_data, video_frame->GetRowBytes() * video_frame->GetHeight(), - capture_format, + capture_format, gfx::ColorSpace(), 0, // Rotation. now, timestamp); } @@ -486,13 +489,14 @@ const uint8_t* data, size_t length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int rotation, // Clockwise. base::TimeTicks reference_time, base::TimeDelta timestamp) { base::AutoLock lock(lock_); if (client_) { - client_->OnIncomingCapturedData(data, length, frame_format, rotation, - reference_time, timestamp); + client_->OnIncomingCapturedData(data, length, frame_format, color_space, + rotation, reference_time, timestamp); } }
diff --git a/media/capture/video/mac/video_capture_device_mac.h b/media/capture/video/mac/video_capture_device_mac.h index ef1dc21..ba02224 100644 --- a/media/capture/video/mac/video_capture_device_mac.h +++ b/media/capture/video/mac/video_capture_device_mac.h
@@ -75,6 +75,7 @@ void ReceiveFrame(const uint8_t* video_frame, int video_frame_length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace color_space, int aspect_numerator, int aspect_denominator, base::TimeDelta timestamp);
diff --git a/media/capture/video/mac/video_capture_device_mac.mm b/media/capture/video/mac/video_capture_device_mac.mm index 39fc01f2..097485312 100644 --- a/media/capture/video/mac/video_capture_device_mac.mm +++ b/media/capture/video/mac/video_capture_device_mac.mm
@@ -442,6 +442,7 @@ void VideoCaptureDeviceMac::ReceiveFrame(const uint8_t* video_frame, int video_frame_length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace color_space, int aspect_numerator, int aspect_denominator, base::TimeDelta timestamp) { @@ -454,7 +455,8 @@ } client_->OnIncomingCapturedData(video_frame, video_frame_length, frame_format, - 0, base::TimeTicks::Now(), timestamp); + color_space, 0, base::TimeTicks::Now(), + timestamp); } void VideoCaptureDeviceMac::OnPhotoTaken(const uint8_t* image_data,
diff --git a/media/capture/video/mock_device.cc b/media/capture/video/mock_device.cc index 080b8a4..833cde00 100644 --- a/media/capture/video/mock_device.cc +++ b/media/capture/video/mock_device.cc
@@ -21,7 +21,7 @@ stub_frame->data(0), static_cast<int>(media::VideoFrame::AllocationSize( stub_frame->format(), stub_frame->coded_size())), - format, rotation, base::TimeTicks(), base::TimeDelta(), + format, gfx::ColorSpace(), rotation, base::TimeTicks(), base::TimeDelta(), frame_feedback_id); }
diff --git a/media/capture/video/mock_video_capture_device_client.h b/media/capture/video/mock_video_capture_device_client.h index b0e2d76f..6b53add 100644 --- a/media/capture/video/mock_video_capture_device_client.h +++ b/media/capture/video/mock_video_capture_device_client.h
@@ -15,10 +15,11 @@ MockVideoCaptureDeviceClient(); ~MockVideoCaptureDeviceClient() override; - MOCK_METHOD7(OnIncomingCapturedData, + MOCK_METHOD8(OnIncomingCapturedData, void(const uint8_t* data, int length, const media::VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/video_capture_device.h b/media/capture/video/video_capture_device.h index 3cef019..7255cf9b 100644 --- a/media/capture/video/video_capture_device.h +++ b/media/capture/video/video_capture_device.h
@@ -147,6 +147,7 @@ virtual void OnIncomingCapturedData(const uint8_t* data, int length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc index cd0eb4df..aa834d9 100644 --- a/media/capture/video/video_capture_device_client.cc +++ b/media/capture/video/video_capture_device_client.cc
@@ -68,6 +68,38 @@ *uv_plane_stride = *y_plane_stride / 2; } +gfx::ColorSpace OverrideColorSpaceForLibYuvConversion( + const gfx::ColorSpace& color_space, + const media::VideoPixelFormat pixel_format) { + gfx::ColorSpace overriden_color_space = color_space; + switch (pixel_format) { + case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. + break; + case media::PIXEL_FORMAT_ARGB: + case media::PIXEL_FORMAT_XRGB: + case media::PIXEL_FORMAT_RGB24: + case media::PIXEL_FORMAT_RGB32: + case media::PIXEL_FORMAT_ABGR: + case media::PIXEL_FORMAT_XBGR: + // TODO(julien.isorce): Merge data 's primary and transfer function into + // the returned color space, see http://crbug.com/945468. + + // Color space is not specified but it's probably safe to assume its + // sRGB though, and so it would be valid to assume that libyuv 's + // ConvertToI420() is going to produce results in Rec601 + // (or very close to it). + // TODO(julien.isorce): pass color space information to libyuv once the + // support is added, see http://crbug.com/libyuv/835. + if (!color_space.IsValid()) + overriden_color_space = gfx::ColorSpace::CreateREC601(); + break; + default: + break; + } + + return overriden_color_space; +} + } // anonymous namespace namespace media { @@ -148,6 +180,7 @@ const uint8_t* data, int length, const VideoCaptureFormat& format, + const gfx::ColorSpace& data_color_space, int rotation, base::TimeTicks reference_time, base::TimeDelta timestamp, @@ -219,7 +252,6 @@ int crop_x = 0; int crop_y = 0; libyuv::FourCC fourcc_format = libyuv::FOURCC_ANY; - gfx::ColorSpace color_space; bool flip = false; switch (format.pixel_format) { @@ -268,11 +300,6 @@ // that vertical flipping is needed. flip = true; #endif - // We don't actually know, for sure, what the source color space is. It's - // probably safe to assume its sRGB, though, and so it would be valid to - // assume libyuv::ConvertToI420() is going to produce results in Rec601 - // (or very close to it). - color_space = gfx::ColorSpace::CreateREC601(); break; case PIXEL_FORMAT_RGB32: // Fallback to PIXEL_FORMAT_ARGB setting |flip| in Windows @@ -283,7 +310,6 @@ #endif case PIXEL_FORMAT_ARGB: fourcc_format = libyuv::FOURCC_ARGB; - color_space = gfx::ColorSpace::CreateREC601(); break; case PIXEL_FORMAT_MJPEG: fourcc_format = libyuv::FOURCC_MJPG; @@ -292,6 +318,9 @@ NOTREACHED(); } + const gfx::ColorSpace color_space = OverrideColorSpaceForLibYuvConversion( + data_color_space, format.pixel_format); + // The input |length| can be greater than the required buffer size because of // paddings and/or alignments, but it cannot be smaller. DCHECK_GE(static_cast<size_t>(length), format.ImageAllocationSize());
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h index 1b113d2..05fd66a 100644 --- a/media/capture/video/video_capture_device_client.h +++ b/media/capture/video/video_capture_device_client.h
@@ -65,6 +65,7 @@ void OnIncomingCapturedData(const uint8_t* data, int length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc index 5b8758c..15a39c93 100644 --- a/media/capture/video/video_capture_device_client_unittest.cc +++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -88,6 +88,7 @@ unsigned char data[kScratchpadSizeInBytes] = {}; const VideoCaptureFormat kFrameFormat(gfx::Size(10, 10), 30.0f /*frame_rate*/, PIXEL_FORMAT_I420); + const gfx::ColorSpace kColorSpace = gfx::ColorSpace::CreateREC601(); DCHECK(device_client_.get()); { InSequence s; @@ -96,9 +97,9 @@ EXPECT_CALL(*receiver_, MockOnNewBufferHandle(expected_buffer_id)); EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(expected_buffer_id, _, _)); } - device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, - kFrameFormat, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); + device_client_->OnIncomingCapturedData( + data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, + 0 /*clockwise rotation*/, base::TimeTicks(), base::TimeDelta()); const gfx::Size kBufferDimensions(10, 10); const VideoCaptureFormat kFrameFormatNV12( @@ -130,13 +131,14 @@ const VideoCaptureFormat kFrameFormat( gfx::Size(limits::kMaxDimension + 1, limits::kMaxDimension), limits::kMaxFramesPerSecond + 1, VideoPixelFormat::PIXEL_FORMAT_I420); + const gfx::ColorSpace kColorSpace = gfx::ColorSpace::CreateREC601(); DCHECK(device_client_.get()); // Expect the the call to fail silently inside the VideoCaptureDeviceClient. EXPECT_CALL(*receiver_, OnLog(_)).Times(AtLeast(1)); EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)).Times(0); - device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, - kFrameFormat, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); + device_client_->OnIncomingCapturedData( + data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, + 0 /*clockwise rotation*/, base::TimeTicks(), base::TimeDelta()); const gfx::Size kBufferDimensions(10, 10); const VideoCaptureFormat kFrameFormatNV12( @@ -159,6 +161,7 @@ unsigned char data[kScratchpadSizeInBytes] = {}; const VideoCaptureFormat kFrameFormat(gfx::Size(10, 10), 30.0f /*frame_rate*/, PIXEL_FORMAT_I420); + const gfx::ColorSpace kColorSpace = gfx::ColorSpace::CreateREC601(); EXPECT_CALL(*receiver_, OnLog(_)).Times(1); // Simulate that receiver still holds |buffer_read_permission| for the first // two buffers when the third call to OnIncomingCapturedData comes in. @@ -179,15 +182,15 @@ read_permission.push_back(std::move(*buffer_read_permission)); })); // Pass three frames. The third will be dropped. - device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, - kFrameFormat, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); - device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, - kFrameFormat, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); - device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes, - kFrameFormat, 0 /*clockwise rotation*/, - base::TimeTicks(), base::TimeDelta()); + device_client_->OnIncomingCapturedData( + data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, + 0 /*clockwise rotation*/, base::TimeTicks(), base::TimeDelta()); + device_client_->OnIncomingCapturedData( + data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, + 0 /*clockwise rotation*/, base::TimeTicks(), base::TimeDelta()); + device_client_->OnIncomingCapturedData( + data, kScratchpadSizeInBytes, kFrameFormat, kColorSpace, + 0 /*clockwise rotation*/, base::TimeTicks(), base::TimeDelta()); Mock::VerifyAndClearExpectations(receiver_); } @@ -223,6 +226,7 @@ PIXEL_FORMAT_ARGB, PIXEL_FORMAT_Y16, }; + const gfx::ColorSpace kColorSpace = gfx::ColorSpace::CreateSRGB(); for (VideoPixelFormat format : kSupportedFormats) { params.requested_format.pixel_format = format; @@ -231,8 +235,8 @@ EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)).Times(1); device_client_->OnIncomingCapturedData( data, params.requested_format.ImageAllocationSize(), - params.requested_format, 0 /* clockwise_rotation */, base::TimeTicks(), - base::TimeDelta()); + params.requested_format, kColorSpace, 0 /* clockwise_rotation */, + base::TimeTicks(), base::TimeDelta()); Mock::VerifyAndClearExpectations(receiver_); } } @@ -271,8 +275,8 @@ .WillOnce(SaveArg<2>(&coded_size)); device_client_->OnIncomingCapturedData( data, params.requested_format.ImageAllocationSize(), - params.requested_format, size_and_rotation.rotation, base::TimeTicks(), - base::TimeDelta()); + params.requested_format, gfx::ColorSpace(), size_and_rotation.rotation, + base::TimeTicks(), base::TimeDelta()); EXPECT_EQ(coded_size.width(), size_and_rotation.output_resolution.width()); EXPECT_EQ(coded_size.height(),
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc index 5fe6ca7..6408259 100644 --- a/media/capture/video/video_capture_device_unittest.cc +++ b/media/capture/video/video_capture_device_unittest.cc
@@ -305,11 +305,12 @@ EXPECT_CALL(*result, DoOnIncomingCapturedBuffer(_, _, _, _)).Times(0); EXPECT_CALL(*result, DoOnIncomingCapturedBufferExt(_, _, _, _, _, _, _)) .Times(0); - ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _)) + ON_CALL(*result, OnIncomingCapturedData(_, _, _, _, _, _, _, _)) .WillByDefault( Invoke([this](const uint8_t* data, int length, - const media::VideoCaptureFormat& frame_format, int, - base::TimeTicks, base::TimeDelta, int) { + const media::VideoCaptureFormat& frame_format, + const gfx::ColorSpace&, int, base::TimeTicks, + base::TimeDelta, int) { ASSERT_GT(length, 0); ASSERT_TRUE(data); main_thread_task_runner_->PostTask(
diff --git a/media/capture/video/win/video_capture_device_mf_win.cc b/media/capture/video/win/video_capture_device_mf_win.cc index 5ff5e5e..86f096bc 100644 --- a/media/capture/video/win/video_capture_device_mf_win.cc +++ b/media/capture/video/win/video_capture_device_mf_win.cc
@@ -1003,9 +1003,13 @@ client_->OnStarted(); } + // TODO(julien.isorce): retrieve the color space information using Media + // Foundation api, MFGetAttributeSize/MF_MT_VIDEO_PRIMARIES,in order to + // build a gfx::ColorSpace. See http://crbug.com/959988. client_->OnIncomingCapturedData( data, length, selected_video_capability_->supported_format, - GetCameraRotation(facing_mode_), reference_time, timestamp); + gfx::ColorSpace(), GetCameraRotation(facing_mode_), reference_time, + timestamp); } while (!video_stream_take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/win/video_capture_device_mf_win_unittest.cc b/media/capture/video/win/video_capture_device_mf_win_unittest.cc index f8c252f..6c96763 100644 --- a/media/capture/video/win/video_capture_device_mf_win_unittest.cc +++ b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
@@ -30,6 +30,7 @@ void OnIncomingCapturedData(const uint8_t* data, int length, const VideoCaptureFormat& frame_format, + const gfx::ColorSpace& color_space, int clockwise_rotation, base::TimeTicks reference_time, base::TimeDelta timestamp,
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc index db7ea55..905726f7 100644 --- a/media/capture/video/win/video_capture_device_win.cc +++ b/media/capture/video/win/video_capture_device_win.cc
@@ -881,7 +881,13 @@ if (timestamp == kNoTimestamp) timestamp = base::TimeTicks::Now() - first_ref_time_; - client_->OnIncomingCapturedData(buffer, length, format, + // TODO(julien.isorce): retrieve the color space information using the + // DirectShow api, AM_MEDIA_TYPE::VIDEOINFOHEADER2::dwControlFlags. If + // AMCONTROL_COLORINFO_PRESENT, then reinterpret dwControlFlags as a + // DXVA_ExtendedFormat. Then use its fields DXVA_VideoPrimaries, + // DXVA_VideoTransferMatrix, DXVA_VideoTransferFunction and + // DXVA_NominalRangeto build a gfx::ColorSpace. See http://crbug.com/959992. + client_->OnIncomingCapturedData(buffer, length, format, gfx::ColorSpace(), GetCameraRotation(device_descriptor_.facing), base::TimeTicks::Now(), timestamp);
diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h index 562951ee..6d7259c 100644 --- a/mojo/public/cpp/bindings/lib/serialization_forward.h +++ b/mojo/public/cpp/bindings/lib/serialization_forward.h
@@ -8,6 +8,7 @@ #include "base/optional.h" #include "mojo/public/cpp/bindings/array_traits.h" #include "mojo/public/cpp/bindings/enum_traits.h" +#include "mojo/public/cpp/bindings/lib/buffer.h" #include "mojo/public/cpp/bindings/lib/template_util.h" #include "mojo/public/cpp/bindings/map_traits.h" #include "mojo/public/cpp/bindings/string_traits.h"
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl index 1183669..3f9e8ab5 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
@@ -39,6 +39,10 @@ #ifndef {{header_guard}} #define {{header_guard}} +{% if all_enums|length -%} +#include <stdint.h> +{%- endif %} + {% if structs|length -%} #include "mojo/public/cpp/bindings/struct_ptr.h" {%- endif %} @@ -56,8 +60,6 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h" -{%- else %} -#include "mojo/public/cpp/bindings/lib/buffer.h" {%- endif %} {% if not disallow_native_types and structs|length %} @@ -98,7 +100,12 @@ {%- from "enum_macros.tmpl" import enum_hash_blink_forward%} {%- if for_blink %} {%- if all_enums %} -#include "third_party/blink/renderer/platform/wtf/hash_functions.h" + +namespace WTF { +template <typename T> +struct DefaultHash; +} + {%- endif %} {%- for enum in all_enums %}
diff --git a/net/BUILD.gn b/net/BUILD.gn index 00a042dd..79cfe95 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -6340,6 +6340,24 @@ # understood (probably just needs to restrict maximum input size). additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] } + + fuzzer_test("net_url_request_ftp_fuzzer") { + sources = [ + "url_request/url_request_ftp_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + ":test_support", + "//base", + "//net", + ] + dict = "data/fuzzer_dictionaries/net_url_request_ftp_fuzzer.dict" + seed_corpus = "data/fuzzer_data/net_url_request_ftp_fuzzer/" + + # TODO(https://crbug.com/962087): Re-enable once source of timeout is + # understood (probably just needs to restrict maximum input size). + additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] + } } fuzzer_test("net_unescape_url_component_fuzzer") { @@ -6473,20 +6491,6 @@ ] } -fuzzer_test("net_url_request_ftp_fuzzer") { - sources = [ - "url_request/url_request_ftp_fuzzer.cc", - ] - deps = [ - ":net_fuzzer_test_support", - ":test_support", - "//base", - "//net", - ] - dict = "data/fuzzer_dictionaries/net_url_request_ftp_fuzzer.dict" - seed_corpus = "data/fuzzer_data/net_url_request_ftp_fuzzer/" -} - fuzzer_test("net_url_request_fuzzer") { sources = [ "url_request/url_request_fuzzer.cc",
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java index d495b4b..80592cab 100644 --- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java +++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -290,6 +290,34 @@ return ""; } + /** + * Gets the signal strength from the currently associated WiFi access point if there is one, and + * it is available. Signal strength may not be available if the app does not have permissions to + * access it. + * @return -1 if value is unavailable, otherwise, a value between 0 and {@code countBuckets-1} + * (both inclusive). + */ + @CalledByNative + public static int getWifiSignalLevel(int countBuckets) { + final Intent intent = ContextUtils.getApplicationContext().registerReceiver( + null, new IntentFilter(WifiManager.RSSI_CHANGED_ACTION)); + if (intent == null) { + return -1; + } + + final int rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, Integer.MIN_VALUE); + if (rssi == Integer.MIN_VALUE) { + return -1; + } + + final int signalLevel = WifiManager.calculateSignalLevel(rssi, countBuckets); + if (signalLevel < 0 || signalLevel >= countBuckets) { + return -1; + } + + return signalLevel; + } + public static class NetworkSecurityPolicyProxy { private static NetworkSecurityPolicyProxy sInstance = new NetworkSecurityPolicyProxy();
diff --git a/net/android/network_library.cc b/net/android/network_library.cc index 818565d..41665c4 100644 --- a/net/android/network_library.cc +++ b/net/android/network_library.cc
@@ -123,6 +123,18 @@ base::android::AttachCurrentThread())); } +base::Optional<int32_t> GetWifiSignalLevel() { + const int count_buckets = 5; + int signal_strength = Java_AndroidNetworkLibrary_getWifiSignalLevel( + base::android::AttachCurrentThread(), count_buckets); + if (signal_strength < 0) + return base::nullopt; + DCHECK_LE(0, signal_strength); + DCHECK_GE(count_buckets - 1, signal_strength); + + return signal_strength; +} + internal::ConfigParsePosixResult GetDnsServers( std::vector<IPEndPoint>* dns_servers) { JNIEnv* env = AttachCurrentThread();
diff --git a/net/android/network_library.h b/net/android/network_library.h index 59076d8..5bce314 100644 --- a/net/android/network_library.h +++ b/net/android/network_library.h
@@ -86,6 +86,11 @@ // point or its SSID is unavailable, an empty string is returned. NET_EXPORT_PRIVATE std::string GetWifiSSID(); +// Returns the signal strength level (between 0 and 4, both inclusive) of the +// currently registered Wifi connection. If the value is unavailable, an +// empty value is returned. +NET_EXPORT_PRIVATE base::Optional<int32_t> GetWifiSignalLevel(); + // Gets the DNS servers and puts them in |dns_servers|. // Only callable on Marshmallow and newer releases. // Returns CONFIG_PARSE_POSIX_OK upon success,
diff --git a/net/android/network_library_unittest.cc b/net/android/network_library_unittest.cc index b6cb875..bc0e536f 100644 --- a/net/android/network_library_unittest.cc +++ b/net/android/network_library_unittest.cc
@@ -14,6 +14,14 @@ EXPECT_FALSE(android::GetIsCaptivePortal()); } +TEST(NetworkLibraryTest, GetWifiSignalLevel) { + base::Optional<int32_t> signal_strength = android::GetWifiSignalLevel(); + if (!signal_strength.has_value()) + return; + EXPECT_LE(0, signal_strength.value()); + EXPECT_GE(4, signal_strength.value()); +} + } // namespace android } // namespace net
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc index 155d023..ac46244 100644 --- a/net/disk_cache/backend_unittest.cc +++ b/net/disk_cache/backend_unittest.cc
@@ -70,6 +70,19 @@ // Provide a BackendImpl object to macros from histogram_macros.h. #define CACHE_UMA_BACKEND_IMPL_OBJ backend_ +// TODO(crbug.com/949811): Fix memory leaks in tests and re-enable on LSAN. +#ifdef LEAK_SANITIZER +#define MAYBE_BlockFileOpenOrCreateEntry DISABLED_BlockFileOpenOrCreateEntry +#define MAYBE_NonEmptyCorruptSimpleCacheDoesNotRecover \ + DISABLED_NonEmptyCorruptSimpleCacheDoesNotRecover +#define MAYBE_SimpleOpenOrCreateEntry DISABLED_SimpleOpenOrCreateEntry +#else +#define MAYBE_BlockFileOpenOrCreateEntry BlockFileOpenOrCreateEntry +#define MAYBE_NonEmptyCorruptSimpleCacheDoesNotRecover \ + NonEmptyCorruptSimpleCacheDoesNotRecover +#define MAYBE_SimpleOpenOrCreateEntry SimpleOpenOrCreateEntry +#endif + using base::Time; namespace { @@ -4843,11 +4856,11 @@ BackendOpenOrCreateEntry(); } -TEST_F(DiskCacheBackendTest, BlockFileOpenOrCreateEntry) { +TEST_F(DiskCacheBackendTest, MAYBE_BlockFileOpenOrCreateEntry) { BackendOpenOrCreateEntry(); } -TEST_F(DiskCacheBackendTest, SimpleOpenOrCreateEntry) { +TEST_F(DiskCacheBackendTest, MAYBE_SimpleOpenOrCreateEntry) { SetSimpleCacheMode(); BackendOpenOrCreateEntry(); } @@ -4955,7 +4968,7 @@ EXPECT_THAT(cb.GetResult(rv), IsOk()); } -TEST_F(DiskCacheBackendTest, NonEmptyCorruptSimpleCacheDoesNotRecover) { +TEST_F(DiskCacheBackendTest, MAYBE_NonEmptyCorruptSimpleCacheDoesNotRecover) { SetSimpleCacheMode(); BackendOpenOrCreateEntry();
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc index a227a3a0..339d1db 100644 --- a/net/nqe/network_quality_estimator.cc +++ b/net/nqe/network_quality_estimator.cc
@@ -516,17 +516,30 @@ rtt_ms_observations_[i].Clear(); #if defined(OS_ANDROID) + + bool is_cell_connection = + NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type); + bool is_wifi_connection = + (current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI); + if (params_->weight_multiplier_per_signal_strength_level() < 1.0 && - NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) { + (is_cell_connection || is_wifi_connection)) { bool signal_strength_available = min_signal_strength_since_connection_change_ && max_signal_strength_since_connection_change_; - UMA_HISTOGRAM_BOOLEAN("NQE.CellularSignalStrength.LevelAvailable", - signal_strength_available); + + std::string histogram_name = + is_cell_connection ? "NQE.CellularSignalStrength.LevelAvailable" + : "NQE.WifiSignalStrength.LevelAvailable"; + + base::UmaHistogramBoolean(histogram_name, signal_strength_available); if (signal_strength_available) { - UMA_HISTOGRAM_COUNTS_100( - "NQE.CellularSignalStrength.LevelDifference", + std::string histogram_name = + is_cell_connection ? "NQE.CellularSignalStrength.LevelDifference" + : "NQE.WifiSignalStrength.LevelDifference"; + base::UmaHistogramCounts100( + histogram_name, max_signal_strength_since_connection_change_.value() - min_signal_strength_since_connection_change_.value()); } @@ -604,10 +617,14 @@ #if defined(OS_ANDROID) if (params_->weight_multiplier_per_signal_strength_level() >= 1.0) return INT32_MIN; - if (!NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) - return INT32_MIN; - return android::cellular_signal_strength::GetSignalStrengthLevel().value_or( - INT32_MIN); + if (params_->get_wifi_signal_strength() && + current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI) { + return android::GetWifiSignalLevel().value_or(INT32_MIN); + } + + if (NetworkChangeNotifier::IsConnectionCellular(current_network_id_.type)) + return android::cellular_signal_strength::GetSignalStrengthLevel().value_or( + INT32_MIN); #endif // OS_ANDROID return INT32_MIN; @@ -830,6 +847,14 @@ if (current_network_id_.signal_strength == INT32_MIN) return effective_connection_type_; + // Capping ECT on WiFi is currently not enabled. + if (current_network_id_.type == NetworkChangeNotifier::CONNECTION_WIFI) { + // The maximum signal strength level is 4. + UMA_HISTOGRAM_EXACT_LINEAR("NQE.WifiSignalStrength.AtECTComputation", + current_network_id_.signal_strength, 4); + return effective_connection_type_; + } + if (effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_UNKNOWN || effective_connection_type_ == EFFECTIVE_CONNECTION_TYPE_OFFLINE) { return effective_connection_type_;
diff --git a/net/nqe/network_quality_estimator_params.cc b/net/nqe/network_quality_estimator_params.cc index 6b036c06..2c9e9b8 100644 --- a/net/nqe/network_quality_estimator_params.cc +++ b/net/nqe/network_quality_estimator_params.cc
@@ -507,6 +507,10 @@ params_, "upper_bound_typical_kbps_multiplier", 3.5)), + get_wifi_signal_strength_(GetStringValueForVariationParamWithDefaultValue( + params_, + "get_wifi_signal_strength", + "true") != "false"), use_small_responses_(false) { DCHECK(hanging_request_http_rtt_upper_bound_transport_rtt_multiplier_ == -1 || hanging_request_http_rtt_upper_bound_transport_rtt_multiplier_ > 0);
diff --git a/net/nqe/network_quality_estimator_params.h b/net/nqe/network_quality_estimator_params.h index f98a2848c..648ce80 100644 --- a/net/nqe/network_quality_estimator_params.h +++ b/net/nqe/network_quality_estimator_params.h
@@ -255,6 +255,9 @@ return upper_bound_typical_kbps_multiplier_; } + // Returns true if the signal strength should be queried on WiFi connections. + bool get_wifi_signal_strength() const { return get_wifi_signal_strength_; } + // Sets the forced effective connection type as |type|. void SetForcedEffectiveConnectionTypeForTesting(EffectiveConnectionType type); @@ -288,6 +291,7 @@ const bool use_end_to_end_rtt_; const bool cap_ect_based_on_signal_strength_; const double upper_bound_typical_kbps_multiplier_; + const bool get_wifi_signal_strength_; bool use_small_responses_;
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 460e2b6..a63b6ae 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h
@@ -1057,7 +1057,7 @@ PP_Bool /* init_to_zero */, ppapi::HostResource /* result_resource */, PP_ImageDataDesc /* image_data_desc */, - ppapi::proxy::ImageHandle /* result */) + ppapi::proxy::SerializedHandle /* result */) IPC_SYNC_MESSAGE_ROUTED4_3(PpapiHostMsg_PPBImageData_CreateSimple, PP_Instance /* instance */, int32_t /* format */,
diff --git a/ppapi/proxy/ppb_image_data_proxy.cc b/ppapi/proxy/ppb_image_data_proxy.cc index 718a8b3..fe88fca 100644 --- a/ppapi/proxy/ppb_image_data_proxy.cc +++ b/ppapi/proxy/ppb_image_data_proxy.cc
@@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/shared_memory_mapping.h" #include "base/memory/singleton.h" #include "base/memory/weak_ptr.h" #include "build/build_config.h" @@ -105,7 +106,7 @@ base::TimeTicks added_time; - // Set to true when the renderer tells us that it's OK to re-use this iamge. + // Set to true when the renderer tells us that it's OK to re-use this image. bool usable; scoped_refptr<ImageData> image; @@ -346,8 +347,8 @@ return PP_TRUE; } -int32_t ImageData::GetSharedMemory(base::SharedMemory** /* shm */, - uint32_t* /* byte_count */) { +int32_t ImageData::GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** /* region */) { // Not supported in the proxy (this method is for actually implementing the // proxy in the host). return PP_ERROR_NOACCESS; @@ -369,30 +370,30 @@ // PlatformImageData ----------------------------------------------------------- #if !defined(OS_NACL) -PlatformImageData::PlatformImageData(const HostResource& resource, - const PP_ImageDataDesc& desc, - ImageHandle handle) +PlatformImageData::PlatformImageData( + const HostResource& resource, + const PP_ImageDataDesc& desc, + base::UnsafeSharedMemoryRegion image_region) : ImageData(resource, PPB_ImageData_Shared::PLATFORM, desc) { #if defined(OS_WIN) - transport_dib_.reset(TransportDIB::CreateWithHandle(handle)); + transport_dib_ = TransportDIB::CreateWithHandle(std::move(image_region)); #else - transport_dib_.reset(TransportDIB::Map(handle)); + transport_dib_ = TransportDIB::Map(std::move(image_region)); #endif // defined(OS_WIN) } -PlatformImageData::~PlatformImageData() { -} +PlatformImageData::~PlatformImageData() = default; void* PlatformImageData::Map() { if (!mapped_canvas_.get()) { if (!transport_dib_.get()) - return NULL; + return nullptr; const bool is_opaque = false; mapped_canvas_ = transport_dib_->GetPlatformCanvas( desc_.size.width, desc_.size.height, is_opaque); if (!mapped_canvas_.get()) - return NULL; + return nullptr; } SkPixmap pixmap; skia::GetWritablePixels(mapped_canvas_.get(), &pixmap); @@ -408,40 +409,33 @@ SkCanvas* PlatformImageData::GetCanvas() { return mapped_canvas_.get(); } - -// static -ImageHandle PlatformImageData::NullHandle() { - return ImageHandle(); -} #endif // !defined(OS_NACL) // SimpleImageData ------------------------------------------------------------- SimpleImageData::SimpleImageData(const HostResource& resource, const PP_ImageDataDesc& desc, - const base::SharedMemoryHandle& handle) + base::UnsafeSharedMemoryRegion region) : ImageData(resource, PPB_ImageData_Shared::SIMPLE, desc), - shm_(handle, false /* read_only */), + shm_region_(std::move(region)), size_(desc.size.width * desc.size.height * 4), - map_count_(0) { -} + map_count_(0) {} -SimpleImageData::~SimpleImageData() { -} +SimpleImageData::~SimpleImageData() = default; void* SimpleImageData::Map() { if (map_count_++ == 0) - shm_.Map(size_); - return shm_.memory(); + shm_mapping_ = shm_region_.MapAt(0, size_); + return shm_mapping_.IsValid() ? shm_mapping_.memory() : nullptr; } void SimpleImageData::Unmap() { if (--map_count_ == 0) - shm_.Unmap(); + shm_mapping_ = base::WritableSharedMemoryMapping(); } SkCanvas* SimpleImageData::GetCanvas() { - return NULL; // No canvas available. + return nullptr; // No canvas available. } // PPB_ImageData_Proxy --------------------------------------------------------- @@ -478,28 +472,35 @@ PP_ImageDataDesc desc; switch (type) { case PPB_ImageData_Shared::SIMPLE: { - ppapi::proxy::SerializedHandle image_handle_wrapper; + ppapi::proxy::SerializedHandle image_handle; dispatcher->Send(new PpapiHostMsg_PPBImageData_CreateSimple( - kApiID, instance, format, size, init_to_zero, - &result, &desc, &image_handle_wrapper)); - if (image_handle_wrapper.is_shmem()) { - base::SharedMemoryHandle image_handle = image_handle_wrapper.shmem(); + kApiID, instance, format, size, init_to_zero, &result, &desc, + &image_handle)); + if (image_handle.is_shmem_region()) { + base::UnsafeSharedMemoryRegion image_region = + base::UnsafeSharedMemoryRegion::Deserialize( + image_handle.TakeSharedMemoryRegion()); if (!result.is_null()) { - return - (new SimpleImageData(result, desc, image_handle))->GetReference(); + return (new SimpleImageData(result, desc, std::move(image_region))) + ->GetReference(); } } break; } case PPB_ImageData_Shared::PLATFORM: { #if !defined(OS_NACL) - ImageHandle image_handle = PlatformImageData::NullHandle(); + ppapi::proxy::SerializedHandle image_handle; dispatcher->Send(new PpapiHostMsg_PPBImageData_CreatePlatform( - kApiID, instance, format, size, init_to_zero, - &result, &desc, &image_handle)); - if (!result.is_null()) { - return - (new PlatformImageData(result, desc, image_handle))->GetReference(); + kApiID, instance, format, size, init_to_zero, &result, &desc, + &image_handle)); + if (image_handle.is_shmem_region()) { + base::UnsafeSharedMemoryRegion image_region = + base::UnsafeSharedMemoryRegion::Deserialize( + image_handle.TakeSharedMemoryRegion()); + if (!result.is_null()) { + return (new PlatformImageData(result, desc, std::move(image_region))) + ->GetReference(); + } } #else // PlatformImageData shouldn't be created in untrusted code. @@ -538,8 +539,7 @@ const PP_Size& size, bool init_to_zero, PP_ImageDataDesc* desc, - base::SharedMemoryHandle* image_handle, - uint32_t* byte_count) { + base::UnsafeSharedMemoryRegion* image_region) { HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance); if (!dispatcher) return 0; @@ -576,15 +576,14 @@ return 0; } - base::SharedMemory* local_shm; - if (enter_resource.object()->GetSharedMemory(&local_shm, byte_count) != - PP_OK) { + base::UnsafeSharedMemoryRegion* local_shm; + if (enter_resource.object()->GetSharedMemoryRegion(&local_shm) != PP_OK) { DVLOG(1) << "CreateImageData failed: could not GetSharedMemory"; return 0; } - *image_handle = - dispatcher->ShareSharedMemoryHandleWithRemote(local_shm->handle()); + *image_region = + dispatcher->ShareUnsafeSharedMemoryRegionWithRemote(*local_shm); return resource.Release(); } @@ -595,22 +594,23 @@ PP_Bool init_to_zero, HostResource* result, PP_ImageDataDesc* desc, - ImageHandle* result_image_handle) { + ppapi::proxy::SerializedHandle* result_image_handle) { // Clear |desc| so we don't send uninitialized memory to the plugin. // https://crbug.com/391023. *desc = PP_ImageDataDesc(); - base::SharedMemoryHandle image_handle; - uint32_t byte_count; + base::UnsafeSharedMemoryRegion image_region; PP_Resource resource = - CreateImageData(instance, - PPB_ImageData_Shared::PLATFORM, - static_cast<PP_ImageDataFormat>(format), - size, - true /* init_to_zero */, - desc, &image_handle, &byte_count); + CreateImageData(instance, PPB_ImageData_Shared::PLATFORM, + static_cast<PP_ImageDataFormat>(format), size, + true /* init_to_zero */, desc, &image_region); result->SetHostResource(instance, resource); - *result_image_handle = - resource ? image_handle : PlatformImageData::NullHandle(); + if (resource) { + result_image_handle->set_shmem_region( + base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( + std::move(image_region))); + } else { + result_image_handle->set_null_shmem_region(); + } } void PPB_ImageData_Proxy::OnHostMsgCreateSimple( @@ -624,21 +624,18 @@ // Clear |desc| so we don't send uninitialized memory to the plugin. // https://crbug.com/391023. *desc = PP_ImageDataDesc(); - base::SharedMemoryHandle image_handle; - uint32_t byte_count; + base::UnsafeSharedMemoryRegion image_region; PP_Resource resource = - CreateImageData(instance, - PPB_ImageData_Shared::SIMPLE, - static_cast<PP_ImageDataFormat>(format), - size, - true /* init_to_zero */, - desc, &image_handle, &byte_count); - + CreateImageData(instance, PPB_ImageData_Shared::SIMPLE, + static_cast<PP_ImageDataFormat>(format), size, + true /* init_to_zero */, desc, &image_region); result->SetHostResource(instance, resource); if (resource) { - result_image_handle->set_shmem(image_handle, byte_count); + result_image_handle->set_shmem_region( + base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( + std::move(image_region))); } else { - result_image_handle->set_null_shmem(); + result_image_handle->set_null_shmem_region(); } } #endif // !defined(OS_NACL)
diff --git a/ppapi/proxy/ppb_image_data_proxy.h b/ppapi/proxy/ppb_image_data_proxy.h index 5effda73..6e97221 100644 --- a/ppapi/proxy/ppb_image_data_proxy.h +++ b/ppapi/proxy/ppb_image_data_proxy.h
@@ -10,7 +10,7 @@ #include <memory> #include "base/macros.h" -#include "base/memory/shared_memory.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "build/build_config.h" #include "ipc/ipc_platform_file.h" #include "ppapi/c/pp_bool.h" @@ -55,8 +55,8 @@ // PPB_ImageData API. PP_Bool Describe(PP_ImageDataDesc* desc) override; - int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) override; + int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) override; void SetIsCandidateForReuse() override; PPB_ImageData_Shared::ImageDataType type() const { return type_; } @@ -81,14 +81,14 @@ }; // PlatformImageData is a full featured image data resource which can access -// the underlying platform-specific canvas and ImageHandle. This can't be used -// by NaCl apps. +// the underlying platform-specific canvas and |image_region|. This can't be +// used by NaCl apps. #if !defined(OS_NACL) class PPAPI_PROXY_EXPORT PlatformImageData : public ImageData { public: PlatformImageData(const ppapi::HostResource& resource, const PP_ImageDataDesc& desc, - ImageHandle handle); + base::UnsafeSharedMemoryRegion image_region); ~PlatformImageData() override; // PPB_ImageData API. @@ -96,8 +96,6 @@ void Unmap() override; SkCanvas* GetCanvas() override; - static ImageHandle NullHandle(); - private: std::unique_ptr<TransportDIB> transport_dib_; @@ -115,7 +113,7 @@ public: SimpleImageData(const ppapi::HostResource& resource, const PP_ImageDataDesc& desc, - const base::SharedMemoryHandle& handle); + base::UnsafeSharedMemoryRegion region); ~SimpleImageData() override; // PPB_ImageData API. @@ -124,7 +122,8 @@ SkCanvas* GetCanvas() override; private: - base::SharedMemory shm_; + base::UnsafeSharedMemoryRegion shm_region_; + base::WritableSharedMemoryMapping shm_mapping_; uint32_t size_; int map_count_; @@ -151,7 +150,8 @@ // On failure, will return invalid resource (0). On success it will return a // valid resource and the out params will be written. // |desc| contains the result of Describe. - // |image_handle| and |byte_count| contain the result of GetSharedMemory. + // |image_region| and |byte_count| contain the result of + // GetSharedMemoryRegion. // NOTE: if |init_to_zero| is false, you should write over the entire image // to avoid leaking sensitive data to a less privileged process. PPAPI_PROXY_EXPORT static PP_Resource CreateImageData( @@ -161,8 +161,7 @@ const PP_Size& size, bool init_to_zero, PP_ImageDataDesc* desc, - base::SharedMemoryHandle* image_handle, - uint32_t* byte_count); + base::UnsafeSharedMemoryRegion* image_region); static const ApiID kApiID = API_ID_PPB_IMAGE_DATA; @@ -175,7 +174,7 @@ PP_Bool init_to_zero, HostResource* result, PP_ImageDataDesc* desc, - ImageHandle* result_image_handle); + ppapi::proxy::SerializedHandle* result_image_handle); void OnHostMsgCreateSimple( PP_Instance instance, int32_t format,
diff --git a/ppapi/proxy/serialized_structs.h b/ppapi/proxy/serialized_structs.h index bec991f..974132e1 100644 --- a/ppapi/proxy/serialized_structs.h +++ b/ppapi/proxy/serialized_structs.h
@@ -133,9 +133,6 @@ PP_HardwareAcceleration acceleration; }; -// TODO(raymes): Make ImageHandle compatible with SerializedHandle. -typedef base::SharedMemoryHandle ImageHandle; - } // namespace proxy } // namespace ppapi
diff --git a/ppapi/thunk/ppb_image_data_api.h b/ppapi/thunk/ppb_image_data_api.h index af1eaef..4e6a493 100644 --- a/ppapi/thunk/ppb_image_data_api.h +++ b/ppapi/thunk/ppb_image_data_api.h
@@ -13,7 +13,7 @@ class SkCanvas; namespace base { -class SharedMemory; +class UnsafeSharedMemoryRegion; } // namespace base namespace ppapi { @@ -28,8 +28,8 @@ virtual void Unmap() = 0; // Trusted inteface. - virtual int32_t GetSharedMemory(base::SharedMemory** shm, - uint32_t* byte_count) = 0; + virtual int32_t GetSharedMemoryRegion( + base::UnsafeSharedMemoryRegion** region) = 0; // Get the canvas that backs this ImageData, if there is one. // The canvas will be NULL:
diff --git a/services/identity/public/cpp/core_account_info_mojom_traits.h b/services/identity/public/cpp/core_account_info_mojom_traits.h index fd760e0..586b0c1 100644 --- a/services/identity/public/cpp/core_account_info_mojom_traits.h +++ b/services/identity/public/cpp/core_account_info_mojom_traits.h
@@ -16,7 +16,7 @@ struct StructTraits<identity::mojom::CoreAccountInfo::DataView, ::CoreAccountInfo> { static const std::string& account_id(const ::CoreAccountInfo& r) { - return r.account_id; + return r.account_id.id; } static const std::string& gaia(const ::CoreAccountInfo& r) { return r.gaia; }
diff --git a/services/identity/public/cpp/primary_account_mutator_unittest.cc b/services/identity/public/cpp/primary_account_mutator_unittest.cc index 6610d1d3..420d7fa4 100644 --- a/services/identity/public/cpp/primary_account_mutator_unittest.cc +++ b/services/identity/public/cpp/primary_account_mutator_unittest.cc
@@ -187,9 +187,9 @@ EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken( secondary_account_info.account_id)); EXPECT_TRUE(base::ContainsKey(observed_removals, - former_primary_account.account_id)); + former_primary_account.account_id.id)); EXPECT_FALSE(base::ContainsKey(observed_removals, - secondary_account_info.account_id)); + secondary_account_info.account_id.id)); break; case RemoveAccountExpectation::kRemoveAll: EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( @@ -197,9 +197,9 @@ EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken( secondary_account_info.account_id)); EXPECT_TRUE(base::ContainsKey(observed_removals, - former_primary_account.account_id)); + former_primary_account.account_id.id)); EXPECT_TRUE(base::ContainsKey(observed_removals, - secondary_account_info.account_id)); + secondary_account_info.account_id.id)); break; } }
diff --git a/services/metrics/ukm_api.md b/services/metrics/ukm_api.md index d6e13bd..58d74c6 100644 --- a/services/metrics/ukm_api.md +++ b/services/metrics/ukm_api.md
@@ -15,7 +15,7 @@ * If an event will only happen once per Navigation, it can be marked singular="true". ### Example -``` +```xml <event name="Goat.Teleported"> <owner>teleporter@chromium.org</owner> <summary> @@ -41,7 +41,7 @@ file in the Chromium codebase. To have a metric aggregated, `<aggregation>` and `<history>` tags need to be added. -``` +```xml <event name="Goat.Teleported"> <metric name="Duration"> ... @@ -94,7 +94,7 @@ supported). However, since generating statistics for this "key" metric isn't likely to be useful on its own, add "export=False" to its <statistics> tag. -``` +```xml <event name="Memory.Experimental"> <metric name="ProcessType"> <aggregation> @@ -160,11 +160,11 @@ There are two main ways of getting a UkmRecorder instance. -1) Use ukm::UkmRecorder::Get(). This currently only works from the Browser process. +1) Use `ukm::UkmRecorder::Get()`. This currently only works from the Browser process. 2) Use a service connector and get a UkmRecorder. -``` +```cpp std::unique_ptr<ukm::UkmRecorder> ukm_recorder = ukm::UkmRecorder::Create(context()->connector()); ukm::builders::MyEvent(source_id) @@ -176,18 +176,41 @@ UKM identifies navigations by their source ID and you'll need to associate and ID with your event in order to tie it to a main frame URL. Preferrably, get an existing ID for the navigation from another object. -The main methods for doing this are using one of the following methods: +The main method for doing this is by getting a navigation ID: -``` +```cpp ukm::SourceId source_id = GetSourceIdForWebContentsDocument(web_contents); ukm::SourceId source_id = ukm::ConvertToSourceId( navigation_handle->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID); ``` -Currently, however, the code for passing these IDs around is incomplete so you may need to temporarily create your own IDs and associate the URL with them. However we currently prefer that this method is not used, and if you need to setup the URL yourself, please email us first at ukm-team@google.com. +Some events however occur in the background, and a navigation ID does not exist. In that case, you can use the `ukm::UkmBackgroundRecorderService` to check whether the event can be recorded. This is achieved by using the History Service to determine whether the event's origin is still present in the user profile. + +```cpp +// Add the service as a dependency in your service's constructor. +DependsOn(ukm::UkmBackgroundRecorderFactory::GetInstance()); + +// Get an instance of the UKM service using a Profile. +auto* ukm_background_service = ukm::UkmBackgroundRecorderFactory::GetForProfile(profile); + +// Check if recording a background event for |origin| is allowed. +ukm_background_service->GetBackgroundSourceIdIfAllowed(origin, base::BindOnce(&DidGetBackgroundSourceId)); + +// A callback will run with an optional source ID. +void DidGetBackgroundSourceId(base::Optional<ukm::SourceId> source_id) { + if (!source_id) return; // Can't record as it wasn't found in the history. + + // Use the newly generated source ID. + ukm::builders::MyEvent(*source_id) + .SetMyMetric(metric_value) + .Record(ukm_recorder.get()); +} +``` + +For the remaining cases you may need to temporarily create your own IDs and associate the URL with them. However we currently prefer that this method is not used, and if you need to setup the URL yourself, please email us first at ukm-team@google.com. Example: -``` +```cpp ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID(); ukm_recorder->UpdateSourceURL(source_id, main_frame_url); ``` @@ -198,7 +221,7 @@ Helper objects for recording your event are generated from the descriptions in ukm.xml. You can use them like so: -``` +```cpp #include "services/metrics/public/cpp/ukm_builders.h" void OnGoatTeleported() {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index a3686805..9e6aee2 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -13727,6 +13727,2876 @@ } ] }, + "android-asan": { + "gtest_tests": [ + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "android_webview_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "android_webview_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "angle_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "angle_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "base_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "base_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "base_util_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "base_util_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "blink_common_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "blink_common_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "blink_heap_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "blink_heap_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "blink_platform_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "blink_platform_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "webkit_unit_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "webkit_unit_tests", + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 2 + }, + "test": "blink_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "boringssl_crypto_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "boringssl_crypto_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "boringssl_ssl_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "boringssl_ssl_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "breakpad_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "breakpad_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "cacheinvalidation_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "cacheinvalidation_unittests" + }, + { + "args": [ + "--gtest_filter=-*UsingRealWebcam*", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "capture_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "capture_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "cast_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "cast_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "cc_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "cc_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "chrome_public_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 20 + }, + "test": "chrome_public_test_apk" + }, + { + "args": [ + "--enable-features=NetworkService", + "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_chrome_public_test_apk.filter", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "network_service_chrome_public_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "network_service_chrome_public_test_apk", + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 20 + }, + "test": "chrome_public_test_apk" + }, + { + "args": [ + "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "chrome_public_test_vr_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 2 + }, + "test": "chrome_public_test_vr_apk" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "components_browsertests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "components_browsertests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "components_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 4 + }, + "test": "components_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "content_browsertests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 9 + }, + "test": "content_browsertests" + }, + { + "args": [ + "--disable-perfetto", + "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "nonperfetto_content_browsertests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "nonperfetto_content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "content_browsertests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "content_shell_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 3 + }, + "test": "content_shell_test_apk" + }, + { + "args": [ + "--enable-features=NetworkService", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "network_service_content_shell_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "network_service_content_shell_test_apk", + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 5 + }, + "test": "content_shell_test_apk" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "content_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 3 + }, + "test": "content_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "crypto_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "crypto_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "device_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "device_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "display_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "display_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "events_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "events_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gcm_unit_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gcm_unit_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gfx_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gfx_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gin_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gin_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gl_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gl_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gl_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gl_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "google_apis_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "google_apis_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "gpu_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "gpu_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "ipc_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "ipc_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "jingle_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "jingle_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "latency_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "latency_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "libjingle_xmpp_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "libjingle_xmpp_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "media_blink_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "media_blink_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "media_service_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "media_service_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "media_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "media_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "midi_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "midi_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "mojo_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "mojo_test_apk" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "mojo_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "mojo_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "net_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 3 + }, + "test": "net_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "perfetto_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "perfetto_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "sandbox_linux_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "sandbox_linux_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "services_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "services_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "shell_dialogs_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "shell_dialogs_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "skia_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "skia_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "sql_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "sql_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "storage_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "storage_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "ui_android_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "ui_android_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "ui_base_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "ui_base_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "ui_touch_selection_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "ui_touch_selection_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "unit_tests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 10 + }, + "test": "unit_tests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "url_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "url_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "viz_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "viz_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "vr_android_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "vr_android_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "vr_common_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "vr_common_unittests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "vr_pixeltests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "vr_pixeltests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "webview_instrumentation_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 7 + }, + "test": "webview_instrumentation_test_apk" + }, + { + "args": [ + "--enable-features=NetworkService,NetworkServiceInProcess", + "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter", + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "network_service_webview_instrumentation_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "network_service_webview_instrumentation_test_apk", + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "shards": 20 + }, + "test": "webview_instrumentation_test_apk" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "wtf_unittests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "wtf_unittests" + } + ] + }, "win-asan": { "gtest_tests": [ {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index e3a6f0d..80aef87 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1308,6 +1308,11 @@ "label": "//ios/showcase:ios_showcase_egtests", "type": "raw", }, + "ios_chrome_unified_consent_egtests": { + "args": [], + "label": "//ios/chrome/test/earl_grey:ios_chrome_unified_consent_egtests", + "type": "raw", + }, "ios_web_inttests": { "args": [], "label": "//ios/web:ios_web_inttests",
diff --git a/testing/buildbot/manage.py b/testing/buildbot/manage.py index 0ccad19..b4b3422 100755 --- a/testing/buildbot/manage.py +++ b/testing/buildbot/manage.py
@@ -105,6 +105,7 @@ 'ios_chrome_smoke_egtests', 'ios_chrome_translate_egtests', 'ios_chrome_ui_egtests', + 'ios_chrome_unified_consent_egtests', 'ios_chrome_unittests', 'ios_chrome_web_egtests', 'ios_components_unittests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 35e0db3..1507f9d 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -3819,6 +3819,21 @@ 'isolated_scripts': 'chromium_webkit_isolated_scripts', }, }, + 'android-asan': { + 'test_suites': { + 'gtest_tests': 'chromium_android_gtests', + }, + 'swarming': { + 'dimension_sets': [ + { + 'device_os': 'MMB29Q', + 'device_type': 'bullhead', + 'os': 'Android', + }, + ], + }, + 'os_type': 'android', + }, 'win-asan': { 'test_suites': { 'gtest_tests': 'chromium_win_gtests',
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc index d3c2d63..010fdca 100644 --- a/third_party/blink/common/feature_policy/feature_policy.cc +++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -365,6 +365,9 @@ {mojom::FeaturePolicyFeature::kExecutionWhileNotRendered, FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, mojom::PolicyValueType::kBool)}, + {mojom::FeaturePolicyFeature::kFocusWithoutUserActivation, + FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, + mojom::PolicyValueType::kBool)}, {mojom::FeaturePolicyFeature::kFontDisplay, FeatureDefaultValue(FeaturePolicy::FeatureDefault::EnableForAll, mojom::PolicyValueType::kBool)},
diff --git a/third_party/blink/perf_tests/display_locking/abs_pos_outer_sizes_change.html b/third_party/blink/perf_tests/display_locking/abs_pos_outer_sizes_change.html new file mode 100644 index 0000000..d301f5498 --- /dev/null +++ b/third_party/blink/perf_tests/display_locking/abs_pos_outer_sizes_change.html
@@ -0,0 +1,106 @@ +<!doctype HTML> + +<!-- +This test appends 30,000 items to the page, locking all but the first one. +It then changes the style of all the locked elements, causing self-layout +for all of them. + +Note that there are 3 absolute positioned divs in each of the locked +elements. This test ensures we skip out of flow positioned children layout +when display locked. + +The test works with and without display locking. If display locking is not +enabled, then none of the elements are locked and the performance should +be noticeably worse. +--> + +<head> +<script src="../resources/runner.js"></script> +<style> +.container { + contain: style layout; + width: 200px; +} +.box { + background: blue; + overflow: hidden; + height: 100px; + position: absolute; + right: 1%; +} +.spacer { + width: 100%; + height: 300px; + background: lightblue; +} +</style> +</head> + +<body> +<!-- node template from which to construct items --> +<template id="node_template"> +<div class="container"> + <div class="spacer"></div> + <div class="box" style="width: 50%; top: 0px;"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. + </div> + <div class="box" style="width: 75%; top: 100px;"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. + </div> + <div class="box" style="width: 98%; top: 200px;"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. + </div> +</div> +</template> +</body> + +<script> +function construct(n) { + const specimen = document.importNode( + document.getElementById("node_template").content, true).firstElementChild; + for (let i = 0; i < n; ++i) { + const clone = specimen.cloneNode(true); + if (clone.displayLock && i > 0) + clone.displayLock.acquire({ timeout: Infinity, size: [100, 100] }); + document.body.appendChild(clone); + } +} + +let sizes = ["200px", "250px"]; +let size_index = 0; +function changeStyle() { + document.styleSheets[0].rules[0].style.width = sizes[size_index]; + size_index = 1 - size_index; +} + +let testDone = false; +let startTime; +function runTest() { + if (startTime) { + PerfTestRunner.measureValueAsync(PerfTestRunner.now() - startTime); + PerfTestRunner.addRunTestEndMarker(); + } + if (testDone) + return; + + startTime = PerfTestRunner.now(); + PerfTestRunner.addRunTestEndMarker(); + + changeStyle(); + requestAnimationFrame(runTest); +} + +construct(30000); + +PerfTestRunner.startMeasureValuesAsync({ + unit: 'ms', + done: () => { testDone = true; }, + run: runTest, + warmUpCount: 3, + iterationCount: 5 +}); + +</script>
diff --git a/third_party/blink/perf_tests/display_locking/outer_sizes_change.html b/third_party/blink/perf_tests/display_locking/outer_sizes_change.html new file mode 100644 index 0000000..5d0d7a4 --- /dev/null +++ b/third_party/blink/perf_tests/display_locking/outer_sizes_change.html
@@ -0,0 +1,91 @@ +<!doctype HTML> + +<!-- +This test appends 30,000 items to the page, locking all but the first one. +It then changes the style of all the locked elements, causing self-layout +for all of them. + +The test works with and without display locking. If display locking is not +enabled, then none of the elements are locked and the performance should +be noticeably worse. +--> + +<head> +<script src="../resources/runner.js"></script> +<style> +.container { + contain: style layout; + width: 200px; +} +.box { + background: blue; + overflow: hidden; + width: 100px; + height: 100px; +} +</style> +</head> + +<body> +<!-- node template from which to construct items --> +<template id="node_template"> +<div class="container"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. + <div class="box"> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. + </div> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Quisque ante dui, posuere at pretium suscipit, condimentum at augue. +</div> +</template> +</body> + +<script> +function construct(n) { + const specimen = document.importNode( + document.getElementById("node_template").content, true).firstElementChild; + for (let i = 0; i < n; ++i) { + const clone = specimen.cloneNode(true); + if (clone.displayLock && i > 0) + clone.displayLock.acquire({ timeout: Infinity, size: [100, 100] }); + document.body.appendChild(clone); + } +} + +let sizes = ["200px", "250px"]; +let size_index = 0; +function changeStyle() { + document.styleSheets[0].rules[0].style.width = sizes[size_index]; + size_index = 1 - size_index; +} + +let testDone = false; +let startTime; +function runTest() { + if (startTime) { + PerfTestRunner.measureValueAsync(PerfTestRunner.now() - startTime); + PerfTestRunner.addRunTestEndMarker(); + } + if (testDone) + return; + + startTime = PerfTestRunner.now(); + PerfTestRunner.addRunTestEndMarker(); + + changeStyle(); + requestAnimationFrame(runTest); +} + +construct(30000); + +PerfTestRunner.startMeasureValuesAsync({ + unit: 'ms', + done: () => { testDone = true; }, + run: runTest, + warmUpCount: 3, + iterationCount: 5 +}); + +</script>
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 9b1691c..67d989af 100644 --- a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom +++ b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
@@ -138,6 +138,9 @@ // Allow execution while not rendered. kExecutionWhileNotRendered = 51, + // When disallowed, a frame without user activation cannot acquire focus. + kFocusWithoutUserActivation = 52, + // Don't change assigned numbers of any item, and don't reuse removed slots. // Add new features at the end of the enum. // Also, run update_feature_policy_enum.py in
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h index 811a2cea..35c2034d 100644 --- a/third_party/blink/public/platform/web_runtime_features.h +++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -84,6 +84,8 @@ BLINK_PLATFORM_EXPORT static void EnableBackgroundFetch(bool); BLINK_PLATFORM_EXPORT static void EnableBlinkHeapIncrementalMarking(bool); BLINK_PLATFORM_EXPORT static void EnableBloatedRendererDetection(bool); + BLINK_PLATFORM_EXPORT static void EnableBlockingFocusWithoutUserActivation( + bool); BLINK_PLATFORM_EXPORT static void EnableCacheInlineScriptCode(bool); BLINK_PLATFORM_EXPORT static void EnableIsolatedCodeCache(bool); BLINK_PLATFORM_EXPORT static void EnableWasmCodeCache(bool);
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h index 13ac49de6..7a4be89 100644 --- a/third_party/blink/public/web/web_ax_object.h +++ b/third_party/blink/public/web/web_ax_object.h
@@ -283,6 +283,7 @@ BLINK_EXPORT bool HasComputedStyle() const; BLINK_EXPORT WebString ComputedStyleDisplay() const; BLINK_EXPORT bool AccessibilityIsIgnored() const; + BLINK_EXPORT bool AccessibilityIsIncludedInTree() const; BLINK_EXPORT void Markers(WebVector<ax::mojom::MarkerType>& types, WebVector<int>& starts, WebVector<int>& ends) const;
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn index 33ac4e1..5e80e12 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1974,6 +1974,7 @@ "layout/layout_text_test.cc", "layout/layout_theme_test.cc", "layout/layout_view_test.cc", + "layout/line/abstract_inline_text_box_test.cc", "layout/line/inline_text_box_test.cc", "layout/line/line_orientation_utils_test.cc", "layout/map_coordinates_test.cc",
diff --git a/third_party/blink/renderer/core/dom/child_list_mutation_scope.cc b/third_party/blink/renderer/core/dom/child_list_mutation_scope.cc index 7d9f0ae..094924a 100644 --- a/third_party/blink/renderer/core/dom/child_list_mutation_scope.cc +++ b/third_party/blink/renderer/core/dom/child_list_mutation_scope.cc
@@ -84,45 +84,45 @@ return accumulator; } -inline bool ChildListMutationAccumulator::IsAddedNodeInOrder(Node* child) { - return IsEmpty() || (last_added_ == child->previousSibling() && - next_sibling_ == child->nextSibling()); +inline bool ChildListMutationAccumulator::IsAddedNodeInOrder(Node& child) { + return IsEmpty() || (last_added_ == child.previousSibling() && + next_sibling_ == child.nextSibling()); } -void ChildListMutationAccumulator::ChildAdded(Node* child) { +void ChildListMutationAccumulator::ChildAdded(Node& child) { DCHECK(HasObservers()); if (!IsAddedNodeInOrder(child)) EnqueueMutationRecord(); if (IsEmpty()) { - previous_sibling_ = child->previousSibling(); - next_sibling_ = child->nextSibling(); + previous_sibling_ = child.previousSibling(); + next_sibling_ = child.nextSibling(); } - last_added_ = child; - added_nodes_.push_back(child); + last_added_ = &child; + added_nodes_.push_back(&child); } -inline bool ChildListMutationAccumulator::IsRemovedNodeInOrder(Node* child) { - return IsEmpty() || next_sibling_ == child; +inline bool ChildListMutationAccumulator::IsRemovedNodeInOrder(Node& child) { + return IsEmpty() || next_sibling_ == &child; } -void ChildListMutationAccumulator::WillRemoveChild(Node* child) { +void ChildListMutationAccumulator::WillRemoveChild(Node& child) { DCHECK(HasObservers()); if (!added_nodes_.IsEmpty() || !IsRemovedNodeInOrder(child)) EnqueueMutationRecord(); if (IsEmpty()) { - previous_sibling_ = child->previousSibling(); - next_sibling_ = child->nextSibling(); - last_added_ = child->previousSibling(); + previous_sibling_ = child.previousSibling(); + next_sibling_ = child.nextSibling(); + last_added_ = child.previousSibling(); } else { - next_sibling_ = child->nextSibling(); + next_sibling_ = child.nextSibling(); } - removed_nodes_.push_back(child); + removed_nodes_.push_back(&child); } void ChildListMutationAccumulator::EnqueueMutationRecord() {
diff --git a/third_party/blink/renderer/core/dom/child_list_mutation_scope.h b/third_party/blink/renderer/core/dom/child_list_mutation_scope.h index 62cd18ac..718d39d 100644 --- a/third_party/blink/renderer/core/dom/child_list_mutation_scope.h +++ b/third_party/blink/renderer/core/dom/child_list_mutation_scope.h
@@ -56,8 +56,8 @@ ChildListMutationAccumulator(Node*, MutationObserverInterestGroup*); - void ChildAdded(Node*); - void WillRemoveChild(Node*); + void ChildAdded(Node&); + void WillRemoveChild(Node&); bool HasObservers() const { return observers_; } @@ -71,8 +71,8 @@ private: void EnqueueMutationRecord(); bool IsEmpty(); - bool IsAddedNodeInOrder(Node*); - bool IsRemovedNodeInOrder(Node*); + bool IsAddedNodeInOrder(Node&); + bool IsRemovedNodeInOrder(Node&); Member<Node> target_; @@ -110,12 +110,12 @@ void ChildAdded(Node& child) { if (accumulator_ && accumulator_->HasObservers()) - accumulator_->ChildAdded(&child); + accumulator_->ChildAdded(child); } void WillRemoveChild(Node& child) { if (accumulator_ && accumulator_->HasObservers()) - accumulator_->WillRemoveChild(&child); + accumulator_->WillRemoveChild(child); } private:
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index 237f186..546f58b 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6279,7 +6279,17 @@ opener_feature_state = &frame_->OpenerFeatureState(); } - InitializeFeaturePolicy(declared_policy, GetOwnerContainerPolicy(), + auto container_policy = GetOwnerContainerPolicy(); + if (RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled() && + frame_ && frame_->Tree().Parent() && + IsSandboxed(WebSandboxFlags::kNavigation)) { + // Enforcing the policy for sandbox frames (for context see + // https://crbug.com/954349). + DisallowFeatureIfNotPresent( + mojom::FeaturePolicyFeature::kFocusWithoutUserActivation, + container_policy); + } + InitializeFeaturePolicy(declared_policy, container_policy, GetParentFeaturePolicy(), opener_feature_state); // At this point, the document will not have been installed in the frame's @@ -7778,8 +7788,7 @@ if (!frame_ || frame_->IsMainFrame() || LocalFrame::HasTransientUserActivation(frame_)) { // 'autofocus' runs Element::focus asynchronously at which point the - // document might actually does not have a frame (see - // https://crbug.com/960224). + // document might not have a frame (see https://crbug.com/960224). return true; } @@ -7795,11 +7804,10 @@ : WebFeature::kFocusWithoutUserActivationNotSandboxedNotAdFrame; } UseCounter::Count(*this, uma_type); - if (!base::FeatureList::IsEnabled( - features::kBlockingFocusWithoutUserActivation)) { + if (!RuntimeEnabledFeatures::BlockingFocusWithoutUserActivationEnabled()) return true; - } - return !sandboxed; + return IsFeatureEnabled( + mojom::FeaturePolicyFeature::kFocusWithoutUserActivation); } LazyLoadImageObserver& Document::EnsureLazyLoadImageObserver() {
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 5550f0b..a928765 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
@@ -74,6 +74,11 @@ depends_on: ["FreezeFramesOnVisibility"], }, { + name: "FocusWithoutUserActivation", + feature_policy_name: "focus-without-user-activation", + depends_on: ["BlockingFocusWithoutUserActivation"], + }, + { name: "FontDisplay", feature_policy_name: "font-display-late-swap", depends_on: ["ExperimentalProductivityFeatures"],
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc index deb48913..d74a114c 100644 --- a/third_party/blink/renderer/core/inspector/devtools_session.cc +++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -135,7 +135,8 @@ inspector_backend_dispatcher_(new protocol::UberDispatcher(this)), session_state_(std::move(reattach_session_state)), v8_session_state_(kV8StateKey), - v8_session_state_json_(&v8_session_state_, /*default_value=*/String()) { + v8_session_state_cbor_(&v8_session_state_, + /*default_value=*/std::vector<uint8_t>()) { io_session_ = new IOSession(agent_->io_task_runner_, agent_->inspector_task_runner_, WrapCrossThreadWeakPersistent(this), std::move(io_request)); @@ -160,9 +161,10 @@ void DevToolsSession::ConnectToV8(v8_inspector::V8Inspector* inspector, int context_group_id) { + const std::vector<uint8_t>& cbor = v8_session_state_cbor_.Get(); v8_session_ = inspector->connect(context_group_id, this, - ToV8InspectorStringView(v8_session_state_json_.Get())); + v8_inspector::StringView(cbor.data(), cbor.size())); } bool DevToolsSession::IsDetached() { @@ -285,7 +287,7 @@ return; flushProtocolNotifications(); if (v8_session_) - v8_session_state_json_.Set(ToCoreString(v8_session_->stateJSON())); + v8_session_state_cbor_.Set(v8_session_->state()); // Make tests more predictable by flushing all sessions before sending // protocol response in any of them. if (WebTestSupport::IsRunningWebTest()) @@ -356,7 +358,7 @@ if (!notification_queue_.size()) return; if (v8_session_) - v8_session_state_json_.Set(ToCoreString(v8_session_->stateJSON())); + v8_session_state_cbor_.Set(v8_session_->state()); for (wtf_size_t i = 0; i < notification_queue_.size(); ++i) { auto serialized = notification_queue_[i]->Serialize(); host_ptr_->DispatchProtocolNotification(std::move(serialized),
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.h b/third_party/blink/renderer/core/inspector/devtools_session.h index 8b91b32..e554d87 100644 --- a/third_party/blink/renderer/core/inspector/devtools_session.h +++ b/third_party/blink/renderer/core/inspector/devtools_session.h
@@ -96,7 +96,7 @@ class Notification; Vector<std::unique_ptr<Notification>> notification_queue_; InspectorAgentState v8_session_state_; - InspectorAgentState::String v8_session_state_json_; + InspectorAgentState::Bytes v8_session_state_cbor_; DISALLOW_COPY_AND_ASSIGN(DevToolsSession); };
diff --git a/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc new file mode 100644 index 0000000..319da17 --- /dev/null +++ b/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc
@@ -0,0 +1,42 @@ +// 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 "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "third_party/blink/renderer/core/layout/line/abstract_inline_text_box.h" + +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" + +namespace blink { + +class AbstractInlineTextBoxTest : public testing::WithParamInterface<bool>, + private ScopedLayoutNGForTest, + public RenderingTest { + public: + AbstractInlineTextBoxTest() : ScopedLayoutNGForTest(GetParam()) {} + + protected: + bool LayoutNGEnabled() const { return GetParam(); } +}; + +INSTANTIATE_TEST_SUITE_P(All, AbstractInlineTextBoxTest, testing::Bool()); + +TEST_P(AbstractInlineTextBoxTest, GetTextWithTrailingWhiteSpace) { + SetBodyInnerHTML(R"HTML( + <style>* { font-size: 10px; }</style> + <div style="width: 10ch"><label id=label>abc: <input></label></div>)HTML"); + + const Element& label = *GetDocument().getElementById("label"); + LayoutText& layout_text = + *ToLayoutText(label.firstChild()->GetLayoutObject()); + scoped_refptr<AbstractInlineTextBox> inline_text_box = + layout_text.FirstAbstractInlineTextBox(); + + EXPECT_EQ("abc: ", inline_text_box->GetText()); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc index a55d61b..daf805ec 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" @@ -68,12 +69,6 @@ fragment_ = nullptr; } -bool NGAbstractInlineTextBox::HasSoftWrapToNextLine() const { - return To<NGPhysicalLineBoxFragment>( - fragment_->ContainerLineBox()->PhysicalFragment()) - .HasSoftWrapToNextLine(); -} - const NGPhysicalTextFragment& NGAbstractInlineTextBox::PhysicalTextFragment() const { return To<NGPhysicalTextFragment>(fragment_->PhysicalFragment()); @@ -84,13 +79,22 @@ } bool NGAbstractInlineTextBox::NeedsTrailingSpace() const { - if (!HasSoftWrapToNextLine()) + if (!fragment_->Style().CollapseWhiteSpace()) return false; - const NGPaintFragment* next_fragment = NextTextFragmentForSameLayoutObject(); - if (!next_fragment) + const NGPaintFragment& line_box = *fragment_->ContainerLineBox(); + if (!To<NGPhysicalLineBoxFragment>(line_box.PhysicalFragment()) + .HasSoftWrapToNextLine()) return false; - return To<NGPhysicalTextFragment>(next_fragment->PhysicalFragment()) - .StartOffset() != PhysicalTextFragment().EndOffset(); + const NGPhysicalTextFragment& text_fragment = PhysicalTextFragment(); + if (text_fragment.EndOffset() >= text_fragment.TextContent().length()) + return false; + if (text_fragment.TextContent()[text_fragment.EndOffset()] != ' ') + return false; + const NGInlineBreakToken& break_token = + *To<NGInlineBreakToken>(line_box.PhysicalFragment().BreakToken()); + // TODO(yosin): We should support OOF fragments between |fragment_| and + // break token. + return break_token.TextOffset() == text_fragment.EndOffset() + 1; } const NGPaintFragment*
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h index d74e962..fb2368e 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
@@ -32,7 +32,6 @@ NGAbstractInlineTextBox(LineLayoutText line_layout_item, const NGPaintFragment& fragment); - bool HasSoftWrapToNextLine() const; const NGPhysicalTextFragment& PhysicalTextFragment() const; bool NeedsLayout() const; bool NeedsTrailingSpace() const;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index bb6d72a..479a445 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -936,18 +936,6 @@ PluginScriptForbiddenScope forbid_plugin_destructor_scripting; DocumentLoader* pdl = provisional_document_loader_; - if (frame_->GetDocument()) { - unsigned node_count = 0; - for (Frame* frame = frame_; frame; frame = frame->Tree().TraverseNext()) { - if (auto* local_frame = DynamicTo<LocalFrame>(frame)) - node_count += local_frame->GetDocument()->NodeCount(); - } - unsigned total_node_count = - InstanceCounters::CounterValue(InstanceCounters::kNodeCounter); - float ratio = static_cast<float>(node_count) / total_node_count; - ThreadState::Current()->SchedulePageNavigationGCIfNeeded(ratio); - } - // Don't allow this frame to navigate anymore. This line is needed for // navigation triggered from children's unload handlers. Blocking navigations // triggered from this frame's unload handler is already covered in
diff --git a/third_party/blink/renderer/core/loader/interactive_detector_test.cc b/third_party/blink/renderer/core/loader/interactive_detector_test.cc index f0e2cba2..e895da0 100644 --- a/third_party/blink/renderer/core/loader/interactive_detector_test.cc +++ b/third_party/blink/renderer/core/loader/interactive_detector_test.cc
@@ -522,10 +522,9 @@ t0 + TimeDelta::FromSeconds(4)); // Post a task with 6 seconds duration. - PostCrossThreadTask( - *Thread::Current()->GetTaskRunner(), FROM_HERE, - CrossThreadBind(&InteractiveDetectorTest::DummyTaskWithDuration, - CrossThreadUnretained(this), 6.0)); + Thread::Current()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, + WTF::Unretained(this), 6.0)); platform_->RunUntilIdle(); @@ -543,10 +542,9 @@ t0 + TimeDelta::FromSeconds(4)); // Long task 1. - PostCrossThreadTask( - *Thread::Current()->GetTaskRunner(), FROM_HERE, - CrossThreadBind(&InteractiveDetectorTest::DummyTaskWithDuration, - CrossThreadUnretained(this), 0.1)); + Thread::Current()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, + WTF::Unretained(this), 0.1)); platform_->RunUntilIdle(); @@ -556,10 +554,9 @@ EXPECT_EQ(GetDetector()->GetInteractiveTime(), long_task_1_end_time); // Long task 2. - PostCrossThreadTask( - *Thread::Current()->GetTaskRunner(), FROM_HERE, - CrossThreadBind(&InteractiveDetectorTest::DummyTaskWithDuration, - CrossThreadUnretained(this), 0.1)); + Thread::Current()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, + WTF::Unretained(this), 0.1)); platform_->RunUntilIdle(); // Wait 5 seconds to see if TTI time changes.
diff --git a/third_party/blink/renderer/core/loader/long_task_detector_test.cc b/third_party/blink/renderer/core/loader/long_task_detector_test.cc index ce980c07..82fdf0d 100644 --- a/third_party/blink/renderer/core/loader/long_task_detector_test.cc +++ b/third_party/blink/renderer/core/loader/long_task_detector_test.cc
@@ -53,10 +53,9 @@ TimeTicks DummyTaskEndTime() { return dummy_task_end_time_; } void SimulateTask(base::TimeDelta duration) { - PostCrossThreadTask( - *Thread::Current()->GetTaskRunner(), FROM_HERE, - CrossThreadBind(&LongTaskDetectorTest::DummyTaskWithDuration, - CrossThreadUnretained(this), duration)); + Thread::Current()->GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&LongTaskDetectorTest::DummyTaskWithDuration, + WTF::Unretained(this), duration)); platform_->RunUntilIdle(); }
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc index 8e565a75..4fcf3ace 100644 --- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc +++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -220,6 +220,8 @@ // worklets. if (global_scope_->IsWorkletGlobalScope()) return; + if (!resource_timing_notifier_) + return; const SecurityOrigin* security_origin = GetResourceFetcherProperties() .GetFetchClientSettingsObject() .GetSecurityOrigin(); @@ -242,6 +244,11 @@ out_request.SetTopFrameOrigin(GetTopFrameOrigin()); } +FetchContext* WorkerFetchContext::Detach() { + resource_timing_notifier_.Clear(); + return BaseFetchContext::Detach(); +} + void WorkerFetchContext::SetFirstPartyCookie(ResourceRequest& out_request) { if (out_request.SiteForCookies().IsNull()) out_request.SetSiteForCookies(GetSiteForCookies());
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.h b/third_party/blink/renderer/core/loader/worker_fetch_context.h index 1c596c8..6d0fda6 100644 --- a/third_party/blink/renderer/core/loader/worker_fetch_context.h +++ b/third_party/blink/renderer/core/loader/worker_fetch_context.h
@@ -82,6 +82,7 @@ const ClientHintsPreferences&, const FetchParameters::ResourceWidth&, ResourceRequest&) override; + FetchContext* Detach() override; WorkerSettings* GetWorkerSettings() const; WorkerContentSettingsClient* GetWorkerContentSettingsClient() const; @@ -109,8 +110,7 @@ // WorkerGlobalScope and owned by this WorkerFetchContext. const Member<ContentSecurityPolicy> content_security_policy_; - const CrossThreadPersistent<WorkerResourceTimingNotifier> - resource_timing_notifier_; + CrossThreadPersistent<WorkerResourceTimingNotifier> resource_timing_notifier_; // The value of |save_data_enabled_| is read once per frame from // NetworkStateNotifier, which is guarded by a mutex lock, and cached locally
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index 70bedf0..e902c05 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2471,7 +2471,6 @@ float zoom = GetLayoutObject().StyleRef().EffectiveZoom(); if (zoom != 1) reference_box.Scale(1 / zoom); - reference_box.Move(-ToFloatSize(FilterReferenceBox().Location())); return reference_box; }
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h index cf7c3f6..c002e63a 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.h +++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -644,8 +644,7 @@ PaintLayerResourceInfo& EnsureResourceInfo(); // Filter reference box is the area over which the filter is computed, in the - // coordinate system of the object with the filter. Filter bounds is the - // reference box, offset by the object's location in the graphics layer. + // local coordinate system of the effect node containing the filter. FloatRect FilterReferenceBox() const; FloatRect BackdropFilterReferenceBox() const; gfx::RRectF BackdropFilterBounds(const FloatRect& reference_box) const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc index 57cdeb26..ef0aaf32 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -2724,7 +2724,7 @@ layout_text->FirstAbstractInlineTextBox(); box.get(); box = box->NextInlineTextBox()) { AXObject* ax_object = AXObjectCache().GetOrCreate(box.get()); - if (!ax_object->AccessibilityIsIgnored()) + if (ax_object->AccessibilityIsIncludedInTree()) children_.push_back(ax_object); } } @@ -3448,7 +3448,7 @@ // Find out where the last layout sibling is located within m_children. if (AXObject* child_object = AXObjectCache().Get(child.GetLayoutObject())) { - if (child_object->AccessibilityIsIgnored()) { + if (!child_object->AccessibilityIsIncludedInTree()) { const auto& children = child_object->Children(); child_object = children.size() ? children.back().Get() : nullptr; } @@ -3487,7 +3487,7 @@ AXImageMapLink* area_object = ToAXImageMapLink(obj); area_object->SetParent(this); DCHECK_NE(area_object->AXObjectID(), 0U); - if (!area_object->AccessibilityIsIgnored()) + if (area_object->AccessibilityIsIncludedInTree()) children_.push_back(area_object); else AXObjectCache().Remove(area_object->AXObjectID()); @@ -3529,7 +3529,7 @@ root->SetParent(this); - if (root->AccessibilityIsIgnored()) { + if (!root->AccessibilityIsIncludedInTree()) { for (const auto& child : root->Children()) children_.push_back(child); } else { @@ -3550,7 +3550,7 @@ if (HTMLTableCaptionElement* caption = ToHTMLTableElement(table_node)->caption()) { AXObject* caption_object = ax_cache.GetOrCreate(caption); - if (caption_object && !caption_object->AccessibilityIsIgnored()) + if (caption_object && caption_object->AccessibilityIsIncludedInTree()) children_.push_front(caption_object); } }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc index cdd8977..caaeda8 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_menu_list.cc
@@ -78,7 +78,7 @@ return; ToAXMockObject(popup)->SetParent(this); - if (popup->AccessibilityIsIgnored()) { + if (!popup->AccessibilityIsIncludedInTree()) { cache.Remove(popup->AXObjectID()); return; }
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 2868adb..1e10458 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1486,7 +1486,7 @@ for (AXObject* child : parent->Children()) { DCHECK(child); if (child->RoleValue() == ax::mojom::Role::kRadioButton && - !child->AccessibilityIsIgnored()) { + child->AccessibilityIsIncludedInTree()) { radio_buttons.push_back(child); } } @@ -2364,7 +2364,7 @@ // getting children, ensure data is not stale. child->ClearChildren(); - if (child->AccessibilityIsIgnored()) { + if (!child->AccessibilityIsIncludedInTree()) { const auto& children = child->Children(); wtf_size_t length = children.size(); for (wtf_size_t i = 0; i < length; ++i)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index 6041c19..98b3dc4 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -888,6 +888,12 @@ return cached_is_ignored_; } +// TODO(janewman) AccessibilityIsIncludedInTree should be true for all nodes +// that should be included in the tree, ignored or not. +bool AXObject::AccessibilityIsIncludedInTree() const { + return !AccessibilityIsIgnored(); +} + void AXObject::UpdateCachedAttributeValuesIfNeeded() const { if (IsDetached()) return; @@ -904,7 +910,8 @@ cached_is_descendant_of_disabled_node_ = !!DisabledAncestor(); cached_has_inherited_presentational_role_ = !!InheritsPresentationalRoleFrom(); - cached_is_ignored_ = ComputeAccessibilityIsIgnored(); + IgnoredReasons ignored_reasons; + cached_is_ignored_ = ComputeAccessibilityIsIgnored(&ignored_reasons); cached_is_editable_root_ = ComputeIsEditableRoot(); // Compute live region root, which can be from any ARIA live value, including // "off", or from an automatic ARIA live value, e.g. from role="status".
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h index 633f429..cbedc3b6 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -561,8 +561,13 @@ virtual bool CanSetFocusAttribute() const; bool CanSetValueAttribute() const; - // Whether objects are ignored, i.e. not included in the tree. + // Whether objects are ignored, i.e. hidden in the tree for most ATs bool AccessibilityIsIgnored() const; + + // Whether objects are included in the tree. A node may be Ignored, + // but allowed to pass into the tree, e.g. a hidden node referenced + // by labeled-by. + bool AccessibilityIsIncludedInTree() const; typedef HeapVector<IgnoredReason> IgnoredReasons; virtual bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const { return true; @@ -1107,6 +1112,7 @@ mutable int last_modification_count_; mutable RGBA32 cached_background_color_; mutable bool cached_is_ignored_ : 1; + mutable bool cached_is_inert_or_aria_hidden_ : 1; mutable bool cached_is_descendant_of_leaf_node_ : 1; mutable bool cached_is_descendant_of_disabled_node_ : 1;
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 0ac1c5b..2c689c0 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
@@ -219,7 +219,7 @@ // the HTML element, for example, is focusable but has an AX object that is // ignored - if (obj->AccessibilityIsIgnored()) + if (!obj->AccessibilityIsIncludedInTree()) obj = obj->ParentObjectUnignored(); return obj; @@ -1437,7 +1437,8 @@ return nullptr; AXObject* accessible_object = GetOrCreate(node->GetLayoutObject()); - while (accessible_object && accessible_object->AccessibilityIsIgnored()) { + while (accessible_object && + !accessible_object->AccessibilityIsIncludedInTree()) { node = NodeTraversal::Next(*node); while (node && !node->GetLayoutObject()) @@ -1615,7 +1616,7 @@ AXObject* obj = GetOrCreate(anchor_node->GetLayoutObject()); if (!obj) return; - if (obj->AccessibilityIsIgnored()) + if (!obj->AccessibilityIsIncludedInTree()) obj = obj->ParentObjectUnignored(); PostNotification(obj, ax::mojom::Event::kScrolledToAnchor); }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_slider.cc b/third_party/blink/renderer/modules/accessibility/ax_slider.cc index 8dc379b2..05a3e36 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_slider.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_slider.cc
@@ -87,7 +87,7 @@ // Before actually adding the value indicator to the hierarchy, // allow the platform to make a final decision about it. - if (thumb->AccessibilityIsIgnored()) + if (!thumb->AccessibilityIsIncludedInTree()) cache.Remove(thumb->AXObjectID()); else children_.push_back(thumb);
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 84ec4bb2..4829781 100644 --- a/third_party/blink/renderer/modules/exported/web_ax_object.cc +++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1231,6 +1231,13 @@ return private_->AccessibilityIsIgnored(); } +bool WebAXObject::AccessibilityIsIncludedInTree() const { + if (IsDetached()) + return false; + + return private_->AccessibilityIsIncludedInTree(); +} + int WebAXObject::AriaColumnCount() const { if (IsDetached()) return 0;
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc index db7b4a8..6fc8fc49 100644 --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -43,6 +43,10 @@ RuntimeEnabledFeatures::SetBloatedRendererDetectionEnabled(enable); } +void WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(bool enable) { + RuntimeEnabledFeatures::SetBlockingFocusWithoutUserActivationEnabled(enable); +} + void WebRuntimeFeatures::EnableExperimentalFeatures(bool enable) { RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(enable); }
diff --git a/third_party/blink/renderer/platform/heap/blink_gc.cc b/third_party/blink/renderer/platform/heap/blink_gc.cc index 70c12d53..c645ccb 100644 --- a/third_party/blink/renderer/platform/heap/blink_gc.cc +++ b/third_party/blink/renderer/platform/heap/blink_gc.cc
@@ -18,14 +18,14 @@ return "ForcedGCForTesting"; case BlinkGC::GCReason::kMemoryPressureGC: return "MemoryPressureGC"; - case BlinkGC::GCReason::kPageNavigationGC: - return "PageNavigationGC"; case BlinkGC::GCReason::kThreadTerminationGC: return "ThreadTerminationGC"; case BlinkGC::GCReason::kIncrementalV8FollowupGC: return "IncrementalV8FollowupGC"; case BlinkGC::GCReason::kUnifiedHeapGC: return "UnifiedHeapGC"; + case BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC: + return "UnifiedHeapForMemoryReductionGC"; } IMMEDIATE_CRASH(); }
diff --git a/third_party/blink/renderer/platform/heap/blink_gc.h b/third_party/blink/renderer/platform/heap/blink_gc.h index 0a6927d1a..abdcc2c6 100644 --- a/third_party/blink/renderer/platform/heap/blink_gc.h +++ b/third_party/blink/renderer/platform/heap/blink_gc.h
@@ -94,13 +94,14 @@ kConservativeGC = 2, kForcedGCForTesting = 3, kMemoryPressureGC = 4, - kPageNavigationGC = 5, + // kPageNavigationGC = 5, kThreadTerminationGC = 6, // kTesting = 7, // kIncrementalIdleGC = 8, kIncrementalV8FollowupGC = 9, kUnifiedHeapGC = 10, - kMaxValue = kUnifiedHeapGC, + kUnifiedHeapForMemoryReductionGC = 11, + kMaxValue = kUnifiedHeapForMemoryReductionGC, }; enum ArenaIndices {
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc index 03b8e19e..edeba918 100644 --- a/third_party/blink/renderer/platform/heap/heap_compact.cc +++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -346,11 +346,13 @@ return true; } - // TODO(mlippautz): Only enable compaction when doing garbage collections that - // should aggressively reduce memory footprint. - return gc_count_since_last_compaction_ > - kGCCountSinceLastCompactionThreshold && - free_list_size_ > kFreeListSizeThreshold; + // Only enable compaction when in a memory reduction garbage collection as it + // may significantly increase the final garbage collection pause. + if (reason == BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC) { + return free_list_size_ > kFreeListSizeThreshold; + } + + return false; } void HeapCompact::Initialize(ThreadState* state) {
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.h b/third_party/blink/renderer/platform/heap/heap_compact.h index 5fe668b0..d1fee85 100644 --- a/third_party/blink/renderer/platform/heap/heap_compact.h +++ b/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -118,9 +118,6 @@ private: class MovableObjectFixups; - // Number of GCs that must have passed since last compaction GC. - static const int kGCCountSinceLastCompactionThreshold = 10; - // Freelist size threshold that must be exceeded before compaction // should be considered. static const size_t kFreeListSizeThreshold = 512 * 1024;
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc index 147de415..81a1def 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.cc +++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -415,16 +415,6 @@ 32 * 1024 * 1024, 1.5); } -bool ThreadState::ShouldSchedulePageNavigationGC( - float estimated_removal_ratio) { - // If estimatedRemovalRatio is low we should let IdleGC handle this. - if (estimated_removal_ratio < 0.01) - return false; - return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold, - 32 * 1024 * 1024, - 1.5 * (1 - estimated_removal_ratio)); -} - bool ThreadState::ShouldForceConservativeGC() { // TODO(haraken): 400% is too large. Lower the heap growing factor. return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold, @@ -487,44 +477,6 @@ CompleteSweep(); } -void ThreadState::SchedulePageNavigationGCIfNeeded( - float estimated_removal_ratio) { - VLOG(2) << "[state:" << this << "] SchedulePageNavigationGCIfNeeded: " - << "estimatedRemovalRatio=" << std::setprecision(2) - << estimated_removal_ratio; - DCHECK(CheckThread()); - - if (IsGCForbidden()) - return; - - // Finish on-going lazy sweeping. - // TODO(haraken): It might not make sense to force completeSweep() for all - // page navigations. - CompleteSweep(); - DCHECK(!IsSweepingInProgress()); - DCHECK(!SweepForbidden()); - - if (ShouldForceMemoryPressureGC()) { - VLOG(2) << "[state:" << this << "] " - << "SchedulePageNavigationGCIfNeeded: Scheduled memory pressure GC"; - CollectGarbage(BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kLazySweeping, - BlinkGC::GCReason::kMemoryPressureGC); - return; - } - if (ShouldSchedulePageNavigationGC(estimated_removal_ratio)) { - VLOG(2) << "[state:" << this << "] " - << "SchedulePageNavigationGCIfNeeded: Scheduled page navigation GC"; - SchedulePageNavigationGC(); - } -} - -void ThreadState::SchedulePageNavigationGC() { - DCHECK(CheckThread()); - DCHECK(!IsSweepingInProgress()); - SetGCState(kPageNavigationGCScheduled); -} - void ThreadState::ScheduleForcedGCForTesting() { DCHECK(CheckThread()); CompleteSweep(); @@ -677,7 +629,6 @@ UNEXPECTED_GCSTATE(kIncrementalMarkingStepPaused); UNEXPECTED_GCSTATE(kIncrementalMarkingStepScheduled); UNEXPECTED_GCSTATE(kIncrementalMarkingFinalizeScheduled); - UNEXPECTED_GCSTATE(kPageNavigationGCScheduled); UNEXPECTED_GCSTATE(kIncrementalGCScheduled); } } @@ -697,7 +648,6 @@ VERIFY_STATE_TRANSITION( gc_state_ == kNoGCScheduled || gc_state_ == kPreciseGCScheduled || gc_state_ == kForcedGCForTestingScheduled || - gc_state_ == kPageNavigationGCScheduled || gc_state_ == kIncrementalMarkingStepPaused || gc_state_ == kIncrementalMarkingStepScheduled || gc_state_ == kIncrementalMarkingFinalizeScheduled || @@ -714,7 +664,6 @@ VERIFY_STATE_TRANSITION(gc_state_ == kIncrementalMarkingStepScheduled); break; case kForcedGCForTestingScheduled: - case kPageNavigationGCScheduled: case kPreciseGCScheduled: DCHECK(CheckThread()); DCHECK(!IsSweepingInProgress()); @@ -725,7 +674,6 @@ kIncrementalMarkingFinalizeScheduled || gc_state_ == kPreciseGCScheduled || gc_state_ == kForcedGCForTestingScheduled || - gc_state_ == kPageNavigationGCScheduled || gc_state_ == kIncrementalGCScheduled); break; case kIncrementalGCScheduled: @@ -786,11 +734,6 @@ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, BlinkGC::kLazySweeping, BlinkGC::GCReason::kPreciseGC); break; - case kPageNavigationGCScheduled: - CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kEagerSweeping, - BlinkGC::GCReason::kPageNavigationGC); - break; case kIncrementalMarkingStepScheduled: IncrementalMarkingStep(stack_state); break; @@ -1121,10 +1064,10 @@ COUNT_BY_GC_REASON(ConservativeGC) COUNT_BY_GC_REASON(ForcedGCForTesting) COUNT_BY_GC_REASON(MemoryPressureGC) - COUNT_BY_GC_REASON(PageNavigationGC) COUNT_BY_GC_REASON(ThreadTerminationGC) COUNT_BY_GC_REASON(IncrementalV8FollowupGC) COUNT_BY_GC_REASON(UnifiedHeapGC) + COUNT_BY_GC_REASON(UnifiedHeapForMemoryReductionGC) #undef COUNT_BY_GC_REASON } @@ -1460,10 +1403,10 @@ COUNT_BY_GC_REASON(ConservativeGC) COUNT_BY_GC_REASON(ForcedGCForTesting) COUNT_BY_GC_REASON(MemoryPressureGC) - COUNT_BY_GC_REASON(PageNavigationGC) COUNT_BY_GC_REASON(ThreadTerminationGC) COUNT_BY_GC_REASON(IncrementalV8FollowupGC) COUNT_BY_GC_REASON(UnifiedHeapGC) + COUNT_BY_GC_REASON(UnifiedHeapForMemoryReductionGC) } #undef COUNT_BY_GC_REASON
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h index ffdb605..47e44f2 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.h +++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -151,7 +151,6 @@ kIncrementalMarkingFinalizeScheduled, kPreciseGCScheduled, kForcedGCForTestingScheduled, - kPageNavigationGCScheduled, kIncrementalGCScheduled, }; @@ -224,8 +223,6 @@ void SchedulePreciseGC(); void ScheduleIncrementalGC(BlinkGC::GCReason); void ScheduleV8FollowupGCIfNeeded(BlinkGC::V8GCType); - void SchedulePageNavigationGCIfNeeded(float estimated_removal_ratio); - void SchedulePageNavigationGC(); void ScheduleForcedGCForTesting(); void ScheduleGCIfNeeded(); void PostIdleGCTask(); @@ -237,7 +234,9 @@ bool IsSweepingInProgress() const { return gc_phase_ == GCPhase::kSweeping; } bool IsUnifiedGCMarkingInProgress() const { return IsMarkingInProgress() && - (current_gc_data_.reason == BlinkGC::GCReason::kUnifiedHeapGC); + (current_gc_data_.reason == BlinkGC::GCReason::kUnifiedHeapGC || + current_gc_data_.reason == + BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC); } void EnableCompactionForNextGCForTesting(); @@ -491,11 +490,6 @@ // V8 minor or major GC is likely to drop a lot of references to objects // on Oilpan's heap. We give a chance to schedule a GC. bool ShouldScheduleV8FollowupGC(); - // Page navigation is likely to drop a lot of references to objects - // on Oilpan's heap. We give a chance to schedule a GC. - // estimatedRemovalRatio is the estimated ratio of objects that will be no - // longer necessary due to the navigation. - bool ShouldSchedulePageNavigationGC(float estimated_removal_ratio); // Internal helpers to handle memory pressure conditions.
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc index acb4999..b3c9fcff 100644 --- a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc +++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
@@ -28,7 +28,8 @@ UnifiedHeapController::UnifiedHeapController(ThreadState* thread_state) : thread_state_(thread_state) {} -void UnifiedHeapController::TracePrologue() { +void UnifiedHeapController::TracePrologue( + v8::EmbedderHeapTracer::TraceFlags v8_flags) { VLOG(2) << "UnifiedHeapController::TracePrologue"; ThreadHeapStatsCollector::BlinkGCInV8Scope nested_scope( thread_state_->Heap().stats_collector()); @@ -40,8 +41,11 @@ // Reset any previously scheduled garbage collections. thread_state_->SetGCState(ThreadState::kNoGCScheduled); - // Start incremental marking with unified tracing. - thread_state_->IncrementalMarkingStart(BlinkGC::GCReason::kUnifiedHeapGC); + BlinkGC::GCReason gc_reason = + (v8_flags & v8::EmbedderHeapTracer::TraceFlags::kReduceMemory) + ? BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC + : BlinkGC::GCReason::kUnifiedHeapGC; + thread_state_->IncrementalMarkingStart(gc_reason); is_tracing_done_ = false; } @@ -59,9 +63,9 @@ ThreadHeapStatsCollector::Scope mark_prologue_scope( thread_state_->Heap().stats_collector(), ThreadHeapStatsCollector::kUnifiedMarkingAtomicPrologue); - thread_state_->AtomicPauseMarkPrologue(ToBlinkGCStackState(stack_state), - BlinkGC::kIncrementalMarking, - BlinkGC::GCReason::kUnifiedHeapGC); + thread_state_->AtomicPauseMarkPrologue( + ToBlinkGCStackState(stack_state), BlinkGC::kIncrementalMarking, + thread_state_->current_gc_data_.reason); } }
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 e22350968..614fe28 100644 --- a/third_party/blink/renderer/platform/heap/unified_heap_controller.h +++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.h
@@ -36,7 +36,7 @@ explicit UnifiedHeapController(ThreadState*); // v8::EmbedderHeapTracer implementation. - void TracePrologue() final; + void TracePrologue(v8::EmbedderHeapTracer::TraceFlags) final; void TraceEpilogue() final; void EnterFinalPause(EmbedderStackState) final; void RegisterV8References(const std::vector<std::pair<void*, void*>>&) final;
diff --git a/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc b/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc index 68e4d050..f240867b 100644 --- a/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc +++ b/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc
@@ -11,7 +11,6 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/network/http_names.h" #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { @@ -121,12 +120,8 @@ counter = kTextXmlFeatures[same_origin][is_worker_global_scope]; } - // Depending on RuntimeEnabledFeatures, we'll allow, allow-but-warn, or block - // these types when we're in a worker. - bool allow = mime_type_check_mode == MimeTypeCheck::kLax || - !RuntimeEnabledFeatures::WorkerNosniffBlockEnabled(); - warn = allow && mime_type_check_mode == MimeTypeCheck::kStrict && - RuntimeEnabledFeatures::WorkerNosniffWarnEnabled(); + bool allow = mime_type_check_mode == MimeTypeCheck::kLax; + warn = false; return allow; }
diff --git a/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc b/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc index c923f52..725f0be 100644 --- a/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc +++ b/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc
@@ -12,7 +12,6 @@ #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h" #include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h" #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" namespace blink { @@ -115,31 +114,6 @@ ResourceResponse response(url); response.SetHttpHeaderField("Content-Type", testcase.mimetype); - // Nosniff 'legacy' setting: Both worker + non-worker obey the 'allowed' - // setting. Warnings for any blocked script. - RuntimeEnabledFeatures::SetWorkerNosniffBlockEnabled(false); - RuntimeEnabledFeatures::SetWorkerNosniffWarnEnabled(false); - EXPECT_CALL(*context, CountUsage(_)).Times(::testing::AnyNumber()); - if (!testcase.allowed) - EXPECT_CALL(*logger, AddConsoleMessage(_, _, _)); - EXPECT_EQ(testcase.allowed, - AllowedByNosniff::MimeTypeAsScript(*context, logger, response, - MimeTypeCheck::kLax, false)); - ::testing::Mock::VerifyAndClear(context); - - EXPECT_CALL(*context, CountUsage(_)).Times(::testing::AnyNumber()); - if (!testcase.allowed) - EXPECT_CALL(*logger, AddConsoleMessage(_, _, _)); - EXPECT_EQ(testcase.allowed, - AllowedByNosniff::MimeTypeAsScript( - *context, logger, response, MimeTypeCheck::kStrict, false)); - ::testing::Mock::VerifyAndClear(context); - - // Nosniff worker blocked: Workers follow the 'strict_allow' setting. - // Warnings for any blocked scripts. - RuntimeEnabledFeatures::SetWorkerNosniffBlockEnabled(true); - RuntimeEnabledFeatures::SetWorkerNosniffWarnEnabled(false); - EXPECT_CALL(*context, CountUsage(_)).Times(::testing::AnyNumber()); if (!testcase.allowed) EXPECT_CALL(*logger, AddConsoleMessage(_, _, _)); @@ -155,27 +129,6 @@ AllowedByNosniff::MimeTypeAsScript( *context, logger, response, MimeTypeCheck::kStrict, false)); ::testing::Mock::VerifyAndClear(context); - - // Nosniff 'legacy', but with warnings. The allowed setting follows the - // 'allowed' setting, but the warnings follow the 'strict' setting. - RuntimeEnabledFeatures::SetWorkerNosniffBlockEnabled(false); - RuntimeEnabledFeatures::SetWorkerNosniffWarnEnabled(true); - - EXPECT_CALL(*context, CountUsage(_)).Times(::testing::AnyNumber()); - if (!testcase.allowed) - EXPECT_CALL(*logger, AddConsoleMessage(_, _, _)); - EXPECT_EQ(testcase.allowed, - AllowedByNosniff::MimeTypeAsScript(*context, logger, response, - MimeTypeCheck::kLax, false)); - ::testing::Mock::VerifyAndClear(context); - - EXPECT_CALL(*context, CountUsage(_)).Times(::testing::AnyNumber()); - if (!testcase.strict_allowed) - EXPECT_CALL(*logger, AddConsoleMessage(_, _, _)); - EXPECT_EQ(testcase.allowed, - AllowedByNosniff::MimeTypeAsScript( - *context, logger, response, MimeTypeCheck::kStrict, false)); - ::testing::Mock::VerifyAndClear(context); } }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index c19dd878..e275dad0 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -191,6 +191,10 @@ status: "test", }, { + name: "BlockingFocusWithoutUserActivation", + status: "experimental", + }, + { name: "BlockMetaSetCookie", status: "stable" }, @@ -1621,15 +1625,6 @@ // depends_on: ["WebXR"], // TODO(https://crbug.com/954679): uncomment once bug is fixed }, { - name: "WorkerNosniffBlock", - status: "stable", - }, - { - name: "WorkerNosniffWarn", - status: "stable", - implied_by: ["WorkerNosniffBlock"], - }, - { name: "WorkerTaskQueue", status: "experimental" },
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService index 190408c..4dfe621 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService +++ b/third_party/blink/web_tests/FlagExpectations/disable-features=NetworkService
@@ -10,7 +10,6 @@ crbug.com/899303 http/tests/inspector-protocol/fetch/fetch-basic.js [ Timeout ] crbug.com/899303 http/tests/inspector-protocol/fetch/fetch-renderer.js [ Timeout ] crbug.com/917284 external/wpt/service-workers/service-worker/claim-fetch-with-appcache.https.html [ Failure ] -crbug.com/917284 virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/claim-fetch-with-appcache.https.html [ Failure ] crbug.com/933880 external/wpt/FileAPI/url/cross-global-revoke.sub.html [ Failure ] crbug.com/933880 external/wpt/FileAPI/url/url-with-fetch.any.html [ Failure ] crbug.com/933880 external/wpt/FileAPI/url/url-with-xhr.any.html [ Failure ]
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 fd47a57..51aee5f 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -406,6 +406,8 @@ crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-border-radius.html [ Failure ] +crbug.com/923429 css3/filters/backdrop-filter-clip-rect-zoom.html [ Failure ] +crbug.com/923429 css3/filters/backdrop-filter-plus-filter.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-rendering.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-rendering-no-background.html [ Failure ] crbug.com/923429 css3/filters/backdrop-filter-transform.html [ Failure ] @@ -414,6 +416,9 @@ crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-basic.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ] crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation.html [ Failure ] +crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ] +crbug.com/923429 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ] + crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ] crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index e617b8c..93d9c5d0 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1617,16 +1617,7 @@ crbug.com/497522 css3/filters/backdrop-filter-boundary.html [ Failure ] crbug.com/497522 css3/filters/backdrop-filter-bleeding.html [ Failure ] crbug.com/497522 css3/filters/backdrop-filter-svg.html [ Failure ] -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom.html [ Failure ] -#crbug.com/497522 css3/filters/backdrop-filter-plus-filter.html [ Failure ] -# This fails, but only in CAP mode: -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ] crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-paint-order.html [ Failure ] -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ] -crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html [ Pass Failure ] -# Flaky (timeout) tests: -crbug.com/912748 external/wpt/css/filter-effects/backdrop-filter-plus-filter.html [ Skip ] -crbug.com/913114 css3/filters/backdrop-filter-plus-filter.html [ Skip ] # ====== Backdrop-filter related tests END ====== @@ -2144,6 +2135,7 @@ # These are the failing tests because Chrome hasn't implemented according to the spec. crbug.com/645988 external/wpt/uievents/order-of-events/focus-events/focus-manual.html [ Failure ] +# TODO(https://crbug.com/960132): Remove these when WebVR is removed. crbug.com/807152 vr/VRDisplay_rAF_fires_with_window_rAF.html [ Pass Failure ] crbug.com/813697 vr/getFrameData_oneframeupdate.html [ Pass Failure ] @@ -2906,14 +2898,6 @@ crbug.com/626703 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Timeout ] crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Timeout ] crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/clients-matchall-exact-controller.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/claim-with-redirect.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/close.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/ServiceWorkerGlobalScope/extendable-message-event.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/navigation-redirect.https.html?client [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/multiple-update.https.html [ Timeout ] -crbug.com/626703 [ Retina ] virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/update.https.html [ Timeout ] crbug.com/626703 external/wpt/reporting/disconnect.html [ Timeout ] crbug.com/626703 external/wpt/reporting/generateTestReport.html [ Timeout ] crbug.com/626703 external/wpt/reporting/bufferSize.html [ Timeout ] @@ -4644,7 +4628,6 @@ crbug.com/754986 media/video-transformed.html [ Pass Failure ] # Sheriff 2018-03-29 -crbug.com/827088 bluetooth/requestDevice/chooser/new-scan-device-added.html [ Pass Crash ] crbug.com/827209 [ Win ] fast/events/middleClickAutoscroll-latching.html [ Pass Timeout Failure ] crbug.com/827209 [ Linux ] fast/events/middleClickAutoscroll-latching.html [ Pass Timeout Failure ] crbug.com/827209 [ Win ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Pass Timeout Failure ] @@ -5284,8 +5267,9 @@ # Sheriff 2019-01-24 crbug.com/924954 http/tests/images/feature-policy-image-policies-with-border-radius.html [ Pass Failure ] -# WebXR feature policy feature name in Chrome doesn't match spec. -crbug.com/924670 external/wpt/webvr/webvr-supported-by-feature-policy.html [ Failure ] +# Chrome does not implement the feature policy name currently in the spec, +# which is still in flux. +crbug.com/924670 external/wpt/webxr/webxr-supported-by-feature-policy.html [ Failure ] crbug.com/961015 [ Mac ] external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https.html [ Timeout ] crbug.com/961015 [ Mac ] external/wpt/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html [ Timeout ] @@ -5563,3 +5547,11 @@ # Sheriff 2019-05-11 crbug.com/962139 [ Linux Debug ] external/wpt/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.html [ Pass Crash ] crbug.com/962139 [ Linux Debug ] external/wpt/html/infrastructure/urls/dynamic-changes-to-base-urls/historical.sub.xhtml [ Pass Crash ] + +# Sheriff 2019-05-13 +crbug.com/865432 [ Linux ] external/wpt/workers/modules/dedicated-worker-import-blob-url.any.worker.html [ Timeout Pass ] +crbug.com/867532 [ Linux ] external/wpt/workers/modules/dedicated-worker-import-data-url.any.worker.html [ Timeout Pass ] +crbug.com/867532 [ Linux ] external/wpt/workers/modules/dedicated-worker-import.any.worker.html [ Timeout Pass ] +crbug.com/865432 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-blob-url.any.worker.html [ Timeout Pass ] +crbug.com/867532 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import-data-url.any.worker.html [ Timeout Pass ] +crbug.com/867532 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/modules/dedicated-worker-import.any.worker.html [ Timeout Pass ]
diff --git a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html index 105c2b2..1b699ea9 100644 --- a/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html +++ b/third_party/blink/web_tests/bluetooth/requestDevice/chooser/new-scan-device-added.html
@@ -6,23 +6,44 @@ <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script> <script> 'use strict'; -bluetooth_test(() => { - setBluetoothManualChooser(true); +const test_desc = 'The chooser should display newly detected devices.'; - let requestDevicePromise = setBluetoothFakeAdapter('DeviceEventAdapter') - .then( - () => requestDeviceWithTrustedClick( - {filters: [{services: ['glucose']}]})); - return getBluetoothManualChooserEvents(4).then(events => { - assert_equals(events[0], 'chooser-opened(file://)'); - assert_equals(events[1], 'discovering'); - let idsByName = new AddDeviceEventSet(); - idsByName.assert_add_device_event(events[2]); - assert_true(idsByName.has('New Glucose Device')); - assert_equals(events[3], 'discovery-idle'); - sendBluetoothManualChooserEvent( - 'selected', idsByName.get('New Glucose Device')); - return requestDevicePromise; +bluetooth_test(async () => { + let fake_central = + await navigator.bluetooth.test.simulateCentral({state: 'powered-on'}); + let fake_chooser = await navigator.bluetooth.test.getManualChooser(); + + // 1. Open the chooser. + let requestDevicePromise = requestDeviceWithTrustedClick({ + filters: [{services: ['health_thermometer']}] }); -}); + + let events = await fake_chooser.waitForEvents(2); + assert_equals(events.length, 2); + assert_equals(events[0].type, 'chooser-opened'); + assert_equals(events[0].origin.scheme, 'file'); + assert_equals(events[1].type, 'discovering'); + + // 2. Send the advertisement packet to central. + let fake_peripheral = + await fake_central.simulateAdvertisementReceived( + health_thermometer_ad_packet); + + events = await fake_chooser.waitForEvents(1); + assert_equals(events.length, 1); + assert_equals(events[0].type, 'add-or-update-device'); + assert_equals(events[0].peripheral_address, + health_thermometer_ad_packet.scanRecord.deviceAddress); + + // 3. Select the device on the chooser. + await fake_chooser.selectPeripheral(fake_peripheral); + + events = await fake_chooser.waitForEvents(1); + assert_equals(events.length, 1); + assert_equals(events[0].type, 'chooser-closed'); + + // 4. Check that the device and advertisement packet have the same name. + let device = await requestDevicePromise; + assert_equals(device.name, health_thermometer_ad_packet.scanRecord.name); +}, test_desc); </script>
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..da57311 --- /dev/null +++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html similarity index 63% rename from third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom.html rename to third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html index 36f0b1b..1058c8f8 100644 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom.html +++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html
@@ -6,18 +6,24 @@ <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty"> <link rel="match" href="backdrop-filter-clip-rect-zoom-ref.html"> -<div> - <p>Expected: A green box, color-inverted inside the short, wide box with a<br> - blue border and rounded corners, and not color-inverted anywhere else. In<br> - particular, there should be no color inversion inside the tall, narrow box,<br> - or anywhere outside that.</p> -</div> +<!-- See [1] for an implementation of this test in WPT format. It requires the + WPT Fuzzy Matching [2] feature to be implemented, due to AA on the border + radii. + + [1] https://cs.chromium.org/chromium/src/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html?rcl=04f3fcde0d60b908e7c452b4956b693144cdfdd5&l=1 + [2] https://github.com/web-platform-tests/wpt/blob/1f570a686843ca10f151a79956ee16110f4a4d42/docs/_writing-tests/reftests.md#fuzzy-matching +--> + <div class="box"></div> <div class="navbar"> <div class="menu"></div> <div class="menu2"></div> </div> <style> +html::-webkit-scrollbar { + width: 0 !important; + height: 0 !important; +} div { position: absolute; }
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.html deleted file mode 100644 index 91129fde..0000000 --- a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.html +++ /dev/null
@@ -1,36 +0,0 @@ -<!DOCTYPE html> -<div class="container"> - <div class="orangebox"></div> - <div class="bluebox blur-bd" style="background:transparent;"></div> - <div class="bluebox blur"></div> -</div> - -<style> -div { - width: 100px; - height: 100px; - position :absolute; -} -.container { - width:200px; - height:200px; - position:absolute; -} -.blur { - -webkit-filter: blur(3px); - filter: blur(3px); -} -.blur-bd { - backdrop-filter: blur(15px); -} -.orangebox { - left: 10px; - top: 50px; - background: orange; -} -.bluebox { - left: 60px; - top: 110px; - background: #0000ff33; -} -</style>
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.png b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.png new file mode 100644 index 0000000..50c0a55f --- /dev/null +++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-filter-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json index ea18cd64a..fa373d0 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -240229,6 +240229,12 @@ {} ] ], + "css/css-writing-modes/bidi-inline-fragment-crash.html": [ + [ + "css/css-writing-modes/bidi-inline-fragment-crash.html", + {} + ] + ], "css/css-writing-modes/inheritance.html": [ [ "css/css-writing-modes/inheritance.html", @@ -302481,6 +302487,12 @@ {} ] ], + "resource-timing/resource_nested_dedicated_worker.worker.js": [ + [ + "resource-timing/resource_nested_dedicated_worker.worker.html", + {} + ] + ], "resource-timing/resource_reparenting.html": [ [ "resource-timing/resource_reparenting.html", @@ -416474,6 +416486,10 @@ "a34235016b816afa680179c866f6aa7789dd0bd7", "reftest" ], + "css/css-writing-modes/bidi-inline-fragment-crash.html": [ + "5d3796566b9c68abdc0defb016c1675d1ea8c9d0", + "testharness" + ], "css/css-writing-modes/bidi-isolate-001.html": [ "335791b845b49f2b52db90f1763a4f8f4f8f4b01", "reftest" @@ -485730,6 +485746,10 @@ "9912da7c3400138d73245acc372463ca174e6f58", "testharness" ], + "resource-timing/resource_nested_dedicated_worker.worker.js": [ + "fc793075bbade1b0891ea8ecf50137051d84b820", + "testharness" + ], "resource-timing/resource_reparenting.html": [ "7d4947fa7703d13a5adb465ff5eebbb4456cace9", "testharness" @@ -486007,7 +486027,7 @@ "support" ], "resource-timing/resources/worker_with_images.js": [ - "2d7688fcf9941a23bb75ef20abc93211fc224ca5", + "1fa4893201ee006431a58d7a96a50b82e6ab6f30", "support" ], "resource-timing/single-entry-per-resource.html": [ @@ -498743,7 +498763,7 @@ "support" ], "tools/wptrunner/requirements_firefox.txt": [ - "668cea391543074722e19ad802ea189df84890c0", + "9d7520dd01f29936cc7e232c2e08b1d5d6f6467a", "support" ], "tools/wptrunner/requirements_ie.txt": [ @@ -498803,7 +498823,7 @@ "support" ], "tools/wptrunner/wptrunner/browsers/edge.py": [ - "21d41d1be2f20d28d5789bb0a08cf50884fe7e1f", + "18a1ed781189cca23df2a4e1b5b5dca830bd0ea4", "support" ], "tools/wptrunner/wptrunner/browsers/edge_webdriver.py": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-change-auto-repeat-tracks.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-change-auto-repeat-tracks.html new file mode 100644 index 0000000..33fcb248 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-change-auto-repeat-tracks.html
@@ -0,0 +1,178 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Grid Layout Test: Support 'repeat()' notation for 'grid-template-columns' and 'grid-template-rows' properties</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="http://www.w3.org/TR/css-grid-1/#repeat-notation" title="5.1.2 Repeating Rows and Columns: the 'repeat()' notation"> +<meta name="assert" content="This test checks that grid-template-{rows|columns} with auto-repeat tracks recomputes the positions of automatically placed grid items."> + +<link href="support/grid.css" rel="stylesheet"> +<style> +.grid { + grid-auto-rows: 25px; + grid-auto-columns: 25px; + margin-bottom: 10px; +} +.fixedWidth { + width: 50px; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fill, 25px); +} +.fixedHeight { + width: 100px; + height: 50px; + grid-auto-flow: column; + grid-template-rows: repeat(auto-fill, 25px); +} +#i1-1, #i2-1 { + grid-row: auto; + grid-column: 1; + background-color: orange; +} +#i1-2, #i2-2 { + grid-row: 1; + grid-column: auto; + background-color: green; +} +#i1-3, #i2-3 { + grid-row: auto; + grid-column: auto; + background-color: blue; +} +</style> + +<div id="log"></div> + +<div style="position: relative"> + <div id="grid1" class="grid fixedWidth"> + <div id="i1-1"></div> + <div id="i1-2"></div> + <div id="i1-3"></div> + </div> +</div> +<div style="position: relative"> + <div id="grid2" class="grid fixedHeight"> + <div id="i2-1"></div> + <div id="i2-2"></div> + <div id="i2-3"></div> + </div> +</div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<script> +function setGridTemplate(id, gridTemplateRows, gridTemplateColumns) { + let gridElement = document.getElementById(id); + gridElement.style.gridTemplateRows = gridTemplateRows; + gridElement.style.gridTemplateColumns = gridTemplateColumns; +} + +function setGridSize(id, width, height) { + let gridElement = document.getElementById(id); + gridElement.style.width = width; + gridElement.style.height = height; +} + +function setGridItemPlacement(id, gridRow, gridColumn) { + let gridItem = document.getElementById(id); + gridItem.style.gridRow = gridRow; + gridItem.style.gridColumn = gridColumn; +} + +function testGridDefinitions(...gridItemDataList) { + for (let gridItemData of gridItemDataList) { + let gridItem = document.getElementById(gridItemData.id); + gridItem.setAttribute("data-expected-width", gridItemData.width); + gridItem.setAttribute("data-expected-height", gridItemData.height); + gridItem.setAttribute("data-offset-x", gridItemData.x); + gridItem.setAttribute("data-offset-y", gridItemData.y); + } + checkLayout(".grid", false); +} + +// Test changing the number of auto-repeat tracks. +setGridTemplate('grid1', 'none', 'repeat(auto-fill, 25px)'); +setGridTemplate('grid2', 'repeat(auto-fill, 25px)', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '25', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '25', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '25', height: '25', x: '25', y: '25' }, + { id: 'i2-1', width: '25', height: '25', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '25', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '25', x: '25', y: '25' }); + +setGridTemplate('grid1', 'none', 'none'); +setGridTemplate('grid2', 'none', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '25', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '25', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '25', height: '25', x: '0', y: '50' }, + { id: 'i2-1', width: '25', height: '25', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '25', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '25', x: '50', y: '0' }); + +setGridTemplate('grid1', 'none', '5px repeat(auto-fill, 20px)'); +setGridTemplate('grid2', 'repeat(auto-fill, 20px) 3px', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '5', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '5', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '20', height: '25', x: '5', y: '25' }, + { id: 'i2-1', width: '25', height: '20', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '20', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '20', x: '25', y: '20' }); + +setGridTemplate('grid1', 'none', '5px repeat(auto-fill, 22px)'); +setGridTemplate('grid2', 'repeat(auto-fill, 18px) 3px', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '5', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '5', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '22', height: '25', x: '5', y: '25' }, + { id: 'i2-1', width: '25', height: '18', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '18', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '18', x: '25', y: '18' }); + +setGridTemplate('grid1', 'none', 'repeat(auto-fill, 45px)'); +setGridTemplate('grid2', 'repeat(auto-fill, 45px)', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '45', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '45', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '45', height: '25', x: '0', y: '50' }, + { id: 'i2-1', width: '25', height: '45', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '45', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '45', x: '50', y: '0' }); + +// Test changing the size of the grid. +setGridSize('grid1', '100px', 'auto'); +setGridSize('grid2', '100px', '100px'); +testGridDefinitions( + { id: 'i1-1', width: '45', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '45', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '45', height: '25', x: '45', y: '25' }, + { id: 'i2-1', width: '25', height: '45', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '45', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '45', x: '25', y: '45' }); + +// Move the third item so that there is an empty track between it and the others. +setGridItemPlacement('i1-3', 'auto', '3'); +setGridItemPlacement('i2-3', '3', 'auto'); +testGridDefinitions( + { id: 'i1-1', width: '45', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '45', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '25', height: '25', x: '90', y: '25' }, + { id: 'i2-1', width: '25', height: '45', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '45', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '25', x: '25', y: '90' }); + +// Set the same templates, but using auto-fit instead of auto-fill. The empty track should collapse. +setGridTemplate('grid1', 'none', 'repeat(auto-fit, 45px)'); +setGridTemplate('grid2', 'repeat(auto-fit, 45px)', 'none'); +testGridDefinitions( + { id: 'i1-1', width: '45', height: '25', x: '0', y: '25' }, + { id: 'i1-2', width: '45', height: '25', x: '0', y: '0' }, + { id: 'i1-3', width: '25', height: '25', x: '45', y: '25' }, + { id: 'i2-1', width: '25', height: '45', x: '0', y: '0' }, + { id: 'i2-2', width: '25', height: '45', x: '25', y: '0' }, + { id: 'i2-3', width: '25', height: '25', x: '25', y: '45' }); + +done(); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html deleted file mode 100644 index 5e43c7d..0000000 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-zoom-ref.html +++ /dev/null
@@ -1,58 +0,0 @@ -<!DOCTYPE html> -<html style="zoom:5"> -<meta charset="utf-8"> -<title>backdrop-filter: Clip the filter at border box of element</title> -<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> - - - -<div> - <p>Expected: A green box, color-inverted inside the short, wide box with a<br> - blue border and rounded corners, and not color-inverted anywhere else. In<br> - particular, there should be no color inversion inside the tall, narrow box,<br> - or anywhere outside that.</p> -</div> -<div class="box"></div> -<div class="navbar"></div> -<div class="menu"></div> -<div class="menu2"></div> - -<style> -div { - position: absolute; -} -.box { - width: 200px; - height: 200px; - top: 100px; - left: 100px; - background: green; -} -.navbar { - width: 300px; - height: 50px; - top: 150px; - left: 50px; - border: 2px solid blue; - backdrop-filter: invert(1); - border-radius: 10px 20px 30px 40px; -} -.menu { - width: 100px; - height: 150px; - top: 202px; - left: 147px; - border: 2px solid red; -} -.menu2 { - width: 100px; - height: 30px; - top: 118px; - left: 147px; - border: 2px solid red; -} -</style> - -<script> - window.scrollTo(0,700); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html index a3c3fa25..f7835a1 100644 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html +++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html
@@ -1,13 +1,13 @@ <!DOCTYPE html> <meta charset="utf-8"> -<title>backdrop-filter: Should not filter outside parent stacking context.</title> +<title>backdrop-filter: fixed position should not cause a backdrop root.</title> <link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty"> -<link rel="match" href="backdrop-filter-isolation-ref.html"> +<link rel="match" href="backdrop-filter-non-isolation-ref.html"> <div> <p>Expected: Two green boxes overlapped by a yellow box. The overlapped region<br> - of the right-hand box ONLY should be inverted (pink).</p> + of BOTH green boxes should be inverted (pink).</p> </div> <div class="box outside"> <div class="box stacking-context">
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html index 88dd91a..008f6f8 100644 --- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html +++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html
@@ -1,13 +1,13 @@ <!DOCTYPE html> <meta charset="utf-8"> -<title>backdrop-filter: Should not filter outside parent stacking context.</title> +<title>backdrop-filter: isolation isolate should not cause a backdrop root.</title> <link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty"> -<link rel="match" href="backdrop-filter-isolation-ref.html"> +<link rel="match" href="backdrop-filter-non-isolation-ref.html"> <div> <p>Expected: Two green boxes overlapped by a yellow box. The overlapped region<br> - of the right-hand box ONLY should be inverted (pink).</p> + of BOTH green boxes should be inverted (pink).</p> </div> <div class="box outside"> <div class="box stacking-context">
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html new file mode 100644 index 0000000..0453d7f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>backdrop-filter: Isolation</title> +<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org"> + + + +<div> + <p>Expected: Two green boxes overlapped by a yellow box. The overlapped region<br> + of BOTH green boxes should be inverted (pink).</p> +</div> +<div class="box green"></div> +<div class="box green" style="left: 130px;"></div> +<div class="box yellow" style="width:160px;height:160px;top:140px;left:40px;"></div> +<div class="box orange" style="width:70px;height:70px;top:140px;left:40px;"></div> +<div class="box orange" style="width:70px;height:70px;top:140px;left:130px;"></div> + +<style> +.box { + position: absolute; + width: 100px; + height: 100px; + top:110px; + left:10px; +} +.green { background: green; } +.yellow { background: #880; } +.orange { background: #ffc377; } + +</style> +
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/focus-without-user-activation-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/focus-without-user-activation-tentative.sub.html new file mode 100644 index 0000000..ad90864 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/focus-without-user-activation-tentative.sub.html
@@ -0,0 +1,74 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/common.js"></script> +<title> 'focus-without-user-activation' Policy : Correctly block automatic focus when policy disabled +</title> +<body> + <input onblur="did_blur();" autofocus/> +<script> + "use strict" + + const url = "http://{{hosts[alt][www1]}}:{{ports[http][0]}}/feature-policy/experimental-features/resources/focus_steal.html"; + + let did_blur_ = false; + function did_blur() { + did_blur_ = true; + } + + function short_delay(test_instance) { + // Long enough to allow focus to propagate correctly. + const SHORT_DELAY = 400; + return new Promise( (r) => test_instance.step_timeout(r, SHORT_DELAY)); + } + + function reset_focus() { + did_blur_ = false; + document.querySelector("input").focus(); + } + + promise_test( async (instance) => { + const frame = createIframe(document.body, { + sandbox: "allow-scripts allow-same-origin", + allow: "focus-without-user-activation 'none'", + src: url + }); + await wait_for_load(frame); + + await short_delay(instance); + assert_false(did_blur_, "'autofocus' should not work."); + + frame.contentWindow.postMessage("focus-input", "*"); + await short_delay(instance); + assert_false(did_blur_, "'element.focus' should not work."); + + frame.contentWindow.postMessage("focus-window", "*"); + await short_delay(instance); + assert_false(did_blur_, "'window.focus' should not work."); + }, "When the policy is disabled, 'autofocus' and scripted focus do not focus " + + "the document."); + + + promise_test( async (instance) => { + const frame = createIframe(document.body, { + sandbox: "allow-scripts allow-same-origin", + allow: "focus-without-user-activation *", + src: url + }); + await wait_for_load(frame); + await short_delay(instance); + + reset_focus(); + frame.contentWindow.postMessage("focus-input", "*"); + await short_delay(instance); + assert_true(did_blur_, "'element.focus' should work."); + did_blur_ = false; + + reset_focus(); + frame.contentWindow.postMessage("focus-window", "*"); + await short_delay(instance); + assert_true(did_blur_, "'window.focus' should work."); + }, "When the policy is enabled, 'autofocus' and scripted focus do focus " + + "the document."); +</script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/focus_steal.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/focus_steal.html new file mode 100644 index 0000000..43e86881 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/focus_steal.html
@@ -0,0 +1,12 @@ +<!doctype html> +<input autofocus/> +<script> + window.addEventListener("message", (e) => { + if (e.data === "focus-window") { + window.focus(); + } + else if (e.data === "focus-input") { + document.querySelector("input").focus(); + } +}); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js index db8f5480..4c89463 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js
@@ -45,6 +45,22 @@ extended_properties: 'extended_properties', }; +// Mapping of the Mojo ChooserEventType enum to a string. +const MOJO_CHOOSER_EVENT_TYPE_MAP = (() => { + const ChooserEventType = content.mojom.ChooserEventType; + return { + [ChooserEventType.CHOOSER_OPENED]: 'chooser-opened', + [ChooserEventType.CHOOSER_CLOSED]: 'chooser-closed', + [ChooserEventType.ADAPTER_REMOVED]: 'adapter-removed', + [ChooserEventType.ADAPTER_DISABLED]: 'adapter-disabled', + [ChooserEventType.ADAPTER_ENABLED]: 'adapter-enabled', + [ChooserEventType.DISCOVERY_FAILED_TO_START]: 'discovery-failed-to-start', + [ChooserEventType.DISCOVERING]: 'discovering', + [ChooserEventType.DISCOVERY_IDLE]: 'discovery-idle', + [ChooserEventType.ADD_OR_UPDATE_DEVICE]: 'add-or-update-device', + } +})(); + function ArrayToMojoCharacteristicProperties(arr) { let struct = new bluetooth.mojom.CharacteristicProperties(); @@ -504,14 +520,51 @@ } } -// FakeChooser allows clients to simulate events that a user would trigger when -// using the Bluetooth chooser, and monitor the events that are produced. +// FakeChooser allows clients to simulate user actions on a Bluetooth chooser, +// and records the events produced by the Bluetooth chooser. class FakeChooser { constructor() { + let fakeBluetoothChooserFactoryPtr = + new content.mojom.FakeBluetoothChooserFactoryPtr(); + Mojo.bindInterface(content.mojom.FakeBluetoothChooserFactory.name, + mojo.makeRequest(fakeBluetoothChooserFactoryPtr).handle, 'process'); + this.fake_bluetooth_chooser_ptr_ = new content.mojom.FakeBluetoothChooserPtr(); - Mojo.bindInterface(content.mojom.FakeBluetoothChooser.name, - mojo.makeRequest(this.fake_bluetooth_chooser_ptr_).handle, 'process'); + + let clientPtrInfo = new mojo.AssociatedInterfacePtrInfo(); + this.fake_bluetooth_chooser_client_binding_ = + new mojo.AssociatedBinding(content.mojom.FakeBluetoothChooserClient, + this, mojo.makeRequest(clientPtrInfo)); + + fakeBluetoothChooserFactoryPtr.createFakeBluetoothChooser( + mojo.makeRequest(this.fake_bluetooth_chooser_ptr_), clientPtrInfo); + + this.events_ = new Array(); + this.event_listener_ = null; + } + + // If the chooser has received more events than |numOfEvents| this function + // will reject the promise, else it will wait until |numOfEvents| events are + // received before resolving with an array of |FakeBluetoothChooserEvent| + // objects. + async waitForEvents(numOfEvents) { + return new Promise(resolve => { + if (this.events_.length > numOfEvents) { + throw `Asked for ${numOfEvents} event(s), but received ` + + `${this.events_.length}.`; + } + + this.event_listener_ = () => { + if (this.events_.length === numOfEvents) { + let result = Array.from(this.events_); + this.event_listener_ = null; + this.events_ = []; + resolve(result); + } + }; + this.event_listener_(); + }); } async selectPeripheral(peripheral) { @@ -520,6 +573,22 @@ } await this.fake_bluetooth_chooser_ptr_.selectPeripheral(peripheral.address); } + + async cancel() { + await this.fake_bluetooth_chooser_ptr_.cancel(); + } + + async rescan() { + await this.fake_bluetooth_chooser_ptr_.rescan(); + } + + onEvent(chooserEvent) { + chooserEvent.type = MOJO_CHOOSER_EVENT_TYPE_MAP[chooserEvent.type]; + this.events_.push(chooserEvent); + if (this.event_listener_ !== null) { + this.event_listener_(); + } + } } // If this line fails, it means that current environment does not support the
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt index 668cea3..9d7520dd 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements_firefox.txt
@@ -5,5 +5,5 @@ mozrunner==7.4.0 mozleak==0.2 mozinstall==2.0.0 -mozdownload==1.25 +mozdownload==1.26.0 mozversion==2.1.0
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py index 21d41d1b..18a1ed7 100644 --- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py +++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/edge.py
@@ -1,4 +1,5 @@ from __future__ import print_function +import time import subprocess from .base import Browser, ExecutorBrowser, require_arg from ..webdriver_server import EdgeDriverServer @@ -84,6 +85,21 @@ def stop(self, force=False): self.server.stop(force=force) + # Wait for Edge browser process to exit if driver process is found + edge_proc_name = 'MicrosoftEdge.exe' + for i in range(0,5): + procs = subprocess.check_output(['tasklist', '/fi', 'ImageName eq ' + edge_proc_name]) + if 'MicrosoftWebDriver.exe' not in procs: + # Edge driver process already exited, don't wait for browser process to exit + break + elif edge_proc_name in procs: + time.sleep(0.5) + else: + break + + if edge_proc_name in procs: + # close Edge process if it is still running + subprocess.call(['taskkill.exe', '/f', '/im', 'microsoftedge*']) def pid(self): return self.server.pid
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html index 9487c35..416e286 100644 --- a/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html +++ b/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html
@@ -1,11 +1,11 @@ <!DOCTYPE html> -<title>Test that xr is advertised in the feature list</title> -<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features"> -<link rel="help" href="https://immersive-web.github.io/webxr/#feature-policy"> +<title>Test that (obsolete) vr is advertised in the feature list</title> +<!-- Some WebVR implementations used "vr", but this is now obsolete and WebXR is moving in a different direction.-->> +<link rel="help" href="https://github.com/immersive-web/webxr/issues/308"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script> test(() => { - assert_in_array('xr', document.featurePolicy.features()); -}, 'document.featurePolicy.features should advertise xr.'); + assert_in_array('vr', document.featurePolicy.features()); +}, 'document.featurePolicy.features should advertise (obsolete) vr.'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/webxr-supported-by-feature-policy.html b/third_party/blink/web_tests/external/wpt/webxr/webxr-supported-by-feature-policy.html new file mode 100644 index 0000000..6d05310 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webxr/webxr-supported-by-feature-policy.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<title>Test that xr is advertised in the feature list</title> +<!-- The Feature Policy feature name(s) for WebXR are still TBD. See the issue below. This test checks what is currently in the spec. --> +<link rel="help" href="https://github.com/immersive-web/webxr/issues/308"> +<link rel="help" href="https://w3c.github.io/webappsec-feature-policy/#dom-featurepolicy-features"> +<link rel="help" href="https://immersive-web.github.io/webxr/#feature-policy"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(() => { + assert_in_array('xr', document.featurePolicy.features()); +}, 'document.featurePolicy.features should advertise xr.'); +</script>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-change-auto-repeat-tracks.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-change-auto-repeat-tracks.html deleted file mode 100644 index 4885b03..0000000 --- a/third_party/blink/web_tests/fast/css-grid-layout/grid-change-auto-repeat-tracks.html +++ /dev/null
@@ -1,157 +0,0 @@ -<!DOCTYPE html> -<link href="resources/grid.css" rel="stylesheet"> -<style> -.grid { - grid-auto-rows: 25px; - grid-auto-columns: 25px; - margin-bottom: 10px; -} - -.fixedWidth { - width: 50px; - grid-auto-flow: row; - grid-template-columns: repeat(auto-fill, 25px); -} - -.fixedHeight { - width: 100px; - height: 50px; - grid-auto-flow: column; - grid-template-rows: repeat(auto-fill, 25px); -} - -#i1, #i21 { - grid-row: auto; - grid-column: 1; - background-color: orange; -} - -#i2, #i22 { - grid-row: 1; - grid-column: auto; - background-color: green; -} - -#i3, #i23 { - grid-row: auto; - grid-column: auto; - background-color: blue; -} -</style> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../../resources/check-layout-th.js"></script> -<script> -function setGridTemplate(id, gridTemplateRows, gridTemplateColumns) -{ - var gridElement = document.getElementById(id); - gridElement.style.gridTemplateRows = gridTemplateRows; - gridElement.style.gridTemplateColumns = gridTemplateColumns; -} - -function setGridSize(id, width, height) -{ - var gridElement = document.getElementById(id); - gridElement.style.width = width; - gridElement.style.height = height; -} - -function setGridItemPlacement(id, gridRow, gridColumn) { - var gridItem = document.getElementById(id); - gridItem.style.gridRow = gridRow; - gridItem.style.gridColumn = gridColumn; -} - -function testGridDefinitions(firstGridItemData, secondGridItemData, thirdGridItemData) -{ - var i1 = document.getElementById(firstGridItemData.id); - i1.setAttribute("data-expected-width", firstGridItemData.width); - i1.setAttribute("data-expected-height", firstGridItemData.height); - i1.setAttribute("data-offset-x", firstGridItemData.x); - i1.setAttribute("data-offset-y", firstGridItemData.y); - - var i2 = document.getElementById(secondGridItemData.id); - i2.setAttribute("data-expected-width", secondGridItemData.width); - i2.setAttribute("data-expected-height", secondGridItemData.height); - i2.setAttribute("data-offset-x", secondGridItemData.x); - i2.setAttribute("data-offset-y", secondGridItemData.y); - - var i3 = document.getElementById(thirdGridItemData.id); - i3.setAttribute("data-expected-width", thirdGridItemData.width); - i3.setAttribute("data-expected-height", thirdGridItemData.height); - i3.setAttribute("data-offset-x", thirdGridItemData.x); - i3.setAttribute("data-offset-y", thirdGridItemData.y); - - checkLayout(".grid", false); -} - -function testChangingGridDefinitions() -{ - // Test changing the number of auto-repeat tracks. - setGridTemplate('grid1', 'none', 'repeat(auto-fill, 25px)'); - testGridDefinitions({ 'id': 'i1', 'width': '25', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '25', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '25', 'height': '25', 'x': '25', 'y': '25' }); - setGridTemplate('grid2', 'repeat(auto-fill, 25px)', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '25', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '25', 'x': '25', 'y': '25' }); - - setGridTemplate('grid1', 'none', 'none'); - testGridDefinitions({ 'id': 'i1', 'width': '25', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '25', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '25', 'height': '25', 'x': '0', 'y': '50' }); - setGridTemplate('grid2', 'none', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '25', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '25', 'x': '50', 'y': '0' }); - - setGridTemplate('grid1', 'none', '5px repeat(auto-fill, 20px)'); - testGridDefinitions({ 'id': 'i1', 'width': '5', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '5', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '20', 'height': '25', 'x': '5', 'y': '25' }); - setGridTemplate('grid2', 'repeat(auto-fill, 20px) 3px', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '20', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '20', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '20', 'x': '25', 'y': '20' }); - - setGridTemplate('grid1', 'none', '5px repeat(auto-fill, 22px)'); - testGridDefinitions({ 'id': 'i1', 'width': '5', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '5', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '22', 'height': '25', 'x': '5', 'y': '25' }); - setGridTemplate('grid2', 'repeat(auto-fill, 18px) 3px', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '18', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '18', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '18', 'x': '25', 'y': '18' }); - - setGridTemplate('grid1', 'none', 'repeat(auto-fill, 45px)'); - testGridDefinitions({ 'id': 'i1', 'width': '45', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '45', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '45', 'height': '25', 'x': '0', 'y': '50' }); - setGridTemplate('grid2', 'repeat(auto-fill, 45px)', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '45', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '45', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '45', 'x': '50', 'y': '0' }); - - // Test changing the size of the grid. - setGridSize('grid1', '100px', 'auto'); - testGridDefinitions({ 'id': 'i1', 'width': '45', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '45', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '45', 'height': '25', 'x': '45', 'y': '25' }); - setGridSize('grid2', '100px', '100px'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '45', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '45', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '45', 'x': '25', 'y': '45' }); - - // Move the third item so that there is an empty track between it and the others. - setGridItemPlacement('i3', 'auto', '3'); - testGridDefinitions({ 'id': 'i1', 'width': '45', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '45', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '25', 'height': '25', 'x': '90', 'y': '25' }); - setGridItemPlacement('i23', '3', 'auto'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '45', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '45', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '25', 'x': '25', 'y': '90' }); - - // Set the same templates, but using auto-fit instead of auto-fill. The empty track should collapse. - setGridTemplate('grid1', 'none', 'repeat(auto-fit, 45px)'); - testGridDefinitions({ 'id': 'i1', 'width': '45', 'height': '25', 'x': '0', 'y': '25' }, { 'id': 'i2', 'width': '45', 'height': '25', 'x': '0', 'y': '0' }, { 'id': 'i3', 'width': '25', 'height': '25', 'x': '45', 'y': '25' }); - setGridTemplate('grid2', 'repeat(auto-fit, 45px)', 'none'); - testGridDefinitions({ 'id': 'i21', 'width': '25', 'height': '45', 'x': '0', 'y': '0' }, { 'id': 'i22', 'width': '25', 'height': '45', 'x': '25', 'y': '0' }, { 'id': 'i23', 'width': '25', 'height': '25', 'x': '25', 'y': '45' }); - - done(); -} - -window.addEventListener("load", testChangingGridDefinitions, false); -</script> - -<div>This test checks that grid-template-{rows|columns} with auto-repeat tracks recomputes the positions of automatically placed grid items.</div> -<div id="log"></div> - -<div style="position: relative"> - <div id="grid1" class="grid fixedWidth"> - <div id="i1"></div> - <div id="i2"></div> - <div id="i3"></div> - </div> -</div> - -<div style="position: relative"> - <div id="grid2" class="grid fixedHeight"> - <div id="i21"></div> - <div id="i22"></div> - <div id="i23"></div> - </div> -</div>
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 86a4de4..152bbdd 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
@@ -11,6 +11,7 @@ encrypted-media execution-while-not-rendered execution-while-out-of-viewport +focus-without-user-activation font-display-late-swap forms fullscreen
diff --git a/tools/chrome_proxy/webdriver/video.py b/tools/chrome_proxy/webdriver/video.py index 1577a05..e6b641b9 100644 --- a/tools/chrome_proxy/webdriver/video.py +++ b/tools/chrome_proxy/webdriver/video.py
@@ -95,16 +95,18 @@ # ofcl should be same as compressed full content length, since no # compression for XHR. self.assertEqual(ofcl, compressed_full_content_length) - # Navigate away to trigger the metrics recording for previous page load. + # Wait and navigate away to trigger the metrics recording for previous + # page load. + time.sleep(1) t.LoadURL('about:blank') - original_kb_histogram = t.GetHistogram('PageLoad.Clients.' + original_kb_histogram = t.GetBrowserHistogram('PageLoad.Clients.' 'DataReductionProxy.Experimental.Bytes.Network.Original2') - compression_percent_histogram = t.GetHistogram('PageLoad.Clients.' + compression_percent_histogram = t.GetBrowserHistogram('PageLoad.Clients.' 'DataReductionProxy.Experimental.Bytes.Network.CompressionRatio2') self.assertEqual(1, original_kb_histogram['count']) self.assertEqual(1, compression_percent_histogram['count']) # Verify the total page size is 3 KB, and compression ratio. - self.assertEqual(3, original_kb_histogram['sum']) + self.assertGreaterEqual(3, original_kb_histogram['sum']) self.assertEqual(compression_percent_histogram['sum'], compressed_full_content_length/ofcl*100) self.assertTrue(saw_range_response, 'No range request was seen in test!')
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py index 5350e2e..3a137584 100755 --- a/tools/cygprofile/orderfile_generator_backend.py +++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -834,36 +834,40 @@ out_file_path = os.path.join(out_dir, 'results-chart.json') if not os.path.exists(out_file_path): - raise Exception('orderfile.memory_mobile results file not found!') + raise Exception('Results file not found!') with open(out_file_path, 'r') as f: json_results = json.load(f) if not json_results: - raise Exception('orderfile.memory_mobile results file is empty') + raise Exception('Results file is empty') + + if not 'charts' in json_results: + raise Exception('charts can not be found in results!') + + charts = json_results['charts'] + results = dict() + for story in charts: + if not story.endswith("NativeCodeResidentMemory_avg"): + continue + + results[story] = dict() + for substory in charts[story]: + if substory == 'summary': + continue + if not 'values' in charts[story][substory]: + raise Exception( + 'Values can not be found in charts:%s:%s' % (story, substory)) + + results[story][substory] = charts[story][substory]['values'] + return results + + except Exception as e: + return 'Error: ' + str(e) finally: shutil.rmtree(out_dir) - if not 'charts' in json_results: - raise Exception('charts can not be found in results!') - - charts = json_results['charts'] - results = dict() - for story in charts: - if not story.endswith("NativeCodeResidentMemory_avg"): - continue - - results[story] = dict() - for substory in charts[story]: - if substory == 'summary': - continue - if not 'values' in charts[story][substory]: - raise Exception( - 'Values can not be found in charts:%s:%s' % (story, substory)) - - results[story][substory] = charts[story][substory]['values'] - return results def _PerformanceBenchmark(self, apk): """Runs Speedometer2.0 to assess performance. @@ -881,32 +885,34 @@ '--device={}'.format( self._profiler._device.serial), '--browser=exact', - '--output-format=chartjson', + '--output-format=histograms', '--output-dir={}'.format(out_dir), '--reset-results', '--browser-executable={}'.format(apk), 'speedometer2']) - out_file_path = os.path.join(out_dir, 'results-chart.json') + out_file_path = os.path.join(out_dir, 'histograms.json') if not os.path.exists(out_file_path): - raise Exception('speedometer2 results file not found!') + raise Exception('Results file not found!') with open(out_file_path, 'r') as f: results = json.load(f) if not results: - raise Exception('speedometer2 results file is empty.') + raise Exception('Results file is empty.') + + for el in results: + if 'name' in el and el['name'] == 'Total' and 'sampleValues' in el: + return el['sampleValues'] + + raise Exception('Unexpected results format.') + + except Exception as e: + return 'Error: ' + str(e) finally: shutil.rmtree(out_dir) - keys = ['charts', 'Total', 'Speedometer2', 'values'] - for key in keys: - if not key in results: - raise Exception('Unexpected results format, %s can not be found.' % key) - results = results[key] - - return results def RunBenchmark(self, out_directory, no_orderfile=False): """Builds chrome apk and runs performance and memory benchmarks. @@ -945,6 +951,10 @@ self._compiler.chrome_apk) benchmark_results['orderfile.memory_mobile'] = ( self._NativeCodeMemoryBenchmark(self._compiler.chrome_apk)) + + except Exception as e: + benchmark_results['Error'] = str(e) + finally: if no_orderfile and os.path.exists(backup_orderfile): shutil.move(backup_orderfile, orderfile_path)
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index e946fdb3..7c6d35e 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -495,6 +495,7 @@ 'WebKit Linux Trusty ASAN': 'asan_lsan_release_bot', 'WebKit Linux Trusty Leak': 'release_bot', 'WebKit Linux Trusty MSAN': 'msan_release_bot', + 'android-asan': 'android_clang_asan_debug_bot_minimal_symbols_arm64', 'win-asan': 'asan_clang_fuzzer_static_v8_heap_minimal_symbols_release', }, @@ -924,6 +925,10 @@ 'strip_debug_info', ], + 'android_clang_asan_debug_bot_minimal_symbols_arm64': [ + 'android', 'clang', 'asan', 'debug_bot', 'minimal_symbols', 'arm64', + ], + 'android_clang_asan_debug_trybot_compile_only': [ 'android', 'clang', 'asan', 'debug_trybot', 'compile_only', ],
diff --git a/tools/metrics/histograms/README.md b/tools/metrics/histograms/README.md index d12ea02..550ab45 100644 --- a/tools/metrics/histograms/README.md +++ b/tools/metrics/histograms/README.md
@@ -314,13 +314,21 @@ Histogram expiry is specified by **'expires_after'** attribute in histogram descriptions in histograms.xml. The attribute can be specified as date in -**YYYY-MM-DD** format or as Chrome milestone in **M**\*(e.g. M68) format. After -a histogram expires, it will not be recorded (nor uploaded to the UMA servers). -The code to record it becomes dead code, and should be removed from the -codebase along with marking the histogram definition as obsolete. However, if -histogram would remain useful, the expiration should be extended accordingly -before it becomes expired. If histogram you care about already expired, see -[Expired Histogram Whitelist](#Expired-histogram-whitelist). +**YYYY-MM-DD** format or as Chrome milestone in **M**\*(e.g. M68) format. In the +latter case, the actual expiry date is about 12 weeks after that branch is cut, +or basically when it is replaced on the "stable" channel by the following +release. + +After a histogram expires, it will cease to be displayed on the dashboard. +However, the client may continue to send data for that histogram for some time +after the official expiry date so simply bumping the 'expires_after' date in +HEAD may be sufficient to resurrect it without any discontinuity. If too much +time has passed and the client is no longer sending data, it can be re-enabled +via Finch: see [Expired Histogram Whitelist](#Expired-histogram-whitelist). + +Once a histogram has expired, the code to record it becomes dead code and should +be removed from the codebase along with marking the histogram definition as +obsolete. In **rare** cases, the expiry can be set to "never". This is used to denote metrics of critical importance that are, typically, used for other reports.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 2d1c195bff..da4f905 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -23267,6 +23267,7 @@ <int value="49" label="DownloadsWithoutUserActivation"/> <int value="50" label="ExecutionWhileOutOfViewport"/> <int value="51" label="ExecutionWhileNotRendered"/> + <int value="52" label="FocusWithoutUserActivation"/> </enum> <enum name="FeaturePolicyImageCompressionFormat">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index b6f697b..15e1ce56 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -80267,7 +80267,39 @@ </summary> </histogram> -<histogram name="ntp.searchurls.total" expires_after="2016-04-29"> +<histogram name="NQE.WifiSignalStrength.AtECTComputation" + units="Signal Strength Level" expires_after="2019-12-01"> + <owner>tbansal@chromium.org</owner> + <summary> + Wifi signal strength level reported by the device at the time of ECT + computation. Recorded only on Wifi networks, and only when the value is + available. The value ranges between 0 and 4 (both inclusive). + </summary> +</histogram> + +<histogram name="NQE.WifiSignalStrength.LevelAvailable" enum="BooleanAvailable"> + <owner>tbansal@chromium.org</owner> + <owner>bengr@chromium.org</owner> + <summary> + Whether the signal strength for the Wifi network was available or not. + Recorded right before a connection change event. Recorded only on Wifi + connections on Android platform. + </summary> +</histogram> + +<histogram name="NQE.WifiSignalStrength.LevelDifference" + units="Signal Strength Level"> + <owner>tbansal@chromium.org</owner> + <owner>bengr@chromium.org</owner> + <summary> + Difference between the minimum and the maximum received signal strength + since the last connection change event. Recorded only on Wifi connections on + Android platform when the Wifi signal strength was available. Recorded right + before a connection change event. + </summary> +</histogram> + +<histogram name="ntp.searchurls.total"> <obsolete> Deprecated 04/2016 as doesn't have data nor owner. </obsolete>
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py index 2fed657..f531019 100644 --- a/tools/perf/benchmarks/benchmark_smoke_unittest.py +++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -96,8 +96,10 @@ with open(path_util.GetExpectationsPath()) as fp: single_page_benchmark.AugmentExpectationsWithParser(fp.read()) - self.assertEqual(0, single_page_benchmark.Run(options), - msg='Failed: %s' % benchmark) + return_code = single_page_benchmark.Run(options) + if return_code == -1: + self.skipTest('The benchmark was not run.') + self.assertEqual(0, return_code, msg='Failed: %s' % benchmark) return BenchmarkSmokeTest
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py index 574f883a..d794d66 100644 --- a/tools/perf/benchmarks/system_health_smoke_test.py +++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -218,8 +218,10 @@ with open(path_util.GetExpectationsPath()) as fp: single_page_benchmark.AugmentExpectationsWithParser(fp.read()) - self.assertEqual(0, single_page_benchmark.Run(options), - msg='Failed: %s' % benchmark_class) + return_code = single_page_benchmark.Run(options) + if return_code == -1: + self.skipTest('The benchmark was not run.') + self.assertEqual(0, return_code, msg='Failed: %s' % benchmark_class) # We attach the test method to SystemHealthBenchmarkSmokeTest dynamically # so that we can set the test method name to include
diff --git a/tools/perf/page_sets/image_decoding_cases/decoding_story_helpers.js b/tools/perf/page_sets/image_decoding_cases/decoding_story_helpers.js new file mode 100644 index 0000000..fe1637aa --- /dev/null +++ b/tools/perf/page_sets/image_decoding_cases/decoding_story_helpers.js
@@ -0,0 +1,19 @@ +// 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. + +/* Given |array| of base64 data or image paths, this function successively + * replaces the source of <img id="img"> with elements + * {0, 1, ..., |numSquares| - 1} for |numSquares| from the initial call. + * This happens every 450 ms. + */ +function drawColorfulSquares(numSquares, array) { + if (numSquares > 0) { + setTimeout(function() { + var image_width = 1024; + var image_height = 1024; + document.getElementById('img').src = array[numSquares - 1]; + drawColorfulSquares(numSquares - 1, array); + }, 450); + } +}
diff --git a/tools/perf/page_sets/image_decoding_cases/jpeg_decoding.html b/tools/perf/page_sets/image_decoding_cases/jpeg_decoding.html new file mode 100644 index 0000000..b666cd7 --- /dev/null +++ b/tools/perf/page_sets/image_decoding_cases/jpeg_decoding.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>JPEG image decoding</title> + <script src="decoding_story_helpers.js"></script> + <script src="jpeg_squares_base64.js"></script> + </head> + + <body> + <img id="img"> + <script> + var numSquares = jpeg_squares_base64.length; + drawColorfulSquares(numSquares, jpeg_squares_base64); + </script> + </body> +</html>
diff --git a/tools/perf/page_sets/image_decoding_cases/jpeg_squares_base64.js b/tools/perf/page_sets/image_decoding_cases/jpeg_squares_base64.js new file mode 100644 index 0000000..80bf93c --- /dev/null +++ b/tools/perf/page_sets/image_decoding_cases/jpeg_squares_base64.js
@@ -0,0 +1,15 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +var jpeg_squares_base64 = [ + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAf/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCtgBYFoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/9k=', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAf/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCvABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//2Q==', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAYH/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8Aq4AbgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//Z', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAf/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC4gATcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//2Q==', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAUI/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AtQAWGLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//Z', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAQH/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AuIATsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAcI/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AqwAXhsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//Z', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAf/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCtgBYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/9k=', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAcI/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AqoAXhsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//Z', + 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAQABAADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAb/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCygBYLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//9k=' +];
diff --git a/tools/perf/page_sets/image_decoding_cases/webp_decoding.html b/tools/perf/page_sets/image_decoding_cases/webp_decoding.html new file mode 100644 index 0000000..59079711 --- /dev/null +++ b/tools/perf/page_sets/image_decoding_cases/webp_decoding.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>WebP image decoding</title> + <script src="decoding_story_helpers.js"></script> + <script src="webp_squares_base64.js"></script> + </head> + + <body> + <img id="img"> + <script> + var numSquares = webp_squares_base64.length; + drawColorfulSquares(numSquares, webp_squares_base64); + </script> + </body> +</html>
diff --git a/tools/perf/page_sets/image_decoding_cases/webp_squares_base64.js b/tools/perf/page_sets/image_decoding_cases/webp_squares_base64.js new file mode 100644 index 0000000..1ce372a --- /dev/null +++ b/tools/perf/page_sets/image_decoding_cases/webp_squares_base64.js
@@ -0,0 +1,8 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +var webp_squares_base64 = [ + 'data:image/webp;base64,UklGRpAHAABXRUJQVlA4IIQHAAAw3wCdASoABAAEPgQBAAAACJaW7hd2Ee3AfgAAAT1/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qyxAAA/v/98H7///kpr/G3LV///hTXwpr4U1/wgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'data:image/webp;base64,UklGRowHAABXRUJQVlA4IIAHAADw3gCdASoABAAEPgQBAAAACJaW7hd2Ee3AAAFqMKig825kZpSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZGAAP7//fwNX/6kYYux///7Gb/6M3/0Zv6ygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'data:image/webp;base64,UklGRoYHAABXRUJQVlA4IHoHAACw3gCdASoABAAEPgQBAAAACJaW7hd2EbQArR6xlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWiwAD+//6i3P/wbJFNyP//O34g/UFyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'data:image/webp;base64,UklGRoAHAABXRUJQVlA4IHQHAACw3gCdASoABAAEPgQBAAAACJaW7hd2EbQArR6xlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWiwAD+//550//9avdf6tG0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', + 'data:image/webp;base64,UklGRowHAABXRUJQVlA4IIAHAAAw3wCdASoABAAEPgQBAAAACJaW7hd2Ee3AfgAAAT1/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qyxAAA/v/98H7///kpr/G3LV///KVeCxub4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', 'data:image/webp;base64,UklGRoQHAABXRUJQVlA4IHgHAACw3gCdASoABAAEPgQBAAAACJaW7hd2EbQArR6xlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWiwAD+//550///6TZ//SbP/6TZ8cwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'data:image/webp;base64,UklGRpAHAABXRUJQVlA4IIQHAAAw3wCdASoABAAEPgQBAAAACJaW7hd2Ee3AfgAAAWowqKDzbmRmlK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VkYAA/v/9oGr///aWfqWfqWf48v//56ZrzB/hYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'data:image/webp;base64,UklGRowHAABXRUJQVlA4IIAHAADw3gCdASoABAAEPgQBAAAACJaW7hd2Ee3AAAE9f+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6ssQAAP7//fhyP//na2GIKl///z8z/dP879EgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', + 'data:image/webp;base64,UklGRn4HAABXRUJQVlA4IHIHAACw3gCdASoABAAEPgQBAAAACJaW7hd2EbQAnr/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VliAAD+//5r0//4i6e8AYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'data:image/webp;base64,UklGRoYHAABXRUJQVlA4IHoHAACw3gCdASoABAAEPgQBAAAACJaW7hd2EbQAtRhUUHm3MjNKV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rLSv/VlpX/qy0r/1ZaV/6stK/9WWlf+rIwAD+//zcK///4yp//jKn/+Mqf8XMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' +];
diff --git a/tools/perf/page_sets/image_decoding_cases/yuv_decoding.html b/tools/perf/page_sets/image_decoding_cases/yuv_decoding.html deleted file mode 100644 index 12a0e8f3..0000000 --- a/tools/perf/page_sets/image_decoding_cases/yuv_decoding.html +++ /dev/null
@@ -1,40 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> - <title>JPEG image decoding</title> -</head> -<body> -<img id="myimg"> -<script> -var image_width = 1024; -var image_height = 1024; -var nb_images = 200; -var urls = []; - -for (i = 0; i < nb_images; ++i) { - var canvas = document.createElement("canvas"); - canvas.width = image_width; - canvas.height = image_height; - var ctx = canvas.getContext("2d"); - ctx.fillStyle = "rgb("+ - Math.floor(Math.random()*256)+","+ - Math.floor(Math.random()*256)+","+ - Math.floor(Math.random()*256)+")"; - ctx.fillRect(0,0,image_width,image_height); - // If quality is 1, then we get YUV 444 encoding - // If quality is <1, then we get YUV 420 encoding - urls[i] = canvas.toDataURL("image/jpeg", 0.99); -} - -var idx = 0; -function redraw() { - document.getElementById("myimg").src = urls[idx]; - idx++; - if (idx >= nb_images) { idx = 0; } - window.setTimeout(redraw, 1); -} -window.setTimeout(redraw, 1); -</script> -</body> -</html>
diff --git a/tools/perf/page_sets/rendering/image_decoding_cases.py b/tools/perf/page_sets/rendering/image_decoding_cases.py index 49117be..bc23633 100644 --- a/tools/perf/page_sets/rendering/image_decoding_cases.py +++ b/tools/perf/page_sets/rendering/image_decoding_cases.py
@@ -16,8 +16,6 @@ shared_page_state_class=shared_page_state.SharedPageState, name_suffix='', extra_browser_args=None): - if extra_browser_args is None: - extra_browser_args = ['--disable-accelerated-jpeg-decoding'] super(ImageDecodingPage, self).__init__( page_set=page_set, shared_page_state_class=shared_page_state_class, @@ -29,7 +27,12 @@ action_runner.Wait(5) -class YuvDecodingPage(ImageDecodingPage): - BASE_NAME = 'yuv_decoding' - URL = 'file://../image_decoding_cases/yuv_decoding.html' - TAGS = ImageDecodingPage.TAGS + [story_tags.REPRESENTATIVE_MAC_DESKTOP] +class WebPDecodingPage(ImageDecodingPage): + BASE_NAME = 'webp_decoding' + URL = 'file://../image_decoding_cases/webp_decoding.html' + TAGS = ImageDecodingPage.TAGS + [story_tags.GPU_RASTERIZATION] + +class JpegDecodingPage(ImageDecodingPage): + BASE_NAME = 'jpeg_decoding' + URL = 'file://../image_decoding_cases/jpeg_decoding.html' + TAGS = ImageDecodingPage.TAGS + [story_tags.GPU_RASTERIZATION]
diff --git a/tools/perf/page_sets/rendering/rendering_stories.py b/tools/perf/page_sets/rendering/rendering_stories.py index 98cb201..8954b2f 100644 --- a/tools/perf/page_sets/rendering/rendering_stories.py +++ b/tools/perf/page_sets/rendering/rendering_stories.py
@@ -41,6 +41,7 @@ continue required_args = [] + name_suffix = '' if (story_class.TAGS and story_tags.USE_FAKE_CAMERA_DEVICE in story_class.TAGS): required_args += [ @@ -56,20 +57,25 @@ # 'backdrop-filter' CSS property to work. required_args.append('--enable-experimental-web-platform-features') - self.AddStory(story_class( - page_set=self, - shared_page_state_class=shared_page_state_class, - extra_browser_args=required_args)) - if (story_class.TAGS and - story_tags.IMAGE_DECODING in story_class.TAGS): + story_tags.IMAGE_DECODING in story_class.TAGS and + story_tags.GPU_RASTERIZATION in story_class.TAGS): + required_args.append('--force-gpu-rasterization') + # Run RGB decoding with GPU rasterization (to be most comparable to YUV) self.AddStory(story_class( page_set=self, + extra_browser_args=required_args + + ['--disable-yuv-image-decoding'], shared_page_state_class=shared_page_state_class, - name_suffix='_gpu_rasterization_and_decoding', - extra_browser_args=required_args + [ - '--force-gpu-rasterization', - ])) + name_suffix='_rgb_and_gpu_rasterization')) + # Also run YUV decoding story with GPU rasterization. + name_suffix = '_yuv_and_gpu_rasterization' + + self.AddStory(story_class( + page_set=self, + extra_browser_args=required_args, + shared_page_state_class=shared_page_state_class, + name_suffix=name_suffix)) class DesktopRenderingStorySet(RenderingStorySet):
diff --git a/tools/perf/page_sets/rendering/story_tags.py b/tools/perf/page_sets/rendering/story_tags.py index 5bedaa9..c2fda61 100644 --- a/tools/perf/page_sets/rendering/story_tags.py +++ b/tools/perf/page_sets/rendering/story_tags.py
@@ -25,7 +25,8 @@ BACKDROP_FILTER = Tag( 'backdrop_filter', 'Backdrop filter stories') IMAGE_DECODING = Tag( - 'image_decoding', 'Stories with accelerated jpeg decoding') + 'image_decoding', ('Stories decoding JPEG and WebP (and using GPU ' + 'rasterization) to compare YUV and RGB')) KEY_DESKTOP_MOVE = Tag( 'key_desktop_move', 'Key desktop move stories') KEY_HIT_TEST = Tag(
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc index db33d5b..e72aa83 100644 --- a/ui/accessibility/ax_node.cc +++ b/ui/accessibility/ax_node.cc
@@ -26,6 +26,12 @@ parent_(parent), language_info_(nullptr) { data_.id = id; + // If this node is the root, use the given index_in_parent to provide + // consistency. + if (!parent) + unignored_index_in_parent_ = index_in_parent_; + else + unignored_index_in_parent_ = -1; } AXNode::~AXNode() = default; @@ -75,15 +81,7 @@ } int AXNode::GetUnignoredIndexInParent() const { - AXNode* parent = GetUnignoredParent(); - if (parent) { - for (int i = 0; i < parent->GetUnignoredChildCount(); ++i) { - if (parent->GetUnignoredChildAtIndex(i) == this) - return i; - } - } - - return 0; + return unignored_index_in_parent_; } bool AXNode::IsText() const { @@ -117,6 +115,11 @@ index_in_parent_ = index_in_parent; } +void AXNode::UpdateUnignoredIndexInParentForChildren() { + if (!data().HasState(ax::mojom::State::kIgnored)) + ComputeUnignoredIndexInParentForChildrenRecursive(0); +} + void AXNode::SwapChildren(std::vector<AXNode*>& children) { children.swap(children_); } @@ -687,6 +690,21 @@ } } +int AXNode::ComputeUnignoredIndexInParentForChildrenRecursive(int startIndex) { + int count = 0; + for (int i = 0; i < child_count(); i++) { + AXNode* child = children_[i]; + if (child->data().HasState(ax::mojom::State::kIgnored)) { + child->unignored_index_in_parent_ = -1; + count += child->ComputeUnignoredIndexInParentForChildrenRecursive( + startIndex + count); + } else { + child->unignored_index_in_parent_ = startIndex + count++; + } + } + return count; +} + // Finds ordered set that immediately contains node. // Is not required for set's role to match node's role. AXNode* AXNode::GetOrderedSet() const {
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h index a9e2cb69..8dd03db9 100644 --- a/ui/accessibility/ax_node.h +++ b/ui/accessibility/ax_node.h
@@ -94,6 +94,9 @@ // Set the index in parent, for example if siblings were inserted or deleted. void SetIndexInParent(int index_in_parent); + // Update the unignored index in parent for unignored children. + void UpdateUnignoredIndexInParentForChildren(); + // Swap the internal children vector with |children|. This instance // now owns all of the passed children. void SwapChildren(std::vector<AXNode*>& children); @@ -308,11 +311,14 @@ void IdVectorToNodeVector(std::vector<int32_t>& ids, std::vector<AXNode*>* nodes) const; + int ComputeUnignoredIndexInParentForChildrenRecursive(int startIndex); + // Finds and returns a pointer to ordered set containing node. AXNode* GetOrderedSet() const; OwnerTree* tree_; // Owns this. int index_in_parent_; + int unignored_index_in_parent_; AXNode* parent_; std::vector<AXNode*> children_; AXNodeData data_;
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc index 7466c2f..272caaa 100644 --- a/ui/accessibility/ax_table_info.cc +++ b/ui/accessibility/ax_table_info.cc
@@ -376,8 +376,13 @@ data.id = id; data.role = ax::mojom::Role::kColumn; node->SetData(data); - for (AXTreeObserver& observer : tree_->observers()) + for (AXTreeObserver& observer : tree_->observers()) { observer.OnNodeCreated(tree_, node); + observer.OnAtomicUpdateFinished( + tree_, false, + {AXTreeObserver::Change(node, + AXTreeObserver::ChangeType::NODE_CREATED)}); + } return node; } @@ -389,8 +394,14 @@ data.id = id; data.role = ax::mojom::Role::kTableHeaderContainer; node->SetData(data); - for (AXTreeObserver& observer : tree_->observers()) + + for (AXTreeObserver& observer : tree_->observers()) { observer.OnNodeCreated(tree_, node); + observer.OnAtomicUpdateFinished( + tree_, false, + {AXTreeObserver::Change(node, + AXTreeObserver::ChangeType::NODE_CREATED)}); + } return node; }
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc index 36e661e..cdd12fc 100644 --- a/ui/accessibility/ax_tree.cc +++ b/ui/accessibility/ax_tree.cc
@@ -132,6 +132,10 @@ // decisions about when to notify observers of removals or reparenting. std::set<int> changed_node_ids; + // keeps track of parents whose unignored children have changed. Used for + // caching unignored relationships. + std::unordered_set<int> changed_unignored_parent_ids; + // Keeps track of new nodes created during this update. std::set<const AXNode*> new_nodes; @@ -498,6 +502,12 @@ changes.push_back(AXTreeObserver::Change(node, change)); } + for (int parent_id : update_state.changed_unignored_parent_ids) { + AXNode* parent = GetFromId(parent_id); + if (parent) + parent->UpdateUnignoredIndexInParentForChildren(); + } + // Tree is no longer updating. SetTreeUpdateInProgressState(false); @@ -566,6 +576,10 @@ else observer.OnNodeReparented(this, new_node); } + AXNode* unignored_parent = new_node->GetUnignoredParent(); + if (unignored_parent) { + update_state->changed_unignored_parent_ids.insert(unignored_parent->id()); + } return new_node; } @@ -589,10 +603,18 @@ if (!update_state->IsNewNode(node) || update_state->IsReparentedNode(node)) { auto it = update_state->reparented_node_id_to_data.find(node->id()); - if (it != update_state->reparented_node_id_to_data.end()) - CallNodeChangeCallbacks(node, it->second, src); - else - CallNodeChangeCallbacks(node, node->data(), src); + const AXNodeData& old_data = + it != update_state->reparented_node_id_to_data.end() ? it->second + : node->data(); + CallNodeChangeCallbacks(node, old_data, src); + if (old_data.HasState(ax::mojom::State::kIgnored) != + src.HasState(ax::mojom::State::kIgnored)) { + AXNode* unignored_parent = node->GetUnignoredParent(); + if (unignored_parent) { + update_state->changed_unignored_parent_ids.insert( + unignored_parent->id()); + } + } } UpdateReverseRelations(node, src); node->SetData(src); @@ -851,6 +873,10 @@ for (int i = 0; i < node->child_count(); ++i) DestroyNodeAndSubtree(node->ChildAtIndex(i), update_state); if (update_state) { + AXNode* unignored_parent = node->GetUnignoredParent(); + if (unignored_parent) { + update_state->changed_unignored_parent_ids.insert(unignored_parent->id()); + } update_state->pending_nodes.erase(node); update_state->removed_node_ids.insert(node->id()); } @@ -971,8 +997,8 @@ bool node_is_radio_button = (original_node.data().role == ax::mojom::Role::kRadioButton); - for (int i = 0; i < local_parent->child_count(); ++i) { - const AXNode* child = local_parent->ChildAtIndex(i); + for (int i = 0; i < local_parent->GetUnignoredChildCount(); ++i) { + const AXNode* child = local_parent->GetUnignoredChildAtIndex(i); // Invisible children should not be counted. // However, in the collapsed container case (e.g. a combobox), items can
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc index 3a14156..be46697 100644 --- a/ui/accessibility/ax_tree_unittest.cc +++ b/ui/accessibility/ax_tree_unittest.cc
@@ -1403,6 +1403,100 @@ EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id()); } +TEST(AXTreeTest, UnignoredIndexInParentCache) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(5); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids = {2, 3}; + initial_state.nodes[1].id = 2; + initial_state.nodes[1].AddState(ax::mojom::State::kIgnored); + initial_state.nodes[1].child_ids = {4, 5}; + initial_state.nodes[2].id = 3; + initial_state.nodes[3].id = 4; + initial_state.nodes[4].id = 5; + + AXTree tree(initial_state); + AXNode* root = tree.root(); + ASSERT_EQ(2, root->child_count()); + ASSERT_EQ(2, root->ChildAtIndex(0)->id()); + ASSERT_EQ(3, root->ChildAtIndex(1)->id()); + + EXPECT_EQ(3, root->GetUnignoredChildCount()); + EXPECT_EQ(4, root->GetUnignoredChildAtIndex(0)->id()); + EXPECT_EQ(5, root->GetUnignoredChildAtIndex(1)->id()); + EXPECT_EQ(3, root->GetUnignoredChildAtIndex(2)->id()); + EXPECT_EQ(0, root->GetUnignoredChildAtIndex(0)->GetUnignoredIndexInParent()); + EXPECT_EQ(1, root->GetUnignoredChildAtIndex(1)->GetUnignoredIndexInParent()); + EXPECT_EQ(2, root->GetUnignoredChildAtIndex(2)->GetUnignoredIndexInParent()); + + EXPECT_EQ(1, root->GetUnignoredChildAtIndex(0)->GetUnignoredParent()->id()); + + EXPECT_EQ(-1, tree.GetFromId(2)->GetUnignoredIndexInParent()); + + // Ensure when a node goes from ignored to unignored, its children have their + // unignored_index_in_parent updated. + AXTreeUpdate update = initial_state; + update.nodes[1].RemoveState(ax::mojom::State::kIgnored); + + EXPECT_TRUE(tree.Unserialize(update)); + + root = tree.root(); + EXPECT_EQ(2, root->GetUnignoredChildCount()); + EXPECT_EQ(2, root->GetUnignoredChildAtIndex(0)->id()); + EXPECT_EQ(0, tree.GetFromId(4)->GetUnignoredIndexInParent()); + EXPECT_EQ(1, tree.GetFromId(5)->GetUnignoredIndexInParent()); + + // Ensure when a node goes from unignored to unignored, siblings are correctly + // updated. + AXTreeUpdate update2 = update; + update2.nodes[3].AddState(ax::mojom::State::kIgnored); + + EXPECT_TRUE(tree.Unserialize(update2)); + + EXPECT_EQ(-1, tree.GetFromId(4)->GetUnignoredIndexInParent()); + EXPECT_EQ(0, tree.GetFromId(5)->GetUnignoredIndexInParent()); + + // Ensure siblings of a deleted node are updated. + AXTreeUpdate update3 = update2; + update3.nodes.resize(1); + update3.nodes[0].id = 1; + update3.nodes[0].child_ids = {3}; + + EXPECT_TRUE(tree.Unserialize(update3)); + + EXPECT_EQ(0, tree.GetFromId(3)->GetUnignoredIndexInParent()); + + // Ensure new nodes are correctly updated. + AXTreeUpdate update4 = update3; + update4.nodes.resize(3); + update4.nodes[0].id = 1; + update4.nodes[0].child_ids = {3, 6}; + update4.nodes[1].id = 6; + update4.nodes[1].child_ids = {7}; + update4.nodes[2].id = 7; + + EXPECT_TRUE(tree.Unserialize(update4)); + + EXPECT_EQ(0, tree.GetFromId(3)->GetUnignoredIndexInParent()); + EXPECT_EQ(1, tree.GetFromId(6)->GetUnignoredIndexInParent()); + EXPECT_EQ(0, tree.GetFromId(7)->GetUnignoredIndexInParent()); + + // Ensure reparented nodes are correctly updated. + AXTreeUpdate update5 = update4; + update5.nodes.resize(2); + update5.node_id_to_clear = 6; + update5.nodes[0].id = 1; + update5.nodes[0].child_ids = {3, 7}; + update5.nodes[1].id = 7; + update5.nodes[1].child_ids = {}; + + EXPECT_TRUE(tree.Unserialize(update5)); + + EXPECT_EQ(0, tree.GetFromId(3)->GetUnignoredIndexInParent()); + EXPECT_EQ(1, tree.GetFromId(7)->GetUnignoredIndexInParent()); +} + TEST(AXTreeTest, TestRecursionUnignoredChildCount) { AXTreeUpdate tree_update; tree_update.root_id = 1;
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 0c95f7f..b7ecd96a 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -581,8 +581,7 @@ return type_ != LAYER_NOT_DRAWN && GetCombinedOpacity() > 0.0f; } -void Layer::SetRoundedCornerRadius( - const std::array<uint32_t, 4>& corner_radii) { +void Layer::SetRoundedCornerRadius(const gfx::RoundedCornersF& corner_radii) { cc_layer_->SetRoundedCorner(corner_radii); ScheduleDraw();
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h index 90605aa7e..fcb2d59 100644 --- a/ui/compositor/layer.h +++ b/ui/compositor/layer.h
@@ -29,6 +29,7 @@ #include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_type.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/transform.h" @@ -274,11 +275,9 @@ void SetAcceptEvents(bool accept_events); bool accept_events() const { return accept_events_; } - // Sets a rounded corner clip radius on the layer. This will clip the layer to - // bounds. The ordering for the array is: - // top left, top right, bottom right, bottom left - void SetRoundedCornerRadius(const std::array<uint32_t, 4>& corner_radii); - const std::array<uint32_t, 4>& rounded_corner_radii() const { + // Sets a rounded corner clip on the layer. + void SetRoundedCornerRadius(const gfx::RoundedCornersF& corner_radii); + const gfx::RoundedCornersF& rounded_corner_radii() const { return cc_layer_->corner_radii(); }
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index 6c962ceb..12ab5b0 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -922,13 +922,12 @@ EXPECT_EQ(new_bounds, mirror->bounds()); // Check for rounded corner mirror behavior - constexpr std::array<uint32_t, 4> kEmptyCornerRadii = {0, 0, 0, 0}; - EXPECT_EQ(mirror->rounded_corner_radii(), kEmptyCornerRadii); + EXPECT_TRUE(mirror->rounded_corner_radii().IsEmpty()); EXPECT_FALSE(mirror->is_fast_rounded_corner()); - constexpr std::array<uint32_t, 4> corner_radii = {2, 3, 4, 5}; - child->SetRoundedCornerRadius(corner_radii); + constexpr gfx::RoundedCornersF kCornerRadii(2, 3, 4, 5); + child->SetRoundedCornerRadius(kCornerRadii); child->SetIsFastRoundedCorner(true); - EXPECT_EQ(mirror->rounded_corner_radii(), corner_radii); + EXPECT_EQ(kCornerRadii, mirror->rounded_corner_radii()); EXPECT_TRUE(mirror->is_fast_rounded_corner()); } @@ -1034,7 +1033,7 @@ l1->SetVisible(false); l1->SetBounds(gfx::Rect(4, 5)); - constexpr std::array<uint32_t, 4> kCornerRadii = {1, 2, 3, 4}; + constexpr gfx::RoundedCornersF kCornerRadii(1, 2, 3, 4); l1->SetRoundedCornerRadius(kCornerRadii); l1->SetIsFastRoundedCorner(true); @@ -1278,8 +1277,7 @@ TEST_F(LayerWithDelegateTest, RoundedCorner) { gfx::Rect layer_bounds(10, 20, 100, 100); - const std::array<uint32_t, 4> radii = {5, 10, 15, 20}; - const std::array<uint32_t, 4> kEmpty = {0, 0, 0, 0}; + constexpr gfx::RoundedCornersF kRadii(5, 10, 15, 20); std::unique_ptr<Layer> layer(new Layer(LAYER_TEXTURED)); NullLayerDelegate delegate; @@ -1291,12 +1289,12 @@ compositor()->SetRootLayer(layer.get()); Draw(); - EXPECT_EQ(layer->rounded_corner_radii(), kEmpty); + EXPECT_TRUE(layer->rounded_corner_radii().IsEmpty()); // Setting a rounded corner radius should set an rrect with bounds same as the // layer. - layer->SetRoundedCornerRadius(radii); - EXPECT_EQ(layer->rounded_corner_radii(), radii); + layer->SetRoundedCornerRadius(kRadii); + EXPECT_EQ(kRadii, layer->rounded_corner_radii()); } // Checks that stacking-related methods behave as advertised.
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn index 9ba09f1..ad690bf 100644 --- a/ui/gfx/BUILD.gn +++ b/ui/gfx/BUILD.gn
@@ -677,6 +677,7 @@ "geometry/quad_unittest.cc", "geometry/quaternion_unittest.cc", "geometry/rect_unittest.cc", + "geometry/rounded_corners_f_unittest.cc", "geometry/safe_integer_conversions_unittest.cc", "geometry/scroll_offset_unittest.cc", "geometry/size_unittest.cc",
diff --git a/ui/gfx/geometry/BUILD.gn b/ui/gfx/geometry/BUILD.gn index 6450fdd9..8959bcc0 100644 --- a/ui/gfx/geometry/BUILD.gn +++ b/ui/gfx/geometry/BUILD.gn
@@ -41,6 +41,8 @@ "rect_conversions.h", "rect_f.cc", "rect_f.h", + "rounded_corners_f.cc", + "rounded_corners_f.h", "safe_integer_conversions.h", "scroll_offset.cc", "scroll_offset.h",
diff --git a/ui/gfx/geometry/rounded_corners_f.cc b/ui/gfx/geometry/rounded_corners_f.cc new file mode 100644 index 0000000..7db277a --- /dev/null +++ b/ui/gfx/geometry/rounded_corners_f.cc
@@ -0,0 +1,17 @@ +// 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 "ui/gfx/geometry/rounded_corners_f.h" + +#include "base/strings/stringprintf.h" + +namespace gfx { + +std::string RoundedCornersF::ToString() const { + // Print members in the same order of the constructor parameters. + return base::StringPrintf("%f,%f,%f,%f", upper_left_, upper_right_, + lower_right_, lower_left_); +} + +} // namespace gfx
diff --git a/ui/gfx/geometry/rounded_corners_f.h b/ui/gfx/geometry/rounded_corners_f.h new file mode 100644 index 0000000..12582d3b --- /dev/null +++ b/ui/gfx/geometry/rounded_corners_f.h
@@ -0,0 +1,93 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_GFX_GEOMETRY_ROUNDED_CORNERS_F_H_ +#define UI_GFX_GEOMETRY_ROUNDED_CORNERS_F_H_ + +#include <iosfwd> +#include <limits> +#include <string> + +#include "ui/gfx/geometry/geometry_export.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace gfx { + +// Represents the geometry of a region with rounded corners, expressed as four +// corner radii in the order: top-left, top-right, bottom-right, bottom-left. +class GEOMETRY_EXPORT RoundedCornersF { + public: + // Creates an empty RoundedCornersF with all corners having zero radius. + constexpr RoundedCornersF() : RoundedCornersF(0.0f) {} + + // Creates a RoundedCornersF with the same radius for all corners. + constexpr explicit RoundedCornersF(float all) + : RoundedCornersF(all, all, all, all) {} + + // Creates a RoundedCornersF with four different corner radii. + constexpr RoundedCornersF(float upper_left, + float upper_right, + float lower_right, + float lower_left) + : upper_left_(clamp(upper_left)), + upper_right_(clamp(upper_right)), + lower_right_(clamp(lower_right)), + lower_left_(clamp(lower_left)) {} + + constexpr float upper_left() const { return upper_left_; } + constexpr float upper_right() const { return upper_right_; } + constexpr float lower_right() const { return lower_right_; } + constexpr float lower_left() const { return lower_left_; } + + void set_upper_left(float upper_left) { upper_left_ = clamp(upper_left); } + void set_upper_right(float upper_right) { upper_right_ = clamp(upper_right); } + void set_lower_right(float lower_right) { lower_right_ = clamp(lower_right); } + void set_lower_left(float lower_left) { lower_left_ = clamp(lower_left); } + + void Set(float upper_left, + float upper_right, + float lower_right, + float lower_left) { + upper_left_ = clamp(upper_left); + upper_right_ = clamp(upper_right); + lower_right_ = clamp(lower_right); + lower_left_ = clamp(lower_left); + } + + // Returns true if all of the corners are square (zero effective radius). + bool IsEmpty() const { + return upper_left_ == 0.0f && upper_right_ == 0.0f && + lower_right_ == 0.0f && lower_left_ == 0.0f; + } + + bool operator==(const RoundedCornersF& corners) const { + return upper_left_ == corners.upper_left_ && + upper_right_ == corners.upper_right_ && + lower_right_ == corners.lower_right_ && + lower_left_ == corners.lower_left_; + } + + bool operator!=(const RoundedCornersF& corners) const { + return !(*this == corners); + } + + // Returns a string representation of the insets. + std::string ToString() const; + + private: + static constexpr float kTrivial = 8.f * std::numeric_limits<float>::epsilon(); + + // Prevents values which are smaller than zero or negligibly small. + // Uses the same logic as gfx::Size. + static constexpr float clamp(float f) { return f > kTrivial ? f : 0.f; } + + float upper_left_ = 0.0f; + float upper_right_ = 0.0f; + float lower_right_ = 0.0f; + float lower_left_ = 0.0f; +}; + +} // namespace gfx + +#endif // UI_GFX_GEOMETRY_ROUNDED_CORNERS_F_H_
diff --git a/ui/gfx/geometry/rounded_corners_f_unittest.cc b/ui/gfx/geometry/rounded_corners_f_unittest.cc new file mode 100644 index 0000000..5c9bc91b --- /dev/null +++ b/ui/gfx/geometry/rounded_corners_f_unittest.cc
@@ -0,0 +1,158 @@ +// 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 "ui/gfx/geometry/rounded_corners_f.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gfx { + +TEST(RoundedCornersFTest, DefaultConstructor) { + const RoundedCornersF rc; + EXPECT_EQ(0.0f, rc.upper_left()); + EXPECT_EQ(0.0f, rc.upper_right()); + EXPECT_EQ(0.0f, rc.lower_right()); + EXPECT_EQ(0.0f, rc.lower_left()); +} + +TEST(RoundedCornersFTest, FromSingleValue) { + constexpr float kValue = 1.33f; + const RoundedCornersF rc(kValue); + EXPECT_EQ(kValue, rc.upper_left()); + EXPECT_EQ(kValue, rc.upper_right()); + EXPECT_EQ(kValue, rc.lower_right()); + EXPECT_EQ(kValue, rc.lower_left()); +} + +TEST(RoundedCornersFTest, FromFourValues) { + constexpr float kValue1 = 1.33f; + constexpr float kValue2 = 2.66f; + constexpr float kValue3 = 0.1f; + constexpr float kValue4 = 50.0f; + const RoundedCornersF rc(kValue1, kValue2, kValue3, kValue4); + EXPECT_EQ(kValue1, rc.upper_left()); + EXPECT_EQ(kValue2, rc.upper_right()); + EXPECT_EQ(kValue3, rc.lower_right()); + EXPECT_EQ(kValue4, rc.lower_left()); +} + +TEST(RoundedCornersFTest, IsEmpty) { + EXPECT_TRUE(RoundedCornersF().IsEmpty()); + EXPECT_FALSE(RoundedCornersF(1.0f).IsEmpty()); + EXPECT_FALSE(RoundedCornersF(1.0f, 0.0f, 0.0f, 0.0f).IsEmpty()); + EXPECT_FALSE(RoundedCornersF(0.0f, 1.0f, 0.0f, 0.0f).IsEmpty()); + EXPECT_FALSE(RoundedCornersF(0.0f, 0.0f, 1.0f, 0.0f).IsEmpty()); + EXPECT_FALSE(RoundedCornersF(0.0f, 0.0f, 0.0f, 1.0f).IsEmpty()); +} + +TEST(RoundedCornersFTest, Equality) { + constexpr RoundedCornersF kCorners(1.33f, 2.66f, 0.1f, 50.0f); + RoundedCornersF rc = kCorners; + // Using EXPECT_TRUE and EXPECT_FALSE to explicitly test == and != operators + // (rather than EXPECT_EQ, EXPECT_NE). + EXPECT_TRUE(rc == kCorners); + EXPECT_FALSE(rc != kCorners); + rc.set_upper_left(2.0f); + EXPECT_FALSE(rc == kCorners); + EXPECT_TRUE(rc != kCorners); + rc = kCorners; + rc.set_upper_right(2.0f); + EXPECT_FALSE(rc == kCorners); + EXPECT_TRUE(rc != kCorners); + rc = kCorners; + rc.set_lower_left(2.0f); + EXPECT_FALSE(rc == kCorners); + EXPECT_TRUE(rc != kCorners); + rc = kCorners; + rc.set_lower_right(2.0f); + EXPECT_FALSE(rc == kCorners); + EXPECT_TRUE(rc != kCorners); +} + +TEST(RoundedCornersFTest, Set) { + RoundedCornersF rc(1.0f, 2.0f, 3.0f, 4.0f); + rc.Set(4.0f, 3.0f, 2.0f, 1.0f); + EXPECT_EQ(4.0f, rc.upper_left()); + EXPECT_EQ(3.0f, rc.upper_right()); + EXPECT_EQ(2.0f, rc.lower_right()); + EXPECT_EQ(1.0f, rc.lower_left()); +} + +TEST(RoundedCornersFTest, SetProperties) { + RoundedCornersF rc(1.0f, 2.0f, 3.0f, 4.0f); + + rc.set_upper_left(50.0f); + EXPECT_EQ(50.0f, rc.upper_left()); + EXPECT_EQ(2.0f, rc.upper_right()); + EXPECT_EQ(3.0f, rc.lower_right()); + EXPECT_EQ(4.0f, rc.lower_left()); + + rc.set_upper_right(40.0f); + EXPECT_EQ(50.0f, rc.upper_left()); + EXPECT_EQ(40.0f, rc.upper_right()); + EXPECT_EQ(3.0f, rc.lower_right()); + EXPECT_EQ(4.0f, rc.lower_left()); + + rc.set_lower_right(30.0f); + EXPECT_EQ(50.0f, rc.upper_left()); + EXPECT_EQ(40.0f, rc.upper_right()); + EXPECT_EQ(30.0f, rc.lower_right()); + EXPECT_EQ(4.0f, rc.lower_left()); + + rc.set_lower_left(20.0f); + EXPECT_EQ(50.0f, rc.upper_left()); + EXPECT_EQ(40.0f, rc.upper_right()); + EXPECT_EQ(30.0f, rc.lower_right()); + EXPECT_EQ(20.0f, rc.lower_left()); +} + +namespace { + +// Verify that IsEmpty() returns true and that all values are exactly zero. +void VerifyEmptyAndZero(const RoundedCornersF& rc) { + EXPECT_TRUE(rc.IsEmpty()); + EXPECT_EQ(0.0f, rc.upper_left()); + EXPECT_EQ(0.0f, rc.upper_right()); + EXPECT_EQ(0.0f, rc.lower_right()); + EXPECT_EQ(0.0f, rc.lower_left()); +} + +} // namespace + +TEST(RoundedCornersFTest, Epsilon) { + constexpr float kEpsilon = std::numeric_limits<float>::epsilon(); + RoundedCornersF rc(kEpsilon, kEpsilon, kEpsilon, kEpsilon); + VerifyEmptyAndZero(rc); + + rc.set_upper_left(kEpsilon); + VerifyEmptyAndZero(rc); + rc.set_upper_right(kEpsilon); + VerifyEmptyAndZero(rc); + rc.set_lower_right(kEpsilon); + VerifyEmptyAndZero(rc); + rc.set_lower_left(kEpsilon); + VerifyEmptyAndZero(rc); + + rc.Set(kEpsilon, kEpsilon, kEpsilon, kEpsilon); + VerifyEmptyAndZero(rc); +} + +TEST(RoundedCornersFTest, Negative) { + constexpr float kNegative = -0.5f; + RoundedCornersF rc(kNegative, kNegative, kNegative, kNegative); + VerifyEmptyAndZero(rc); + + rc.set_upper_left(kNegative); + VerifyEmptyAndZero(rc); + rc.set_upper_right(kNegative); + VerifyEmptyAndZero(rc); + rc.set_lower_right(kNegative); + VerifyEmptyAndZero(rc); + rc.set_lower_left(kNegative); + VerifyEmptyAndZero(rc); + + rc.Set(kNegative, kNegative, kNegative, kNegative); + VerifyEmptyAndZero(rc); +} + +} // namespace gfx
diff --git a/ui/gfx/rrect_f.h b/ui/gfx/rrect_f.h index cbf042a..c7687c3 100644 --- a/ui/gfx/rrect_f.h +++ b/ui/gfx/rrect_f.h
@@ -10,6 +10,7 @@ #include "third_party/skia/include/core/SkRRect.h" #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/rounded_corners_f.h" #include "ui/gfx/skia_util.h" namespace gfx { @@ -65,6 +66,19 @@ lower_right_y, lower_left_x, lower_left_y) {} + RRectF(const gfx::RectF& rect, const gfx::RoundedCornersF& corners) + : RRectF(rect.x(), + rect.y(), + rect.width(), + rect.height(), + corners.upper_left(), + corners.upper_left(), + corners.upper_right(), + corners.upper_right(), + corners.lower_right(), + corners.lower_right(), + corners.lower_left(), + corners.lower_left()) {} // The rectangular portion of the RRectF, without the corner radii. gfx::RectF rect() const { return gfx::SkRectToRectF(skrrect_.rect()); } @@ -98,6 +112,8 @@ bool IsEmpty() const { return GetType() == Type::kEmpty; } + // Enumeration of the corners of a rectangle in clockwise order. Values match + // SkRRect::Corner. enum class Corner { kUpperLeft = SkRRect::kUpperLeft_Corner, kUpperRight = SkRRect::kUpperRight_Corner,
diff --git a/ui/gfx/rrect_f_unittest.cc b/ui/gfx/rrect_f_unittest.cc index 62a17c85..6bc842c2c 100644 --- a/ui/gfx/rrect_f_unittest.cc +++ b/ui/gfx/rrect_f_unittest.cc
@@ -142,6 +142,25 @@ EXPECT_EQ(b, c); } +TEST(RRectFTest, FromRoundedCornersF) { + constexpr RectF kRect(50.0f, 40.0f); + constexpr RoundedCornersF kCorners(1.5f, 2.5f, 3.5f, 4.5f); + const RRectF rrect_f(kRect, kCorners); + + const auto upper_left = rrect_f.GetCornerRadii(RRectF::Corner::kUpperLeft); + EXPECT_EQ(kCorners.upper_left(), upper_left.x()); + EXPECT_EQ(kCorners.upper_left(), upper_left.y()); + const auto upper_right = rrect_f.GetCornerRadii(RRectF::Corner::kUpperRight); + EXPECT_EQ(kCorners.upper_right(), upper_right.x()); + EXPECT_EQ(kCorners.upper_right(), upper_right.y()); + const auto lower_right = rrect_f.GetCornerRadii(RRectF::Corner::kLowerRight); + EXPECT_EQ(kCorners.lower_right(), lower_right.x()); + EXPECT_EQ(kCorners.lower_right(), lower_right.y()); + const auto lower_left = rrect_f.GetCornerRadii(RRectF::Corner::kLowerLeft); + EXPECT_EQ(kCorners.lower_left(), lower_left.x()); + EXPECT_EQ(kCorners.lower_left(), lower_left.y()); +} + TEST(RRectFTest, ToString) { RRectF a(40, 50, 60, 70, 0); EXPECT_EQ(a.ToString(), "40.000,50.000 60.000x70.000, rectangular");
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc index c73a739..1119acb 100644 --- a/ui/message_center/views/notification_view_md.cc +++ b/ui/message_center/views/notification_view_md.cc
@@ -390,11 +390,13 @@ NotificationInputContainerMD::~NotificationInputContainerMD() = default; -void NotificationInputContainerMD::AnimateBackground( - const ui::LocatedEvent& event) { - if (View::HitTestPoint(event.location())) - AnimateInkDrop(views::InkDropState::ACTION_PENDING, - ui::LocatedEvent::FromIfValid(&event)); +void NotificationInputContainerMD::AnimateBackground(const ui::Event& event) { + // Try to get a located event. This can be NULL if triggered via keyboard. + const ui::LocatedEvent* located_event = ui::LocatedEvent::FromIfValid(&event); + // Use default animation if location is out of bounds. + if (located_event && !View::HitTestPoint(located_event->location())) + located_event = nullptr; + AnimateInkDrop(views::InkDropState::ACTION_PENDING, located_event); } void NotificationInputContainerMD::AddInkDropLayer(ui::Layer* ink_drop_layer) { @@ -742,7 +744,7 @@ ? l10n_util::GetStringUTF16( IDS_MESSAGE_CENTER_NOTIFICATION_INLINE_REPLY_PLACEHOLDER) : *placeholder); - inline_reply_->AnimateBackground(*event.AsLocatedEvent()); + inline_reply_->AnimateBackground(event); inline_reply_->SetVisible(true); action_buttons_row_->SetVisible(false); // RequestFocus() should be called after SetVisible(). @@ -1330,14 +1332,17 @@ const gfx::Point& location = event.AsLocatedEvent()->location(); gfx::Point converted_location(location); View::ConvertPointToTarget(target, this, &converted_location); + + // Use default animation if location is out of bounds. + if (!View::HitTestPoint(converted_location)) { + AnimateInkDrop(views::InkDropState::ACTION_PENDING, nullptr); + return; + } + std::unique_ptr<ui::Event> cloned_event = ui::Event::Clone(event); ui::LocatedEvent* cloned_located_event = cloned_event->AsLocatedEvent(); cloned_located_event->set_location(converted_location); - - if (View::HitTestPoint(event.AsLocatedEvent()->location())) { - AnimateInkDrop(views::InkDropState::ACTION_PENDING, - ui::LocatedEvent::FromIfValid(cloned_located_event)); - } + AnimateInkDrop(views::InkDropState::ACTION_PENDING, cloned_located_event); } void NotificationViewMD::RemoveBackgroundAnimation() {
diff --git a/ui/message_center/views/notification_view_md.h b/ui/message_center/views/notification_view_md.h index ef732236..75954e0 100644 --- a/ui/message_center/views/notification_view_md.h +++ b/ui/message_center/views/notification_view_md.h
@@ -123,7 +123,7 @@ NotificationInputContainerMD(NotificationInputDelegate* delegate); ~NotificationInputContainerMD() override; - void AnimateBackground(const ui::LocatedEvent& event); + void AnimateBackground(const ui::Event& event); // Overridden from views::InkDropHostView: void AddInkDropLayer(ui::Layer* ink_drop_layer) override; @@ -224,6 +224,8 @@ FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestIconSizing); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestInlineReply); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, + TestInlineReplyActivateWithKeyPress); + FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestInlineReplyRemovedByUpdate); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateAddingIcon); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonCountTest);
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc index 37baaff9..3efde26 100644 --- a/ui/message_center/views/notification_view_md_unittest.cc +++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -739,6 +739,30 @@ EXPECT_FALSE(notification_view()->actions_row_->visible()); } +TEST_F(NotificationViewMDTest, TestInlineReplyActivateWithKeyPress) { + std::unique_ptr<Notification> notification = CreateSimpleNotification(); + + std::vector<ButtonInfo> buttons = CreateButtons(2); + buttons[1].placeholder = base::string16(); + notification->set_buttons(buttons); + UpdateNotificationViews(*notification); + widget()->Show(); + + // Action buttons are hidden by collapsed state. + if (!notification_view()->expanded_) + notification_view()->ToggleExpanded(); + + ui::test::EventGenerator generator(GetRootWindow(widget())); + + // Press and release space key to open inline reply text field. + // Note: VKEY_RETURN should work too, but triggers a click on MacOS. + notification_view()->action_buttons_[1]->RequestFocus(); + generator.PressKey(ui::VKEY_SPACE, ui::EF_NONE); + generator.ReleaseKey(ui::VKEY_SPACE, ui::EF_NONE); + + EXPECT_TRUE(notification_view()->inline_reply_->visible()); +} + // Synthetic scroll events are not supported on Mac in the views // test framework. #if defined(OS_MACOSX)
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc index 994396db..91d1d0e1 100644 --- a/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc
@@ -56,8 +56,6 @@ ~Surface() = default; bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) { - DCHECK(!submitted_buffer_); - WaylandBuffer* buffer = GetBuffer(buffer_id); if (!buffer) return false; @@ -69,15 +67,26 @@ // zwp linux dmabuf protocol version, the wl_buffer can be created // immediately without asynchronous wait 2) the wl_buffer can have been // created by this time. - while (!buffer->wl_buffer) { + // + // Another case, which always happen is waiting until the frame callback is + // completed. Thus, wait here when the Wayland compositor fires the frame + // callback. + while (!buffer->wl_buffer || !!wl_frame_callback_) { // If the wl_buffer has been attached, but the wl_buffer still has been // null, it means the Wayland server failed to create the buffer and we // have to fail here. - if (buffer->attached || + if ((buffer->attached && !buffer->wl_buffer) || wl_display_roundtrip(connection_->display()) == -1) return false; } + // Once the BufferRelease is called. The buffer will be released. + DCHECK(buffer->released); + buffer->released = false; + + DCHECK(!submitted_buffer_); + submitted_buffer_ = buffer; + AttachAndDamageBuffer(buffer, damage_region); SetupFrameCallback(); @@ -86,6 +95,13 @@ CommitSurface(); connection_->ScheduleFlush(); + + // If it was the very first frame, the surface has not had a back buffer + // before, and Wayland won't release the front buffer until next buffer is + // attached. Thus, notify about successful submission immediately. + if (!prev_submitted_buffer_) + OnRelease(); + return true; } @@ -96,8 +112,29 @@ } size_t DestroyBuffer(uint32_t buffer_id) { - if (submitted_buffer_ && submitted_buffer_->buffer_id == buffer_id) - submitted_buffer_ = nullptr; + auto* buffer = GetBuffer(buffer_id); + // We ack the submission of a front buffer whenever a previous front back + // becomes the back one and receives a buffer release callback. But the + // following can happen: + // + // Imagine the order: + // the front buffer is buffer 2, then + // Commit[1] + // Destroy[2] + // Commit[3] + // Release[1] + // Ack[3] <= this results in a wrong order of the callbacks. In reality, + // the buffer [1] must have been acked, because the buffer 2 was + // released. + // But if the buffer 2 is destroyed, the buffer release callback never + // comes for that buffer. Thus, if there is a submitted buffer, notify + // the client about successful swap. + if (buffer && !buffer->released && submitted_buffer_) + OnRelease(); + + if (prev_submitted_buffer_ == buffer) + prev_submitted_buffer_ = nullptr; + auto result = buffers_.erase(buffer_id); return result; } @@ -112,12 +149,21 @@ DCHECK(!buffer->wl_buffer); buffer->wl_buffer = std::move(new_buffer); buffer->attached = true; + + if (buffer->wl_buffer) + SetupBufferReleaseListener(buffer); } void ClearState() { buffers_.clear(); wl_frame_callback_.reset(); presentation_feedbacks_ = PresentationFeedbackQueue(); + + wl_surface_attach(window_->surface(), nullptr, 0, 0); + prev_submitted_buffer_ = nullptr; + submitted_buffer_ = nullptr; + + connection_->ScheduleFlush(); } private: @@ -144,15 +190,17 @@ // A wl_buffer backed by a dmabuf created on the GPU side. wl::Object<struct wl_buffer> wl_buffer; + // Tells if the buffer has the wl_buffer attached. This can be used to // identify potential problems, when the Wayland compositor fails to create // wl_buffers. bool attached = false; - gfx::PresentationFeedback feedback; + // Tells if the buffer has already been released aka not busy, and the + // surface can tell the gpu about successful swap. + bool released = true; - bool swapped = false; - bool presented = false; + gfx::PresentationFeedback feedback; DISALLOW_COPY_AND_ASSIGN(WaylandBuffer); }; @@ -172,8 +220,6 @@ surface, pending_damage_region.x(), pending_damage_region.y(), pending_damage_region.width(), pending_damage_region.height()); wl_surface_attach(surface, buffer->wl_buffer.get(), 0, 0); - - submitted_buffer_ = buffer; } void CommitSurface() { wl_surface_commit(window_->surface()); } @@ -204,6 +250,13 @@ presentation_feedbacks_.back().second.get(), &feedback_listener, this); } + void SetupBufferReleaseListener(WaylandBuffer* buffer) { + static struct wl_buffer_listener buffer_listener = { + &Surface::BufferRelease, + }; + wl_buffer_add_listener(buffer->wl_buffer.get(), &buffer_listener, this); + } + WaylandBuffer* GetBuffer(uint32_t buffer_id) { auto it = buffers_.find(buffer_id); return it != buffers_.end() ? it->second.get() : nullptr; @@ -212,38 +265,6 @@ void OnFrameCallback(struct wl_callback* callback) { DCHECK(wl_frame_callback_.get() == callback); wl_frame_callback_.reset(); - - if (!submitted_buffer_) - return; - - // TODO(msisov): remove these once pending buffers logic goes to the - // manager as long as it will always notify about successful swap once the - // surface is committed. - DCHECK(submitted_buffer_); - WaylandBuffer* buffer = submitted_buffer_; - submitted_buffer_ = nullptr; - - buffer->swapped = true; - DCHECK(connection_); - connection_->OnSubmission(window_->GetWidget(), buffer->buffer_id, - gfx::SwapResult::SWAP_ACK); - - // If presentation feedback is not supported, use a fake feedback. This - // literally means there are no presentation feedback callbacks created. - if (!connection_->presentation()) { - DCHECK(presentation_feedbacks_.empty() && !buffer->presented); - OnPresentation( - buffer->buffer_id, - gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), - GetPresentationKindFlags(0))); - } else if (buffer->presented) { - // If the buffer has been presented before the frame callback aka - // completion callback (in the future, release callback is going to be - // used), present the feedback to the GPU. - OnPresentation(buffer->buffer_id, buffer->feedback); - } else { - DCHECK(!presentation_feedbacks_.empty()); - } } // wl_callback_listener @@ -255,21 +276,49 @@ self->OnFrameCallback(callback); } + // wl_buffer_listener + static void BufferRelease(void* data, struct wl_buffer* wl_buffer) { + Surface* self = static_cast<Surface*>(data); + DCHECK(self); + + DCHECK(self->prev_submitted_buffer_->wl_buffer.get() == wl_buffer); + self->prev_submitted_buffer_->released = true; + + // Although, the Wayland compositor has just released the previously + // attached buffer, which became a back buffer, we have to notify the client + // that next buffer has been attach and become the front one. Thus, mark the + // back buffer as released to ensure the DCHECK is not hit, and notify about + // successful submission of the front buffer. + self->OnRelease(); + } + + void OnRelease() { + DCHECK(submitted_buffer_); + auto id = submitted_buffer_->buffer_id; + prev_submitted_buffer_ = submitted_buffer_; + submitted_buffer_ = nullptr; + // We can now complete the latest submission. We had to wait for this + // release because SwapCompletionCallback indicates to the client that the + // previous buffer is available for reuse. + connection_->OnSubmission(window_->GetWidget(), id, + gfx::SwapResult::SWAP_ACK); + + // If presentation feedback is not supported, use a fake feedback. This + // literally means there are no presentation feedback callbacks created. + if (!connection_->presentation()) { + DCHECK(presentation_feedbacks_.empty()); + OnPresentation(id, gfx::PresentationFeedback( + base::TimeTicks::Now(), base::TimeDelta(), + GetPresentationKindFlags(0))); + } + } + void OnPresentation(uint32_t buffer_id, const gfx::PresentationFeedback& feedback) { - WaylandBuffer* buffer = GetBuffer(buffer_id); - DCHECK(buffer); - - if (buffer->swapped) { - DCHECK(connection_); - connection_->OnPresentation(window_->GetWidget(), buffer_id, feedback); - - buffer->swapped = false; - buffer->presented = false; - } else { - buffer->presented = true; - buffer->feedback = feedback; - } + // The order of submission and presentation callbacks is checked on the GPU + // side, but it must never happen, because the Submission is called + // immediately after the buffer is swapped. + connection_->OnPresentation(window_->GetWidget(), buffer_id, feedback); } // wp_presentation_feedback_listener @@ -322,9 +371,6 @@ // Non-owned pointer to the connection. WaylandConnection* const connection_; - // A buffer the surface has committed. Reset on frame callback. - WaylandBuffer* submitted_buffer_ = nullptr; - // A container of created buffers. base::flat_map<uint32_t, std::unique_ptr<WaylandBuffer>> buffers_; @@ -337,6 +383,11 @@ // shown. PresentationFeedbackQueue presentation_feedbacks_; + // Current submitted buffer. + WaylandBuffer* submitted_buffer_ = nullptr; + // Previous submitted buffer. + WaylandBuffer* prev_submitted_buffer_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(Surface); };
diff --git a/ui/surface/BUILD.gn b/ui/surface/BUILD.gn index 2790a3d..3293e4d 100644 --- a/ui/surface/BUILD.gn +++ b/ui/surface/BUILD.gn
@@ -10,7 +10,6 @@ "surface_export.h", "transport_dib.cc", "transport_dib.h", - "transport_dib_win.cc", ] configs += [ "//third_party/khronos:khronos_headers" ] @@ -25,8 +24,4 @@ "//ui/gfx/geometry", "//ui/gl", ] - - if (is_posix) { - sources += [ "transport_dib_posix.cc" ] - } }
diff --git a/ui/surface/transport_dib.cc b/ui/surface/transport_dib.cc index b0f9193..827fcff 100644 --- a/ui/surface/transport_dib.cc +++ b/ui/surface/transport_dib.cc
@@ -4,23 +4,105 @@ #include "ui/surface/transport_dib.h" -#include <limits.h> +#include <stddef.h> +#include <memory> + +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" +#include "base/numerics/checked_math.h" +#include "build/build_config.h" #include "skia/ext/platform_canvas.h" +TransportDIB::TransportDIB(base::UnsafeSharedMemoryRegion region) + : shm_region_(std::move(region)) {} + +TransportDIB::~TransportDIB() = default; + // static -bool TransportDIB::VerifyCanvasSize(int w, int h) { - static const size_t kMaxSize = static_cast<size_t>(INT_MAX); - const size_t one_stride = skia::PlatformCanvasStrideForWidth(1); - const size_t stride = skia::PlatformCanvasStrideForWidth(w); - if (w <= 0 || h <= 0 || static_cast<size_t>(w) > (kMaxSize / one_stride) || - static_cast<size_t>(h) > (kMaxSize / stride)) { +std::unique_ptr<TransportDIB> TransportDIB::Map( + base::UnsafeSharedMemoryRegion region) { + std::unique_ptr<TransportDIB> dib = CreateWithHandle(std::move(region)); + if (!dib->Map()) + return nullptr; + return dib; +} + +// static +std::unique_ptr<TransportDIB> TransportDIB::CreateWithHandle( + base::UnsafeSharedMemoryRegion region) { + return base::WrapUnique(new TransportDIB(std::move(region))); +} + +std::unique_ptr<SkCanvas> TransportDIB::GetPlatformCanvas(int w, + int h, + bool opaque) { + if (!shm_region_.IsValid()) + return nullptr; + +#if defined(OS_WIN) + // This DIB already mapped the file into this process, but PlatformCanvas + // will map it again. + DCHECK(!memory()) << "Mapped file twice in the same process."; + + // We can't check the canvas size before mapping, but it's safe because + // Windows will fail to map the section if the dimensions of the canvas + // are too large. + std::unique_ptr<SkCanvas> canvas = + skia::CreatePlatformCanvasWithSharedSection( + w, h, opaque, shm_region_.GetPlatformHandle(), + skia::RETURN_NULL_ON_FAILURE); + + // Calculate the size for the memory region backing the canvas. + if (canvas) + size_ = skia::PlatformCanvasStrideForWidth(w) * h; + + return canvas; +#else + if ((!memory() && !Map()) || !VerifyCanvasSize(w, h)) + return nullptr; + return skia::CreatePlatformCanvasWithPixels(w, h, opaque, + static_cast<uint8_t*>(memory()), + skia::RETURN_NULL_ON_FAILURE); +#endif +} + +bool TransportDIB::Map() { + if (!shm_region_.IsValid()) + return false; + + if (memory()) + return true; + + shm_mapping_ = shm_region_.Map(); + if (!shm_mapping_.IsValid()) { + PLOG(ERROR) << "Failed to map transport DIB"; return false; } - return (stride * h) <= size_; + size_ = shm_mapping_.size(); + return true; } -base::SharedMemory* TransportDIB::shared_memory() { - return &shared_memory_; +void* TransportDIB::memory() const { + return shm_mapping_.IsValid() ? shm_mapping_.memory() : nullptr; +} + +base::UnsafeSharedMemoryRegion* TransportDIB::shared_memory_region() { + return &shm_region_; +} + +// static +bool TransportDIB::VerifyCanvasSize(int w, int h) { + if (w <= 0 || h <= 0) + return false; + + const size_t stride = skia::PlatformCanvasStrideForWidth(w); + size_t canvas_size; + if (!base::CheckMul(h, stride).AssignIfValid(&canvas_size)) + return false; + + return canvas_size <= size_; }
diff --git a/ui/surface/transport_dib.h b/ui/surface/transport_dib.h index cf86ed3c..759c23b 100644 --- a/ui/surface/transport_dib.h +++ b/ui/surface/transport_dib.h
@@ -6,10 +6,11 @@ #define UI_SURFACE_TRANSPORT_DIB_H_ #include <stddef.h> -#include <stdint.h> +#include <memory> #include "base/macros.h" -#include "base/memory/shared_memory.h" +#include "base/memory/shared_memory_mapping.h" +#include "base/memory/unsafe_shared_memory_region.h" #include "build/build_config.h" #include "ui/surface/surface_export.h" @@ -17,8 +18,6 @@ #include <windows.h> #endif -#include <memory> - class SkCanvas; // ----------------------------------------------------------------------------- @@ -30,20 +29,15 @@ public: ~TransportDIB(); - // A Handle is the type which can be sent over the wire so that the remote - // side can map the transport DIB. - typedef base::SharedMemoryHandle Handle; + // Creates and maps a new TransportDIB with a shared memory region. + // Returns nullptr on failure. + static std::unique_ptr<TransportDIB> Map( + base::UnsafeSharedMemoryRegion region); - // Map the referenced transport DIB. The caller owns the returned object. - // Returns NULL on failure. - static TransportDIB* Map(Handle transport_dib); - - // Create a new |TransportDIB| with a handle to the shared memory. This - // always returns a valid pointer. The DIB is not mapped. - static TransportDIB* CreateWithHandle(Handle handle); - - // Returns true if the handle is valid. - static bool is_valid_handle(Handle dib); + // Creates a new TransportDIB with a shared memory region. This always returns + // a valid pointer. The DIB is not mapped. + static std::unique_ptr<TransportDIB> CreateWithHandle( + base::UnsafeSharedMemoryRegion region); // Returns a canvas using the memory of this TransportDIB. The returned // pointer will be owned by the caller. The bitmap will be of the given size, @@ -70,18 +64,19 @@ // the maximum amount that /could/ be valid. size_t size() const { return size_; } - // Returns a pointer to the SharedMemory object that backs the transport dib. - base::SharedMemory* shared_memory(); + // Returns a pointer to the UnsafeSharedMemoryRegion object that backs the + // transport dib. + base::UnsafeSharedMemoryRegion* shared_memory_region(); private: - TransportDIB(); - // Verifies that the dib can hold a canvas of the requested dimensions. bool VerifyCanvasSize(int w, int h); - explicit TransportDIB(base::SharedMemoryHandle dib); - base::SharedMemory shared_memory_; - size_t size_; // length, in bytes + explicit TransportDIB(base::UnsafeSharedMemoryRegion region); + + base::UnsafeSharedMemoryRegion shm_region_; + base::WritableSharedMemoryMapping shm_mapping_; + size_t size_ = 0; DISALLOW_COPY_AND_ASSIGN(TransportDIB); };
diff --git a/ui/surface/transport_dib_posix.cc b/ui/surface/transport_dib_posix.cc deleted file mode 100644 index 604268c6..0000000 --- a/ui/surface/transport_dib_posix.cc +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright (c) 2012 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 "ui/surface/transport_dib.h" - -#include <stddef.h> -#include <stdint.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <memory> - -#include "base/logging.h" -#include "base/memory/shared_memory.h" -#include "build/build_config.h" -#include "skia/ext/platform_canvas.h" - -TransportDIB::TransportDIB() - : size_(0) { -} - -TransportDIB::TransportDIB(TransportDIB::Handle dib) - : shared_memory_(dib, false /* read write */), - size_(0) { -} - -TransportDIB::~TransportDIB() { -} - -// static -TransportDIB* TransportDIB::Map(Handle handle) { - std::unique_ptr<TransportDIB> dib(CreateWithHandle(handle)); - if (!dib->Map()) - return NULL; - return dib.release(); -} - -// static -TransportDIB* TransportDIB::CreateWithHandle(Handle handle) { - return new TransportDIB(handle); -} - -// static -bool TransportDIB::is_valid_handle(Handle dib) { - return base::SharedMemory::IsHandleValid(dib); -} - -std::unique_ptr<SkCanvas> TransportDIB::GetPlatformCanvas(int w, - int h, - bool opaque) { - if ((!memory() && !Map()) || !VerifyCanvasSize(w, h)) - return NULL; - return skia::CreatePlatformCanvasWithPixels(w, h, opaque, - static_cast<uint8_t*>(memory()), - skia::RETURN_NULL_ON_FAILURE); -} - -bool TransportDIB::Map() { - if (!is_valid_handle(shared_memory_.handle())) - return false; -#if defined(OS_ANDROID) - if (!shared_memory_.Map(0)) - return false; - size_ = shared_memory_.mapped_size(); -#else - if (memory()) - return true; - - size_t size = shared_memory_.handle().GetSize(); - if (!shared_memory_.Map(size)) - return false; - - size_ = size; -#endif - return true; -} - -void* TransportDIB::memory() const { - return shared_memory_.memory(); -}
diff --git a/ui/surface/transport_dib_win.cc b/ui/surface/transport_dib_win.cc deleted file mode 100644 index a1382d31..0000000 --- a/ui/surface/transport_dib_win.cc +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright (c) 2012 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 "ui/surface/transport_dib.h" - -#include <windows.h> -#include <stddef.h> -#include <stdint.h> - -#include <limits> -#include <memory> - -#include "base/logging.h" -#include "base/system/sys_info.h" -#include "skia/ext/platform_canvas.h" - -TransportDIB::TransportDIB() - : size_(0) { -} - -TransportDIB::~TransportDIB() { -} - -TransportDIB::TransportDIB(base::SharedMemoryHandle handle) - : shared_memory_(handle, false /* read write */), size_(0) {} - -// static -TransportDIB* TransportDIB::Map(Handle handle) { - std::unique_ptr<TransportDIB> dib(CreateWithHandle(handle)); - if (!dib->Map()) - return NULL; - return dib.release(); -} - -// static -TransportDIB* TransportDIB::CreateWithHandle(Handle handle) { - return new TransportDIB(handle); -} - -// static -bool TransportDIB::is_valid_handle(Handle dib) { - return dib.IsValid(); -} - -std::unique_ptr<SkCanvas> TransportDIB::GetPlatformCanvas(int w, - int h, - bool opaque) { - // This DIB already mapped the file into this process, but PlatformCanvas - // will map it again. - DCHECK(!memory()) << "Mapped file twice in the same process."; - - // We can't check the canvas size before mapping, but it's safe because - // Windows will fail to map the section if the dimensions of the canvas - // are too large. - std::unique_ptr<SkCanvas> canvas = - skia::CreatePlatformCanvasWithSharedSection( - w, h, opaque, shared_memory_.handle().GetHandle(), - skia::RETURN_NULL_ON_FAILURE); - - // Calculate the size for the memory region backing the canvas. - if (canvas) - size_ = skia::PlatformCanvasStrideForWidth(w) * h; - - return canvas; -} - -bool TransportDIB::Map() { - if (!is_valid_handle(shared_memory_.handle())) - return false; - if (memory()) - return true; - - if (!shared_memory_.Map(0 /* map whole shared memory segment */)) { - LOG(ERROR) << "Failed to map transport DIB" - << " handle:" << shared_memory_.handle().GetHandle() - << " error:" << ::GetLastError(); - return false; - } - - size_ = shared_memory_.mapped_size(); - return true; -} - -void* TransportDIB::memory() const { - return shared_memory_.memory(); -}
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc index 46eac18..56ac895 100644 --- a/ui/views/controls/button/md_text_button.cc +++ b/ui/views/controls/button/md_text_button.cc
@@ -26,19 +26,21 @@ namespace views { // static -LabelButton* MdTextButton::CreateSecondaryUiButton(ButtonListener* listener, - const base::string16& text) { - return MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD); +std::unique_ptr<LabelButton> MdTextButton::CreateSecondaryUiButton( + ButtonListener* listener, + const base::string16& text) { + return base::WrapUnique( + MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD)); } // static -LabelButton* MdTextButton::CreateSecondaryUiBlueButton( +std::unique_ptr<LabelButton> MdTextButton::CreateSecondaryUiBlueButton( ButtonListener* listener, const base::string16& text) { - MdTextButton* md_button = + auto* md_button = MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD); md_button->SetProminent(true); - return md_button; + return base::WrapUnique(md_button); } // static
diff --git a/ui/views/controls/button/md_text_button.h b/ui/views/controls/button/md_text_button.h index 35f4996..7d5c899 100644 --- a/ui/views/controls/button/md_text_button.h +++ b/ui/views/controls/button/md_text_button.h
@@ -19,10 +19,12 @@ public: // As above, but only creates an MdTextButton if MD is enabled in the // secondary UI (as opposed to just "top chrome"/"primary" UI). - static LabelButton* CreateSecondaryUiButton(ButtonListener* listener, - const base::string16& text); - static LabelButton* CreateSecondaryUiBlueButton(ButtonListener* listener, - const base::string16& text); + static std::unique_ptr<LabelButton> CreateSecondaryUiButton( + ButtonListener* listener, + const base::string16& text); + static std::unique_ptr<LabelButton> CreateSecondaryUiBlueButton( + ButtonListener* listener, + const base::string16& text); static MdTextButton* Create(ButtonListener* listener, const base::string16& text, int button_context = style::CONTEXT_BUTTON_MD);
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc index 1461fef..ed3a0de 100644 --- a/ui/views/controls/editable_combobox/editable_combobox.cc +++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -43,6 +43,7 @@ #include "ui/views/controls/button/button.h" #include "ui/views/controls/combobox/combobox_util.h" #include "ui/views/controls/editable_combobox/editable_combobox_listener.h" +#include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/menu_types.h" #include "ui/views/controls/textfield/textfield.h" @@ -156,6 +157,10 @@ void EnableUpdateItemsShown() { update_items_shown_enabled_ = true; } + bool UseCheckmarks() const { + return MenuConfig::instance().check_selected_combobox_item; + } + ////////////////////////////////////////////////////////////////////////////// // Overridden from ComboboxModelObserver: void OnComboboxModelChanged(ui::ComboboxModel* model) override { @@ -182,7 +187,9 @@ private: bool HasIcons() const override { return false; } - ItemType GetTypeAt(int index) const override { return TYPE_COMMAND; } + ItemType GetTypeAt(int index) const override { + return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND; + } ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override { return ui::NORMAL_SEPARATOR; @@ -204,7 +211,9 @@ return false; } - bool IsItemCheckedAt(int index) const override { return false; } + bool IsItemCheckedAt(int index) const override { + return UseCheckmarks() && items_shown_[index] == owner_->GetText(); + } int GetGroupIdAt(int index) const override { return -1; }
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc index 70d517c..eb28c28 100644 --- a/ui/views/examples/dialog_example.cc +++ b/ui/views/examples/dialog_example.cc
@@ -59,12 +59,14 @@ return parent_->title_->text(); } + // TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View> // DialogDelegate: View* CreateExtraView() override { if (!parent_->has_extra_button_->checked()) return nullptr; - return MdTextButton::CreateSecondaryUiButton( + auto view = MdTextButton::CreateSecondaryUiButton( nullptr, parent_->extra_button_label_->text()); + return view.release(); } bool Cancel() override { return parent_->AllowDialogClose(false); } @@ -174,8 +176,11 @@ layout->StartRowWithPadding( kFixed, kButtonsColumnId, kFixed, provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL)); + + // TODO(crbug.com/943560): Avoid this release(). show_ = - MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show")); + MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Show")) + .release(); layout->AddView(show_); // Grow the dialog a bit when this example is first selected, so it all fits.
diff --git a/ui/views/examples/layout_example_base.cc b/ui/views/examples/layout_example_base.cc index b098285..c763fea 100644 --- a/ui/views/examples/layout_example_base.cc +++ b/ui/views/examples/layout_example_base.cc
@@ -260,11 +260,11 @@ int vertical_pos = kLayoutExampleVerticalSpacing; int horizontal_pos = kLayoutExampleLeftPadding; - add_button_ = + auto add_button = MdTextButton::CreateSecondaryUiButton(this, base::ASCIIToUTF16("Add")); - add_button_->SetPosition(gfx::Point(horizontal_pos, vertical_pos)); - add_button_->SizeToPreferredSize(); - control_panel_->AddChildView(add_button_); + add_button->SetPosition(gfx::Point(horizontal_pos, vertical_pos)); + add_button->SizeToPreferredSize(); + add_button_ = control_panel_->AddChildView(std::move(add_button)); horizontal_pos += add_button_->width() + kLayoutExampleVerticalSpacing; for (size_t i = 0; i < base::size(child_panel_size_); ++i) { child_panel_size_[i] =
diff --git a/ui/views/examples/widget_example.cc b/ui/views/examples/widget_example.cc index be758d6..891c4e5e 100644 --- a/ui/views/examples/widget_example.cc +++ b/ui/views/examples/widget_example.cc
@@ -56,9 +56,11 @@ return ASCIIToUTF16("Dialog Widget Example"); } +// TODO(crbug.com/961660): CreateExtraView should return std::unique_ptr<View> View* WidgetDialogExample::CreateExtraView() { - return MdTextButton::CreateSecondaryUiButton(nullptr, - ASCIIToUTF16("Extra button!")); + auto view = MdTextButton::CreateSecondaryUiButton( + nullptr, ASCIIToUTF16("Extra button!")); + return view.release(); } View* WidgetDialogExample::CreateFootnoteView() {
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc index f4fc584..da7811a 100644 --- a/ui/views/window/dialog_client_view.cc +++ b/ui/views/window/dialog_client_view.cc
@@ -296,7 +296,7 @@ // MdTextButton, make it so. Note that some overrides may not always update // the title (they should). See http://crbug.com/697303 . const base::string16 title = delegate->GetDialogButtonLabel(type); - LabelButton* button = nullptr; + std::unique_ptr<LabelButton> button = nullptr; const bool is_default = delegate->GetDefaultDialogButton() == type && (type != ui::DIALOG_BUTTON_CANCEL || @@ -311,7 +311,7 @@ button->SetGroup(kButtonGroup); - *member = button; + *member = button.release(); } delegate->UpdateButton(*member, type);