| // 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/vr/ui_scene_creator.h" |
| |
| #include "base/macros.h" |
| #include "base/numerics/ranges.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/version.h" |
| #include "chrome/browser/vr/elements/button.h" |
| #include "chrome/browser/vr/elements/content_element.h" |
| #include "chrome/browser/vr/elements/disc_button.h" |
| #include "chrome/browser/vr/elements/indicator_spec.h" |
| #include "chrome/browser/vr/elements/rect.h" |
| #include "chrome/browser/vr/elements/repositioner.h" |
| #include "chrome/browser/vr/elements/ui_element.h" |
| #include "chrome/browser/vr/elements/ui_element_name.h" |
| #include "chrome/browser/vr/elements/vector_icon.h" |
| #include "chrome/browser/vr/model/assets.h" |
| #include "chrome/browser/vr/model/model.h" |
| #include "chrome/browser/vr/speech_recognizer.h" |
| #include "chrome/browser/vr/target_property.h" |
| #include "chrome/browser/vr/test/animation_utils.h" |
| #include "chrome/browser/vr/test/constants.h" |
| #include "chrome/browser/vr/test/mock_ui_browser_interface.h" |
| #include "chrome/browser/vr/test/ui_test.h" |
| #include "chrome/browser/vr/ui_renderer.h" |
| #include "chrome/browser/vr/ui_scene.h" |
| #include "chrome/browser/vr/ui_scene_constants.h" |
| #include "components/omnibox/browser/autocomplete_match.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace vr { |
| |
| namespace { |
| const std::set<UiElementName> kFloorCeilingBackgroundElements = { |
| kSolidBackground, kCeiling, kFloor, |
| }; |
| const std::set<UiElementName> kElementsVisibleInBrowsing = { |
| kSolidBackground, |
| kCeiling, |
| kFloor, |
| kContentFrame, |
| kContentFrameHitPlane, |
| kContentQuad, |
| kBackplane, |
| kUrlBar, |
| kUrlBarBackButton, |
| kUrlBarLeftSeparator, |
| kUrlBarOriginRegion, |
| kUrlBarSecurityButton, |
| kUrlBarUrlText, |
| kUrlBarRightSeparator, |
| kUrlBarOverflowButton, |
| kController, |
| kLaser, |
| kControllerTouchpadButton, |
| kControllerAppButton, |
| kControllerHomeButton, |
| kControllerBatteryDot0, |
| kControllerBatteryDot1, |
| kControllerBatteryDot2, |
| kControllerBatteryDot3, |
| kControllerBatteryDot4, |
| kIndicatorBackplane, |
| }; |
| const std::set<UiElementName> kElementsVisibleWithExitWarning = { |
| kScreenDimmer, kExitWarningBackground, kExitWarningText}; |
| const std::set<UiElementName> kElementsVisibleWithVoiceSearch = { |
| kSpeechRecognitionListening, kSpeechRecognitionMicrophoneIcon, |
| kSpeechRecognitionListeningCloseButton, kSpeechRecognitionCircle, |
| kSpeechRecognitionListeningGrowingCircle}; |
| const std::set<UiElementName> kElementsVisibleWithVoiceSearchResult = { |
| kSpeechRecognitionResult, kSpeechRecognitionCircle, |
| kSpeechRecognitionMicrophoneIcon, kSpeechRecognitionResultBackplane}; |
| |
| constexpr float kTolerance = 1e-5f; |
| constexpr float kSmallDelaySeconds = 0.1f; |
| constexpr float kAnimationTimeMs = 300; |
| |
| MATCHER_P2(SizeFsAreApproximatelyEqual, other, tolerance, "") { |
| return base::IsApproximatelyEqual(arg.width(), other.width(), tolerance) && |
| base::IsApproximatelyEqual(arg.height(), other.height(), tolerance); |
| } |
| |
| void VerifyButtonColor(DiscButton* button, |
| SkColor foreground_color, |
| SkColor background_color, |
| const std::string& trace_name) { |
| SCOPED_TRACE(trace_name); |
| EXPECT_EQ(button->foreground()->GetColor(), foreground_color); |
| EXPECT_EQ(button->background()->edge_color(), background_color); |
| EXPECT_EQ(button->background()->center_color(), background_color); |
| } |
| |
| void VerifyNoHitTestableElementInSubtree(UiElement* element) { |
| EXPECT_FALSE(element->IsHitTestable()); |
| for (auto& child : element->children()) |
| VerifyNoHitTestableElementInSubtree(child.get()); |
| } |
| |
| } // namespace |
| |
| TEST_F(UiTest, WebVrToastStateTransitions) { |
| // Tests toast not showing when directly entering VR though WebVR |
| // presentation. |
| CreateScene(kInWebVr); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| CreateScene(kNotInWebVr); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| browser_ui->SetWebVrMode(true); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| browser_ui->SetCapturingState(CapturingStateModel(), CapturingStateModel(), |
| CapturingStateModel()); |
| EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| browser_ui->SetWebVrMode(false); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| browser_ui->SetWebVrMode(true); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| browser_ui->SetWebVrMode(false); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| } |
| |
| TEST_F(UiTest, WebVrToastTransience) { |
| CreateScene(kNotInWebVr); |
| |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| browser_ui->SetWebVrMode(true); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| browser_ui->SetCapturingState(CapturingStateModel(), CapturingStateModel(), |
| CapturingStateModel()); |
| EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast)); |
| EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds + kSmallDelaySeconds)); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| browser_ui->SetWebVrMode(false); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| } |
| |
| TEST_F(UiTest, PlatformToast) { |
| CreateScene(kNotInWebVr); |
| EXPECT_FALSE(IsVisible(kPlatformToast)); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| // show and hide toast after a timeout. |
| browser_ui->ShowPlatformToast(base::UTF8ToUTF16("Downloading")); |
| EXPECT_TRUE(IsVisible(kPlatformToast)); |
| EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds + kSmallDelaySeconds)); |
| EXPECT_FALSE(IsVisible(kPlatformToast)); |
| |
| // toast can be cancelled. |
| browser_ui->ShowPlatformToast(base::UTF8ToUTF16("Downloading")); |
| EXPECT_TRUE(IsVisible(kPlatformToast)); |
| browser_ui->CancelPlatformToast(); |
| EXPECT_FALSE(IsVisible(kPlatformToast)); |
| |
| // toast can refresh visible timeout. |
| browser_ui->ShowPlatformToast(base::UTF8ToUTF16("Downloading")); |
| EXPECT_TRUE(RunForSeconds(kSmallDelaySeconds)); |
| browser_ui->ShowPlatformToast(base::UTF8ToUTF16("Downloading")); |
| EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds - kSmallDelaySeconds)); |
| EXPECT_TRUE(IsVisible(kPlatformToast)); |
| EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds + kSmallDelaySeconds)); |
| EXPECT_FALSE(IsVisible(kPlatformToast)); |
| } |
| |
| TEST_F(UiTest, CaptureToasts) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| for (auto& spec : GetIndicatorSpecs()) { |
| for (int i = 0; i < 3; ++i) { |
| browser_ui->SetWebVrMode(true); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| |
| CapturingStateModel active_capturing; |
| CapturingStateModel background_capturing; |
| CapturingStateModel potential_capturing; |
| active_capturing.*spec.signal = i == 0; |
| // High accuracy location cannot be used in a background tab. |
| background_capturing.*spec.signal = |
| i == 1 && spec.name != kLocationAccessIndicator; |
| potential_capturing.*spec.signal = true; |
| |
| browser_ui->SetCapturingState(active_capturing, background_capturing, |
| potential_capturing); |
| EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast)); |
| EXPECT_TRUE(IsVisible(spec.webvr_name)); |
| EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds + kSmallDelaySeconds)); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| |
| browser_ui->SetWebVrMode(false); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| EXPECT_FALSE(IsVisible(spec.webvr_name)); |
| } |
| } |
| } |
| |
| TEST_F(UiTest, CloseButtonVisibleInFullscreen) { |
| // Button should not be visible when not in fullscreen. |
| CreateScene(kNotInWebVr); |
| EXPECT_FALSE(IsVisible(kCloseButton)); |
| |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| // Button should be visible in fullscreen and hidden when leaving fullscreen. |
| browser_ui->SetFullscreen(true); |
| EXPECT_TRUE(IsVisible(kCloseButton)); |
| browser_ui->SetFullscreen(false); |
| EXPECT_FALSE(IsVisible(kCloseButton)); |
| |
| // Button should not be visible when in WebVR. |
| CreateScene(kInWebVr); |
| browser_ui = ui_->GetBrowserUiWeakPtr(); |
| EXPECT_FALSE(IsVisible(kCloseButton)); |
| browser_ui->SetWebVrMode(false); |
| EXPECT_FALSE(IsVisible(kCloseButton)); |
| } |
| |
| TEST_F(UiTest, UiUpdatesForIncognito) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| // Hold onto the background color to make sure it changes. |
| SkColor initial_background = SK_ColorBLACK; |
| GetBackgroundColor(&initial_background); |
| EXPECT_EQ( |
| ColorScheme::GetColorScheme(ColorScheme::kModeNormal).world_background, |
| initial_background); |
| browser_ui->SetFullscreen(true); |
| |
| // Make sure background has changed for fullscreen. |
| SkColor fullscreen_background = SK_ColorBLACK; |
| GetBackgroundColor(&fullscreen_background); |
| EXPECT_EQ(ColorScheme::GetColorScheme(ColorScheme::kModeFullscreen) |
| .world_background, |
| fullscreen_background); |
| |
| model_->incognito = true; |
| // Make sure background remains fullscreen colored. |
| SkColor incognito_background = SK_ColorBLACK; |
| GetBackgroundColor(&incognito_background); |
| EXPECT_EQ(ColorScheme::GetColorScheme(ColorScheme::kModeFullscreen) |
| .world_background, |
| incognito_background); |
| |
| model_->incognito = false; |
| SkColor no_longer_incognito_background = SK_ColorBLACK; |
| GetBackgroundColor(&no_longer_incognito_background); |
| EXPECT_EQ(fullscreen_background, no_longer_incognito_background); |
| |
| browser_ui->SetFullscreen(false); |
| SkColor no_longer_fullscreen_background = SK_ColorBLACK; |
| GetBackgroundColor(&no_longer_fullscreen_background); |
| EXPECT_EQ(initial_background, no_longer_fullscreen_background); |
| |
| // Incognito, but not fullscreen, should show incognito colors. |
| model_->incognito = true; |
| SkColor incognito_again_background = SK_ColorBLACK; |
| GetBackgroundColor(&incognito_again_background); |
| EXPECT_EQ( |
| ColorScheme::GetColorScheme(ColorScheme::kModeIncognito).world_background, |
| incognito_again_background); |
| |
| model_->incognito = false; |
| SkColor no_longer_incognito_again_background = SK_ColorBLACK; |
| GetBackgroundColor(&no_longer_incognito_again_background); |
| EXPECT_EQ(initial_background, no_longer_incognito_again_background); |
| } |
| |
| TEST_F(UiTest, VoiceSearchHiddenInIncognito) { |
| CreateScene(kNotInWebVr); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_TRUE(IsVisible(kOmniboxVoiceSearchButton)); |
| |
| model_->incognito = true; |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_FALSE(IsVisible(kOmniboxVoiceSearchButton)); |
| } |
| |
| TEST_F(UiTest, VoiceSearchHiddenWhenCantAskForPermission) { |
| CreateScene(kNotInWebVr); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| model_->speech.has_or_can_request_record_audio_permission = true; |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_TRUE(IsVisible(kOmniboxVoiceSearchButton)); |
| |
| model_->speech.has_or_can_request_record_audio_permission = false; |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_FALSE(IsVisible(kOmniboxVoiceSearchButton)); |
| } |
| |
| TEST_F(UiTest, VoiceSearchHiddenWhenContentCapturingAudio) { |
| CreateScene(kNotInWebVr); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| model_->speech.has_or_can_request_record_audio_permission = true; |
| model_->active_capturing.audio_capture_enabled = false; |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_TRUE(IsVisible(kOmniboxVoiceSearchButton)); |
| |
| model_->active_capturing.audio_capture_enabled = true; |
| EXPECT_TRUE(OnBeginFrame()); |
| EXPECT_FALSE(IsVisible(kOmniboxVoiceSearchButton)); |
| } |
| |
| TEST_F(UiTest, UiModeWebVr) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| |
| browser_ui->SetWebVrMode(true); |
| EXPECT_EQ(model_->ui_modes.size(), 2u); |
| EXPECT_EQ(model_->ui_modes[1], kModeWebVr); |
| EXPECT_EQ(model_->ui_modes[0], kModeBrowsing); |
| VerifyOnlyElementsVisible("WebVR", {kWebVrBackground}); |
| |
| browser_ui->SetWebVrMode(false); |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| VerifyOnlyElementsVisible("Browsing after WebVR", kElementsVisibleInBrowsing); |
| } |
| |
| TEST_F(UiTest, UiModeOmniboxEditing) { |
| CreateScene(kNotInWebVr); |
| |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| EXPECT_EQ(NumVisibleInTree(kOmniboxRoot), 0); |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| OnBeginFrame(); |
| EXPECT_EQ(model_->ui_modes.size(), 2u); |
| EXPECT_EQ(model_->ui_modes[1], kModeEditingOmnibox); |
| EXPECT_EQ(model_->ui_modes[0], kModeBrowsing); |
| EXPECT_GT(NumVisibleInTree(kOmniboxRoot), 0); |
| |
| model_->pop_mode(kModeEditingOmnibox); |
| OnBeginFrame(); |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| VerifyOnlyElementsVisible("Browsing", kElementsVisibleInBrowsing); |
| } |
| |
| TEST_F(UiTest, UiModeVoiceSearchFromOmnibox) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| EXPECT_FALSE(IsVisible(kOmniboxBackground)); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| EXPECT_FALSE(IsVisible(kContentQuad)); |
| EXPECT_TRUE(IsVisible(kOmniboxBackground)); |
| EXPECT_EQ(model_->ui_modes.size(), 2u); |
| EXPECT_EQ(model_->ui_modes[1], kModeEditingOmnibox); |
| EXPECT_EQ(model_->ui_modes[0], kModeBrowsing); |
| |
| browser_ui->SetSpeechRecognitionEnabled(true); |
| EXPECT_FALSE(IsVisible(kOmniboxBackground)); |
| EXPECT_EQ(model_->ui_modes.size(), 4u); |
| EXPECT_EQ(model_->ui_modes[2], kModeVoiceSearch); |
| EXPECT_EQ(model_->ui_modes[1], kModeEditingOmnibox); |
| EXPECT_EQ(model_->ui_modes[0], kModeBrowsing); |
| VerifyVisibility(kElementsVisibleWithVoiceSearch, true); |
| |
| browser_ui->SetSpeechRecognitionEnabled(false); |
| EXPECT_TRUE(IsVisible(kOmniboxBackground)); |
| EXPECT_EQ(model_->ui_modes.size(), 2u); |
| EXPECT_EQ(model_->ui_modes[1], kModeEditingOmnibox); |
| EXPECT_EQ(model_->ui_modes[0], kModeBrowsing); |
| |
| model_->pop_mode(kModeEditingOmnibox); |
| EXPECT_EQ(model_->ui_modes.size(), 1u); |
| EXPECT_EQ(model_->ui_modes.back(), kModeBrowsing); |
| OnBeginFrame(); |
| EXPECT_FALSE(IsVisible(kOmniboxBackground)); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| } |
| |
| TEST_F(UiTest, HostedUiInWebVr) { |
| CreateScene(kInWebVr); |
| VerifyVisibility({kWebVrHostedUi, kWebVrFloor}, false); |
| |
| ui_->SetAlertDialogEnabled(true, nullptr, 0, 0); |
| OnBeginFrame(); |
| VerifyVisibility({kWebVrHostedUi, kWebVrBackground, kWebVrFloor}, true); |
| |
| ui_->SetAlertDialogEnabled(false, nullptr, 0, 0); |
| OnBeginFrame(); |
| VerifyVisibility({kWebVrHostedUi, kWebVrFloor}, false); |
| } |
| |
| TEST_F(UiTest, UiUpdatesForFullscreenChanges) { |
| auto visible_in_fullscreen = kFloorCeilingBackgroundElements; |
| visible_in_fullscreen.insert(kContentQuad); |
| visible_in_fullscreen.insert(kBackplane); |
| visible_in_fullscreen.insert(kCloseButton); |
| visible_in_fullscreen.insert(kController); |
| visible_in_fullscreen.insert(kControllerTouchpadButton); |
| visible_in_fullscreen.insert(kControllerAppButton); |
| visible_in_fullscreen.insert(kControllerHomeButton); |
| visible_in_fullscreen.insert(kControllerBatteryDot0); |
| visible_in_fullscreen.insert(kControllerBatteryDot1); |
| visible_in_fullscreen.insert(kControllerBatteryDot2); |
| visible_in_fullscreen.insert(kControllerBatteryDot3); |
| visible_in_fullscreen.insert(kControllerBatteryDot4); |
| visible_in_fullscreen.insert(kLaser); |
| visible_in_fullscreen.insert(kContentFrame); |
| visible_in_fullscreen.insert(kContentFrameHitPlane); |
| |
| CreateScene(kNotInWebVr); |
| |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| // Hold onto the background color to make sure it changes. |
| SkColor initial_background = SK_ColorBLACK; |
| GetBackgroundColor(&initial_background); |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| UiElement* content_quad = scene_->GetUiElementByName(kContentQuad); |
| UiElement* content_group = |
| scene_->GetUiElementByName(k2dBrowsingContentGroup); |
| gfx::SizeF initial_content_size = content_quad->size(); |
| gfx::Transform initial_position = content_group->LocalTransform(); |
| |
| // In fullscreen mode, content elements should be visible, control elements |
| // should be hidden. |
| browser_ui->SetFullscreen(true); |
| VerifyOnlyElementsVisible("In fullscreen", visible_in_fullscreen); |
| // Make sure background has changed for fullscreen. |
| SkColor fullscreen_background = SK_ColorBLACK; |
| GetBackgroundColor(&fullscreen_background); |
| EXPECT_EQ(ColorScheme::GetColorScheme(ColorScheme::kModeFullscreen) |
| .world_background, |
| fullscreen_background); |
| // Should have started transition. |
| EXPECT_TRUE(IsAnimating(content_quad, {BOUNDS})); |
| EXPECT_TRUE(IsAnimating(content_group, {TRANSFORM})); |
| // Finish the transition. |
| EXPECT_TRUE(RunForMs(1000)); |
| EXPECT_FALSE(IsAnimating(content_quad, {BOUNDS})); |
| EXPECT_FALSE(IsAnimating(content_group, {TRANSFORM})); |
| EXPECT_NE(initial_content_size, content_quad->size()); |
| EXPECT_NE(initial_position, content_group->LocalTransform()); |
| |
| // Everything should return to original state after leaving fullscreen. |
| browser_ui->SetFullscreen(false); |
| VerifyOnlyElementsVisible("Restore initial", kElementsVisibleInBrowsing); |
| SkColor no_longer_fullscreen_background = SK_ColorBLACK; |
| GetBackgroundColor(&no_longer_fullscreen_background); |
| EXPECT_EQ( |
| ColorScheme::GetColorScheme(ColorScheme::kModeNormal).world_background, |
| no_longer_fullscreen_background); |
| // Should have started transition. |
| EXPECT_TRUE(IsAnimating(content_quad, {BOUNDS})); |
| EXPECT_TRUE(IsAnimating(content_group, {TRANSFORM})); |
| // Finish the transition. |
| EXPECT_TRUE(RunForMs(1000)); |
| EXPECT_FALSE(IsAnimating(content_quad, {BOUNDS})); |
| EXPECT_FALSE(IsAnimating(content_group, {TRANSFORM})); |
| EXPECT_EQ(initial_content_size, content_quad->size()); |
| EXPECT_EQ(initial_position, content_group->LocalTransform()); |
| } |
| |
| TEST_F(UiTest, SecurityIconClickShouldShowPageInfo) { |
| CreateScene(kNotInWebVr); |
| |
| // Initial state. |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| |
| EXPECT_CALL(*browser_, ShowPageInfo); |
| auto* security_icon = scene_->GetUiElementByName(kUrlBarSecurityButton); |
| ClickElement(security_icon); |
| } |
| |
| TEST_F(UiTest, ClickingOmniboxTriggersUnsupportedMode) { |
| UiInitialState state; |
| state.needs_keyboard_update = true; |
| CreateScene(state); |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| |
| // Clicking the omnibox should show the update prompt. |
| auto* omnibox = scene_->GetUiElementByName(kUrlBarOriginRegion); |
| EXPECT_CALL(*browser_, |
| OnUnsupportedMode(UiUnsupportedMode::kNeedsKeyboardUpdate)); |
| ClickElement(omnibox); |
| ui_->GetBrowserUiWeakPtr()->ShowExitVrPrompt( |
| UiUnsupportedMode::kNeedsKeyboardUpdate); |
| OnBeginFrame(); |
| EXPECT_EQ(model_->active_modal_prompt_type, |
| ModalPromptType::kModalPromptTypeUpdateKeyboard); |
| EXPECT_TRUE(scene_->GetUiElementByName(kExitPrompt)->IsVisible()); |
| } |
| |
| TEST_F(UiTest, WebInputEditingTriggersUnsupportedMode) { |
| UiInitialState state; |
| state.needs_keyboard_update = true; |
| CreateScene(state); |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| // A call to show the keyboard should show the update prompt. |
| EXPECT_CALL(*browser_, |
| OnUnsupportedMode(UiUnsupportedMode::kNeedsKeyboardUpdate)); |
| browser_ui->ShowSoftInput(true); |
| browser_ui->ShowExitVrPrompt(UiUnsupportedMode::kNeedsKeyboardUpdate); |
| OnBeginFrame(); |
| EXPECT_EQ(model_->active_modal_prompt_type, |
| ModalPromptType::kModalPromptTypeUpdateKeyboard); |
| EXPECT_TRUE(scene_->GetUiElementByName(kExitPrompt)->IsVisible()); |
| } |
| |
| TEST_F(UiTest, ExitWebInputEditingOnMenuButtonClick) { |
| CreateScene(kNotInWebVr); |
| EXPECT_FALSE(scene_->GetUiElementByName(kKeyboard)->IsVisible()); |
| ui_->GetBrowserUiWeakPtr()->ShowSoftInput(true); |
| OnBeginFrame(); |
| EXPECT_TRUE(scene_->GetUiElementByName(kKeyboard)->IsVisible()); |
| InputEventList events; |
| events.push_back( |
| std::make_unique<InputEvent>(InputEvent::kMenuButtonClicked)); |
| ui_->HandleMenuButtonEvents(&events); |
| base::RunLoop().RunUntilIdle(); |
| OnBeginFrame(); |
| // Clicking menu button should hide the keyboard. |
| EXPECT_FALSE(scene_->GetUiElementByName(kKeyboard)->IsVisible()); |
| } |
| |
| TEST_F(UiTest, ShowAndHideExitPrompt) { |
| CreateScene(kNotInWebVr); |
| |
| model_->active_modal_prompt_type = kModalPromptTypeExitVRForSiteInfo; |
| model_->push_mode(kModeModalPrompt); |
| EXPECT_TRUE(IsVisible(kExitPrompt)); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| |
| model_->active_modal_prompt_type = kModalPromptTypeNone; |
| model_->pop_mode(kModeModalPrompt); |
| EXPECT_FALSE(IsVisible(kExitPrompt)); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| } |
| |
| TEST_F(UiTest, PrimaryButtonClickTriggersOnExitPrompt) { |
| CreateScene(kNotInWebVr); |
| |
| // Initial state. |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| ui_->GetBrowserUiWeakPtr()->ShowExitVrPrompt( |
| UiUnsupportedMode::kUnhandledPageInfo); |
| OnBeginFrame(); |
| |
| // Click on 'EXIT VR' should trigger UI browser interface and close prompt. |
| EXPECT_CALL(*browser_, |
| OnExitVrPromptResult(ExitVrPromptChoice::CHOICE_EXIT, |
| UiUnsupportedMode::kUnhandledPageInfo)); |
| auto* prompt = scene_->GetUiElementByName(kExitPrompt); |
| auto* button = prompt->GetDescendantByType(kTypePromptPrimaryButton); |
| ClickElement(button); |
| VerifyOnlyElementsVisible("Prompt cleared", kElementsVisibleInBrowsing); |
| } |
| |
| TEST_F(UiTest, SecondaryButtonClickTriggersOnExitPrompt) { |
| CreateScene(kNotInWebVr); |
| |
| // Initial state. |
| VerifyOnlyElementsVisible("Initial", kElementsVisibleInBrowsing); |
| model_->active_modal_prompt_type = kModalPromptTypeExitVRForSiteInfo; |
| OnBeginFrame(); |
| |
| // Click on 'BACK' should trigger UI browser interface and close prompt. |
| EXPECT_CALL(*browser_, |
| OnExitVrPromptResult(ExitVrPromptChoice::CHOICE_STAY, |
| UiUnsupportedMode::kUnhandledPageInfo)); |
| |
| auto* prompt = scene_->GetUiElementByName(kExitPrompt); |
| auto* button = prompt->GetDescendantByType(kTypePromptSecondaryButton); |
| ClickElement(button); |
| VerifyOnlyElementsVisible("Prompt cleared", kElementsVisibleInBrowsing); |
| } |
| |
| TEST_F(UiTest, ClickOnPromptBackgroundDoesNothing) { |
| CreateScene(kNotInWebVr); |
| |
| ui_->GetBrowserUiWeakPtr()->ShowExitVrPrompt( |
| UiUnsupportedMode::kUnhandledPageInfo); |
| OnBeginFrame(); |
| |
| EXPECT_CALL(*browser_, OnExitVrPromptResult(testing::_, testing::_)).Times(0); |
| auto* prompt = scene_->GetUiElementByName(kExitPrompt); |
| UiElement* target; |
| // Target the inert icon and text to reliably miss the buttons. |
| target = prompt->GetDescendantByType(kTypePromptIcon); |
| ClickElement(target); |
| target = prompt->GetDescendantByType(kTypePromptText); |
| ClickElement(target); |
| |
| EXPECT_EQ(model_->get_mode(), kModeModalPrompt); |
| } |
| |
| TEST_F(UiTest, UiUpdatesForWebVR) { |
| CreateScene(kInWebVr); |
| |
| model_->active_capturing.audio_capture_enabled = true; |
| model_->active_capturing.video_capture_enabled = true; |
| model_->active_capturing.screen_capture_enabled = true; |
| model_->active_capturing.location_access_enabled = true; |
| model_->active_capturing.bluetooth_connected = true; |
| |
| VerifyOnlyElementsVisible("Elements hidden", |
| std::set<UiElementName>{kWebVrBackground}); |
| } |
| |
| // This test verifies that we ignore the WebVR frame when we're not expecting |
| // WebVR presentation. You can get an unexpected frame when for example, the |
| // user hits the menu button to exit WebVR mode, but the site continues to pump |
| // frames. If the frame is not ignored, our UI will think we're in WebVR mode. |
| TEST_F(UiTest, WebVrFramesIgnoredWhenUnexpected) { |
| CreateScene(kInWebVr); |
| |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| VerifyOnlyElementsVisible("Elements hidden", std::set<UiElementName>{}); |
| // Disable WebVR mode. |
| ui_->GetBrowserUiWeakPtr()->SetWebVrMode(false); |
| |
| // New frame available after exiting WebVR mode. |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| VerifyOnlyElementsVisible("Browser visible", kElementsVisibleInBrowsing); |
| } |
| |
| TEST_F(UiTest, UiUpdateTransitionToWebVR) { |
| CreateScene(kNotInWebVr); |
| model_->active_capturing.audio_capture_enabled = true; |
| model_->active_capturing.video_capture_enabled = true; |
| model_->active_capturing.screen_capture_enabled = true; |
| model_->active_capturing.location_access_enabled = true; |
| model_->active_capturing.bluetooth_connected = true; |
| |
| // Transition to WebVR mode |
| ui_->GetBrowserUiWeakPtr()->SetWebVrMode(true); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| |
| // All elements should be hidden. |
| VerifyOnlyElementsVisible("Elements hidden", std::set<UiElementName>{}); |
| } |
| |
| TEST_F(UiTest, CaptureIndicatorsVisibility) { |
| const std::set<UiElementName> indicators = { |
| kAudioCaptureIndicator, kVideoCaptureIndicator, |
| kScreenCaptureIndicator, kLocationAccessIndicator, |
| kBluetoothConnectedIndicator, |
| }; |
| |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| EXPECT_TRUE(VerifyVisibility(indicators, false)); |
| EXPECT_TRUE(VerifyRequiresLayout(indicators, false)); |
| |
| model_->active_capturing.audio_capture_enabled = true; |
| model_->active_capturing.video_capture_enabled = true; |
| model_->active_capturing.screen_capture_enabled = true; |
| model_->active_capturing.location_access_enabled = true; |
| model_->active_capturing.bluetooth_connected = true; |
| EXPECT_TRUE(VerifyVisibility(indicators, true)); |
| EXPECT_TRUE(VerifyRequiresLayout(indicators, true)); |
| |
| // Go into non-browser modes and make sure all indicators are hidden. |
| browser_ui->SetWebVrMode(true); |
| EXPECT_TRUE(VerifyVisibility(indicators, false)); |
| browser_ui->SetWebVrMode(false); |
| browser_ui->SetFullscreen(true); |
| EXPECT_TRUE(VerifyVisibility(indicators, false)); |
| browser_ui->SetFullscreen(false); |
| |
| // Back to browser, make sure the indicators reappear. |
| EXPECT_TRUE(VerifyVisibility(indicators, true)); |
| EXPECT_TRUE(VerifyRequiresLayout(indicators, true)); |
| |
| // Ensure they can be turned off. |
| model_->active_capturing.audio_capture_enabled = false; |
| model_->active_capturing.video_capture_enabled = false; |
| model_->active_capturing.screen_capture_enabled = false; |
| model_->active_capturing.location_access_enabled = false; |
| model_->active_capturing.bluetooth_connected = false; |
| EXPECT_TRUE(VerifyRequiresLayout(indicators, false)); |
| } |
| |
| TEST_F(UiTest, PropagateContentBoundsOnStart) { |
| CreateScene(kNotInWebVr); |
| |
| gfx::SizeF expected_bounds(0.495922f, 0.330614f); |
| EXPECT_CALL(*browser_, |
| OnContentScreenBoundsChanged( |
| SizeFsAreApproximatelyEqual(expected_bounds, kTolerance))); |
| |
| ui_->OnProjMatrixChanged(GetPixelDaydreamProjMatrix()); |
| OnBeginFrame(); |
| } |
| |
| TEST_F(UiTest, PropagateContentBoundsOnFullscreen) { |
| CreateScene(kNotInWebVr); |
| |
| ui_->OnProjMatrixChanged(GetPixelDaydreamProjMatrix()); |
| ui_->GetBrowserUiWeakPtr()->SetFullscreen(true); |
| |
| gfx::SizeF expected_bounds(0.587874f, 0.330614f); |
| EXPECT_CALL(*browser_, |
| OnContentScreenBoundsChanged( |
| SizeFsAreApproximatelyEqual(expected_bounds, kTolerance))); |
| |
| ui_->OnProjMatrixChanged(GetPixelDaydreamProjMatrix()); |
| OnBeginFrame(); |
| } |
| |
| TEST_F(UiTest, DontPropagateContentBoundsOnNegligibleChange) { |
| CreateScene(kNotInWebVr); |
| |
| EXPECT_FALSE(RunForMs(0)); |
| ui_->OnProjMatrixChanged(GetPixelDaydreamProjMatrix()); |
| |
| UiElement* content_quad = scene_->GetUiElementByName(kContentQuad); |
| gfx::SizeF content_quad_size = content_quad->size(); |
| content_quad_size.Scale(1.2f); |
| content_quad->SetSize(content_quad_size.width(), content_quad_size.height()); |
| OnBeginFrame(); |
| |
| EXPECT_CALL(*browser_, OnContentScreenBoundsChanged(testing::_)).Times(0); |
| |
| ui_->OnProjMatrixChanged(GetPixelDaydreamProjMatrix()); |
| } |
| |
| TEST_F(UiTest, LoadingIndicatorBindings) { |
| CreateScene(kNotInWebVr); |
| UiElement* background = scene_->GetUiElementByName(kLoadingIndicator); |
| UiElement* foreground = |
| scene_->GetUiElementByName(kLoadingIndicatorForeground); |
| |
| model_->loading = true; |
| model_->load_progress = 0; |
| RunForMs(100); |
| EXPECT_TRUE( |
| VerifyVisibility({kLoadingIndicator, kLoadingIndicatorForeground}, true)); |
| EXPECT_EQ(foreground->GetClipRect(), gfx::RectF(0, 0, 0, 1)); |
| EXPECT_EQ(foreground->size(), background->size()); |
| |
| model_->load_progress = 0.5f; |
| EXPECT_TRUE( |
| VerifyVisibility({kLoadingIndicator, kLoadingIndicatorForeground}, true)); |
| EXPECT_EQ(foreground->GetClipRect(), gfx::RectF(0, 0, 0.5, 1)); |
| EXPECT_EQ(foreground->size(), background->size()); |
| |
| model_->load_progress = 1.0f; |
| EXPECT_TRUE( |
| VerifyVisibility({kLoadingIndicator, kLoadingIndicatorForeground}, true)); |
| EXPECT_EQ(foreground->GetClipRect(), gfx::RectF(0, 0, 1, 1)); |
| EXPECT_EQ(foreground->size(), background->size()); |
| |
| model_->loading = false; |
| EXPECT_TRUE(VerifyVisibility({kLoadingIndicator, kLoadingIndicatorForeground}, |
| false)); |
| } |
| |
| TEST_F(UiTest, WebVrTimeout) { |
| CreateScene(kInWebVr); |
| |
| ui_->GetBrowserUiWeakPtr()->SetWebVrMode(true); |
| model_->web_vr.state = kWebVrAwaitingFirstFrame; |
| |
| RunForMs(500); |
| VerifyVisibility( |
| { |
| kWebVrTimeoutSpinner, kWebVrTimeoutMessage, |
| kWebVrTimeoutMessageLayout, kWebVrTimeoutMessageIcon, |
| kWebVrTimeoutMessageText, kWebVrTimeoutMessageButton, |
| kWebVrTimeoutMessageButtonText, |
| }, |
| false); |
| VerifyVisibility( |
| { |
| kWebVrBackground, |
| }, |
| true); |
| |
| model_->web_vr.state = kWebVrTimeoutImminent; |
| RunForMs(500); |
| VerifyVisibility( |
| { |
| kWebVrTimeoutMessage, kWebVrTimeoutMessageLayout, |
| kWebVrTimeoutMessageIcon, kWebVrTimeoutMessageText, |
| kWebVrTimeoutMessageButton, kWebVrTimeoutMessageButtonText, |
| }, |
| false); |
| VerifyVisibility( |
| { |
| kWebVrTimeoutSpinner, kWebVrBackground, |
| }, |
| true); |
| |
| model_->web_vr.state = kWebVrTimedOut; |
| RunForMs(500); |
| VerifyVisibility( |
| { |
| kWebVrTimeoutSpinner, |
| }, |
| false); |
| VerifyVisibility( |
| { |
| kWebVrBackground, kWebVrTimeoutMessage, kWebVrTimeoutMessageLayout, |
| kWebVrTimeoutMessageIcon, kWebVrTimeoutMessageText, |
| kWebVrTimeoutMessageButton, kWebVrTimeoutMessageButtonText, |
| }, |
| true); |
| } |
| |
| TEST_F(UiTest, SpeechRecognitionUiVisibility) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| browser_ui->SetSpeechRecognitionEnabled(true); |
| |
| // Start hiding browsing foreground and showing speech recognition listening |
| // UI. |
| EXPECT_TRUE(RunForMs(10)); |
| VerifyIsAnimating({k2dBrowsingForeground}, {OPACITY}, true); |
| VerifyIsAnimating({kSpeechRecognitionListening, kSpeechRecognitionResult}, |
| {OPACITY}, false); |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening}, |
| {OPACITY}, true); |
| |
| EXPECT_TRUE(RunForMs(kSpeechRecognitionOpacityAnimationDurationMs)); |
| // All opacity animations should be finished at this point. |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening, |
| kSpeechRecognitionResult}, |
| {OPACITY}, false); |
| VerifyIsAnimating({kSpeechRecognitionListeningGrowingCircle}, {CIRCLE_GROW}, |
| false); |
| VerifyVisibility(kElementsVisibleWithVoiceSearch, true); |
| VerifyVisibility({k2dBrowsingForeground, kSpeechRecognitionResult}, false); |
| |
| model_->speech.speech_recognition_state = SPEECH_RECOGNITION_READY; |
| EXPECT_TRUE(RunForMs(10)); |
| VerifyIsAnimating({kSpeechRecognitionListeningGrowingCircle}, {CIRCLE_GROW}, |
| true); |
| |
| // Mock received speech result. |
| model_->speech.speech_recognition_state = SPEECH_RECOGNITION_END; |
| browser_ui->SetRecognitionResult(base::ASCIIToUTF16("test")); |
| browser_ui->SetSpeechRecognitionEnabled(false); |
| |
| EXPECT_TRUE(RunForMs(10)); |
| VerifyVisibility(kElementsVisibleWithVoiceSearchResult, true); |
| // Speech result UI should show instantly while listening UI hide immediately. |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening, |
| kSpeechRecognitionResult}, |
| {OPACITY}, false); |
| VerifyIsAnimating({kSpeechRecognitionListeningGrowingCircle}, {CIRCLE_GROW}, |
| false); |
| VerifyVisibility({k2dBrowsingForeground, kSpeechRecognitionListening}, false); |
| |
| // The visibility of Speech Recognition UI should not change at this point, |
| // but it will be animating. |
| EXPECT_FALSE(RunForMs(10)); |
| |
| EXPECT_TRUE(RunForMs(kSpeechRecognitionResultTimeoutMs)); |
| // Start hide speech recognition result and show browsing foreground. |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionResult}, |
| {OPACITY}, true); |
| VerifyIsAnimating({kSpeechRecognitionListening}, {OPACITY}, false); |
| |
| EXPECT_TRUE(RunForMs(kSpeechRecognitionOpacityAnimationDurationMs)); |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening, |
| kSpeechRecognitionResult}, |
| {OPACITY}, false); |
| |
| // Visibility is as expected. |
| VerifyVisibility({kSpeechRecognitionListening, kSpeechRecognitionResult}, |
| false); |
| |
| EXPECT_TRUE(IsVisible(k2dBrowsingForeground)); |
| } |
| |
| TEST_F(UiTest, SpeechRecognitionUiVisibilityNoResult) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| |
| browser_ui->SetSpeechRecognitionEnabled(true); |
| EXPECT_TRUE(RunForMs(kSpeechRecognitionOpacityAnimationDurationMs)); |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening}, |
| {OPACITY}, false); |
| |
| // Mock exit without a recognition result |
| browser_ui->SetSpeechRecognitionEnabled(false); |
| model_->speech.recognition_result.clear(); |
| model_->speech.speech_recognition_state = SPEECH_RECOGNITION_END; |
| |
| EXPECT_TRUE(RunForMs(10)); |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening}, |
| {OPACITY}, true); |
| VerifyIsAnimating({kSpeechRecognitionResult}, {OPACITY}, false); |
| |
| EXPECT_TRUE(RunForMs(kSpeechRecognitionOpacityAnimationDurationMs)); |
| VerifyIsAnimating({k2dBrowsingForeground, kSpeechRecognitionListening, |
| kSpeechRecognitionResult}, |
| {OPACITY}, false); |
| |
| // Visibility is as expected. |
| VerifyVisibility({kSpeechRecognitionListening, kSpeechRecognitionResult}, |
| false); |
| EXPECT_TRUE(IsVisible(k2dBrowsingForeground)); |
| } |
| |
| TEST_F(UiTest, OmniboxSuggestionBindings) { |
| CreateScene(kNotInWebVr); |
| UiElement* container = scene_->GetUiElementByName(kOmniboxSuggestions); |
| ASSERT_NE(container, nullptr); |
| |
| OnBeginFrame(); |
| EXPECT_EQ(container->children().size(), 0u); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| EXPECT_EQ(container->children().size(), 0u); |
| EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 0); |
| |
| model_->omnibox_suggestions.emplace_back( |
| OmniboxSuggestion(base::string16(), base::string16(), |
| ACMatchClassifications(), ACMatchClassifications(), |
| nullptr, GURL(), base::string16(), base::string16())); |
| OnBeginFrame(); |
| EXPECT_EQ(container->children().size(), 1u); |
| EXPECT_GT(NumVisibleInTree(kOmniboxSuggestions), 1); |
| |
| model_->omnibox_suggestions.clear(); |
| OnBeginFrame(); |
| EXPECT_EQ(container->children().size(), 0u); |
| EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 0); |
| } |
| |
| TEST_F(UiTest, OmniboxSuggestionNavigates) { |
| CreateScene(kNotInWebVr); |
| GURL gurl("http://test.com/"); |
| model_->push_mode(kModeEditingOmnibox); |
| model_->omnibox_suggestions.emplace_back( |
| OmniboxSuggestion(base::string16(), base::string16(), |
| ACMatchClassifications(), ACMatchClassifications(), |
| nullptr, gurl, base::string16(), base::string16())); |
| OnBeginFrame(); |
| |
| // Let the omnibox fade in. |
| RunForMs(200); |
| |
| UiElement* suggestions = scene_->GetUiElementByName(kOmniboxSuggestions); |
| ASSERT_NE(suggestions, nullptr); |
| UiElement* suggestion = suggestions->children().front().get(); |
| ASSERT_NE(suggestion, nullptr); |
| EXPECT_CALL(*browser_, |
| Navigate(gurl, NavigationMethod::kOmniboxSuggestionSelected)) |
| .Times(1); |
| ClickElement(suggestion); |
| } |
| |
| TEST_F(UiTest, CloseButtonColorBindings) { |
| CreateScene(kNotInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| browser_ui->SetFullscreen(true); |
| EXPECT_TRUE(IsVisible(kCloseButton)); |
| DiscButton* button = |
| static_cast<DiscButton*>(scene_->GetUiElementByName(kCloseButton)); |
| for (int i = 0; i < ColorScheme::kNumModes; i++) { |
| ColorScheme::Mode mode = static_cast<ColorScheme::Mode>(i); |
| SCOPED_TRACE(mode); |
| if (mode == ColorScheme::kModeIncognito) { |
| browser_ui->SetIncognito(true); |
| } else if (mode == ColorScheme::kModeFullscreen) { |
| browser_ui->SetIncognito(false); |
| browser_ui->SetFullscreen(true); |
| } |
| ColorScheme scheme = model_->color_scheme(); |
| RunForMs(kAnimationTimeMs); |
| VerifyButtonColor(button, scheme.disc_button_colors.foreground, |
| scheme.disc_button_colors.background, "normal"); |
| button->hit_plane()->OnHoverEnter(gfx::PointF(0.5f, 0.5f), |
| base::TimeTicks()); |
| RunForMs(kAnimationTimeMs); |
| VerifyButtonColor(button, scheme.disc_button_colors.foreground, |
| scheme.disc_button_colors.background_hover, "hover"); |
| button->hit_plane()->OnButtonDown(gfx::PointF(0.5f, 0.5f), |
| base::TimeTicks()); |
| RunForMs(kAnimationTimeMs); |
| VerifyButtonColor(button, scheme.disc_button_colors.foreground, |
| scheme.disc_button_colors.background_down, "down"); |
| button->hit_plane()->OnHoverMove(gfx::PointF(), base::TimeTicks()); |
| RunForMs(kAnimationTimeMs); |
| VerifyButtonColor(button, scheme.disc_button_colors.foreground, |
| scheme.disc_button_colors.background, "move"); |
| button->hit_plane()->OnButtonUp(gfx::PointF(), base::TimeTicks()); |
| RunForMs(kAnimationTimeMs); |
| VerifyButtonColor(button, scheme.disc_button_colors.foreground, |
| scheme.disc_button_colors.background, "up"); |
| } |
| } |
| |
| TEST_F(UiTest, ExitPresentAndFullscreenOnMenuButtonClick) { |
| CreateScene(kNotInWebVr); |
| ui_->GetBrowserUiWeakPtr()->SetWebVrMode(true); |
| // Clicking menu button should trigger to exit presentation. |
| EXPECT_CALL(*browser_, ExitPresent()); |
| // And also trigger exit fullscreen. |
| EXPECT_CALL(*browser_, ExitFullscreen()); |
| InputEventList events; |
| events.push_back( |
| std::make_unique<InputEvent>(InputEvent::kMenuButtonClicked)); |
| ui_->HandleMenuButtonEvents(&events); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| |
| TEST_F(UiTest, DefaultBackgroundWhenNoAssetAvailable) { |
| UiInitialState state; |
| state.assets_supported = false; |
| CreateScene(state); |
| |
| EXPECT_FALSE(IsVisible(k2dBrowsingTexturedBackground)); |
| EXPECT_TRUE(IsVisible(k2dBrowsingDefaultBackground)); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| } |
| |
| TEST_F(UiTest, TextureBackgroundAfterAssetLoaded) { |
| UiInitialState state; |
| state.assets_supported = true; |
| CreateScene(state); |
| |
| EXPECT_FALSE(IsVisible(k2dBrowsingTexturedBackground)); |
| EXPECT_FALSE(IsVisible(k2dBrowsingDefaultBackground)); |
| EXPECT_FALSE(IsVisible(kContentQuad)); |
| |
| auto assets = std::make_unique<Assets>(); |
| ui_->GetBrowserUiWeakPtr()->OnAssetsLoaded( |
| AssetsLoadStatus::kSuccess, std::move(assets), base::Version("1.0")); |
| |
| EXPECT_TRUE(IsVisible(k2dBrowsingTexturedBackground)); |
| EXPECT_TRUE(IsVisible(kContentQuad)); |
| EXPECT_FALSE(IsVisible(k2dBrowsingDefaultBackground)); |
| } |
| |
| TEST_F(UiTest, ControllerLabels) { |
| CreateScene(kNotInWebVr); |
| |
| EXPECT_FALSE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->mutable_primary_controller().resting_in_viewport = true; |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->push_mode(kModeFullscreen); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_TRUE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->pop_mode(kModeFullscreen); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->push_mode(kModeEditingOmnibox); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_TRUE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->pop_mode(kModeEditingOmnibox); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->push_mode(kModeVoiceSearch); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_TRUE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->pop_mode(kModeVoiceSearch); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| |
| model_->push_mode(kModeRepositionWindow); |
| model_->mutable_primary_controller().laser_direction = kForwardVector; |
| EXPECT_FALSE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_TRUE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_TRUE(IsVisible(kControllerRepositionFinishLabel)); |
| |
| model_->mutable_primary_controller().resting_in_viewport = false; |
| EXPECT_FALSE(IsVisible(kControllerTrackpadRepositionLabel)); |
| EXPECT_FALSE(IsVisible(kControllerTrackpadLabel)); |
| EXPECT_FALSE(IsVisible(kControllerExitButtonLabel)); |
| EXPECT_FALSE(IsVisible(kControllerBackButtonLabel)); |
| } |
| |
| TEST_F(UiTest, ControllerBatteryIndicator) { |
| CreateScene(kNotInWebVr); |
| |
| Rect* dot_0 = |
| static_cast<Rect*>(scene_->GetUiElementByName(kControllerBatteryDot0)); |
| Rect* dot_1 = |
| static_cast<Rect*>(scene_->GetUiElementByName(kControllerBatteryDot1)); |
| Rect* dot_2 = |
| static_cast<Rect*>(scene_->GetUiElementByName(kControllerBatteryDot2)); |
| Rect* dot_3 = |
| static_cast<Rect*>(scene_->GetUiElementByName(kControllerBatteryDot3)); |
| Rect* dot_4 = |
| static_cast<Rect*>(scene_->GetUiElementByName(kControllerBatteryDot4)); |
| |
| ColorScheme scheme = model_->color_scheme(); |
| |
| model_->mutable_primary_controller().battery_level = 5; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_full); |
| |
| model_->mutable_primary_controller().battery_level = 4; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_empty); |
| |
| model_->mutable_primary_controller().battery_level = 3; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_empty); |
| |
| model_->mutable_primary_controller().battery_level = 2; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_empty); |
| |
| model_->mutable_primary_controller().battery_level = 1; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_full); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_empty); |
| |
| model_->mutable_primary_controller().battery_level = 0; |
| RunForMs(kAnimationTimeMs); |
| EXPECT_EQ(dot_0->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_1->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_2->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_3->center_color(), scheme.controller_battery_empty); |
| EXPECT_EQ(dot_4->center_color(), scheme.controller_battery_empty); |
| } |
| |
| TEST_F(UiTest, ResetRepositioner) { |
| CreateScene(kNotInWebVr); |
| |
| Repositioner* repositioner = static_cast<Repositioner*>( |
| scene_->GetUiElementByName(k2dBrowsingRepositioner)); |
| |
| OnBeginFrame(); |
| gfx::Transform original = repositioner->world_space_transform(); |
| |
| repositioner->set_laser_direction(kForwardVector); |
| repositioner->SetEnabled(true); |
| repositioner->set_laser_direction({1, 0, 0}); |
| OnBeginFrame(); |
| |
| EXPECT_NE(original, repositioner->world_space_transform()); |
| repositioner->SetEnabled(false); |
| |
| model_->mutable_primary_controller().recentered = true; |
| |
| OnBeginFrame(); |
| EXPECT_EQ(original, repositioner->world_space_transform()); |
| } |
| |
| // No element in the controller root's subtree should be hit testable. |
| TEST_F(UiTest, ControllerHitTest) { |
| CreateScene(kNotInWebVr); |
| auto* controller = scene_->GetUiElementByName(kControllerRoot); |
| VerifyNoHitTestableElementInSubtree(controller); |
| } |
| |
| TEST_F(UiTest, BrowsingRootBounds) { |
| CreateScene(kNotInWebVr); |
| auto* elem = scene_->GetUiElementByName(k2dBrowsingContentGroup); |
| auto* root = scene_->GetUiElementByName(k2dBrowsingRepositioner); |
| for (; elem; elem = elem->parent()) { |
| int num_bounds_contributors = 0; |
| for (auto& child : elem->children()) { |
| if (child->contributes_to_parent_bounds()) |
| num_bounds_contributors++; |
| } |
| EXPECT_EQ(1, num_bounds_contributors); |
| if (elem == root) |
| break; |
| } |
| } |
| |
| TEST_F(UiTest, DisableResizeWhenEditing) { |
| CreateScene(kNotInWebVr); |
| UiElement* hit_plane = scene_->GetUiElementByName(kContentFrameHitPlane); |
| EXPECT_TRUE(hit_plane->hit_testable()); |
| model_->editing_web_input = true; |
| OnBeginFrame(); |
| EXPECT_FALSE(hit_plane->hit_testable()); |
| model_->editing_web_input = false; |
| OnBeginFrame(); |
| EXPECT_TRUE(hit_plane->hit_testable()); |
| |
| model_->editing_input = true; |
| OnBeginFrame(); |
| EXPECT_FALSE(hit_plane->hit_testable()); |
| model_->editing_input = false; |
| OnBeginFrame(); |
| EXPECT_TRUE(hit_plane->hit_testable()); |
| |
| model_->hosted_platform_ui.hosted_ui_enabled = true; |
| OnBeginFrame(); |
| EXPECT_FALSE(hit_plane->hit_testable()); |
| model_->hosted_platform_ui.hosted_ui_enabled = false; |
| OnBeginFrame(); |
| EXPECT_TRUE(hit_plane->hit_testable()); |
| |
| model_->active_modal_prompt_type = |
| kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission; |
| OnBeginFrame(); |
| EXPECT_FALSE(hit_plane->hit_testable()); |
| model_->active_modal_prompt_type = kModalPromptTypeNone; |
| OnBeginFrame(); |
| EXPECT_TRUE(hit_plane->hit_testable()); |
| } |
| |
| TEST_F(UiTest, RepositionHostedUi) { |
| CreateScene(kNotInWebVr); |
| |
| Repositioner* repositioner = static_cast<Repositioner*>( |
| scene_->GetUiElementByName(k2dBrowsingRepositioner)); |
| UiElement* hosted_ui = scene_->GetUiElementByName(k2dBrowsingHostedUi); |
| |
| model_->hosted_platform_ui.hosted_ui_enabled = true; |
| OnBeginFrame(); |
| gfx::Transform original = hosted_ui->world_space_transform(); |
| |
| repositioner->set_laser_direction(kForwardVector); |
| repositioner->SetEnabled(true); |
| repositioner->set_laser_direction({0, 1, 0}); |
| OnBeginFrame(); |
| |
| EXPECT_NE(original, hosted_ui->world_space_transform()); |
| repositioner->SetEnabled(false); |
| } |
| |
| // Ensures that permissions do not appear after showing hosted UI. |
| TEST_F(UiTest, DoNotShowIndicatorsAfterHostedUi) { |
| CreateScene(kInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| browser_ui->SetWebVrMode(true); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| browser_ui->SetCapturingState(CapturingStateModel(), CapturingStateModel(), |
| CapturingStateModel()); |
| OnBeginFrame(); |
| EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast)); |
| RunForSeconds(8); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| model_->web_vr.showing_hosted_ui = true; |
| OnBeginFrame(); |
| model_->web_vr.showing_hosted_ui = false; |
| OnBeginFrame(); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| } |
| |
| // Ensures that permissions appear on long press, and that when the menu button |
| // is released that we do not show the exclusive screen toast. Distinguishing |
| // these cases requires knowledge of the previous state. |
| TEST_F(UiTest, LongPressMenuButtonInWebVrMode) { |
| CreateScene(kInWebVr); |
| auto browser_ui = ui_->GetBrowserUiWeakPtr(); |
| browser_ui->SetWebVrMode(true); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable(); |
| browser_ui->SetCapturingState(CapturingStateModel(), CapturingStateModel(), |
| CapturingStateModel()); |
| OnBeginFrame(); |
| EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast)); |
| RunForSeconds(8); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| model_->active_capturing.audio_capture_enabled = true; |
| EXPECT_FALSE(model_->menu_button_long_pressed); |
| InputEventList events; |
| events.push_back( |
| std::make_unique<InputEvent>(InputEvent::kMenuButtonLongPressStart)); |
| ui_->HandleMenuButtonEvents(&events); |
| OnBeginFrame(); |
| EXPECT_TRUE(model_->menu_button_long_pressed); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| EXPECT_TRUE(IsVisible(kWebVrAudioCaptureIndicator)); |
| RunForSeconds(8); |
| OnBeginFrame(); |
| EXPECT_TRUE(model_->menu_button_long_pressed); |
| EXPECT_FALSE(IsVisible(kWebVrAudioCaptureIndicator)); |
| EXPECT_FALSE(IsVisible(kWebVrAudioCaptureIndicator)); |
| EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast)); |
| events.push_back( |
| std::make_unique<InputEvent>(InputEvent::kMenuButtonLongPressEnd)); |
| ui_->HandleMenuButtonEvents(&events); |
| EXPECT_FALSE(model_->menu_button_long_pressed); |
| } |
| |
| TEST_F(UiTest, MenuItems) { |
| CreateScene(kNotInWebVr); |
| model_->overflow_menu_enabled = true; |
| |
| EXPECT_EQ(IsVisible(kOverflowMenuNewIncognitoTabItem), true); |
| EXPECT_EQ(IsVisible(kOverflowMenuCloseAllIncognitoTabsItem), false); |
| EXPECT_EQ(IsVisible(kOverflowMenuPreferencesItem), false); |
| |
| model_->incognito_tabs_open = true; |
| OnBeginFrame(); |
| EXPECT_EQ(IsVisible(kOverflowMenuNewIncognitoTabItem), true); |
| EXPECT_EQ(IsVisible(kOverflowMenuCloseAllIncognitoTabsItem), true); |
| |
| model_->incognito = true; |
| OnBeginFrame(); |
| EXPECT_EQ(IsVisible(kOverflowMenuNewIncognitoTabItem), false); |
| EXPECT_EQ(IsVisible(kOverflowMenuCloseAllIncognitoTabsItem), true); |
| |
| model_->standalone_vr_device = true; |
| OnBeginFrame(); |
| EXPECT_EQ(IsVisible(kOverflowMenuPreferencesItem), true); |
| } |
| |
| TEST_F(UiTest, SteadyState) { |
| CreateScene(kNotInWebVr); |
| RunForSeconds(10.0f); |
| // Should have reached steady state. |
| EXPECT_FALSE(OnBeginFrame()); |
| } |
| |
| } // namespace vr |