| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "modules/media_controls/MediaControlsImpl.h" | 5 #include "modules/media_controls/MediaControlsImpl.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include "core/HTMLNames.h" | 9 #include "core/HTMLNames.h" |
| 10 #include "core/css/StylePropertySet.h" | 10 #include "core/css/StylePropertySet.h" |
| 11 #include "core/dom/ClientRect.h" | 11 #include "core/dom/ClientRect.h" |
| 12 #include "core/dom/Document.h" | 12 #include "core/dom/Document.h" |
| 13 #include "core/dom/ElementTraversal.h" | 13 #include "core/dom/ElementTraversal.h" |
| 14 #include "core/dom/StyleEngine.h" | 14 #include "core/dom/StyleEngine.h" |
| 15 #include "core/events/Event.h" | 15 #include "core/events/Event.h" |
| 16 #include "core/frame/Settings.h" | 16 #include "core/frame/Settings.h" |
| 17 #include "core/html/HTMLElement.h" | 17 #include "core/html/HTMLElement.h" |
| 18 #include "core/html/HTMLVideoElement.h" | 18 #include "core/html/HTMLVideoElement.h" |
| 19 #include "core/html/shadow/MediaControlElementTypes.h" | 19 #include "core/html/shadow/MediaControlElementTypes.h" |
| 20 #include "core/input/EventHandler.h" | 20 #include "core/input/EventHandler.h" |
| 21 #include "core/layout/LayoutObject.h" | 21 #include "core/layout/LayoutObject.h" |
| 22 #include "core/loader/EmptyClients.h" | 22 #include "core/loader/EmptyClients.h" |
| 23 #include "core/testing/DummyPageHolder.h" | 23 #include "core/testing/DummyPageHolder.h" |
| 24 #include "modules/media_controls/elements/MediaControlCurrentTimeDisplayElement.
h" | 24 #include "modules/media_controls/elements/MediaControlCurrentTimeDisplayElement.
h" |
| 25 #include "modules/media_controls/elements/MediaControlDownloadButtonElement.h" | 25 #include "modules/media_controls/elements/MediaControlDownloadButtonElement.h" |
| 26 #include "modules/media_controls/elements/MediaControlTimelineElement.h" | 26 #include "modules/media_controls/elements/MediaControlTimelineElement.h" |
| 27 #include "modules/media_controls/elements/MediaControlVolumeSliderElement.h" | 27 #include "modules/media_controls/elements/MediaControlVolumeSliderElement.h" |
| 28 #include "modules/remoteplayback/HTMLMediaElementRemotePlayback.h" |
| 29 #include "modules/remoteplayback/RemotePlayback.h" |
| 28 #include "platform/heap/Handle.h" | 30 #include "platform/heap/Handle.h" |
| 29 #include "platform/testing/EmptyWebMediaPlayer.h" | 31 #include "platform/testing/EmptyWebMediaPlayer.h" |
| 30 #include "platform/testing/HistogramTester.h" | 32 #include "platform/testing/HistogramTester.h" |
| 31 #include "platform/testing/TestingPlatformSupport.h" | 33 #include "platform/testing/TestingPlatformSupport.h" |
| 32 #include "platform/testing/UnitTestHelpers.h" | 34 #include "platform/testing/UnitTestHelpers.h" |
| 33 #include "public/platform/WebMouseEvent.h" | 35 #include "public/platform/WebMouseEvent.h" |
| 34 #include "public/platform/WebScreenInfo.h" | 36 #include "public/platform/WebScreenInfo.h" |
| 35 #include "public/platform/WebSize.h" | 37 #include "public/platform/WebSize.h" |
| 36 #include "public/platform/modules/remoteplayback/WebRemotePlaybackAvailability.h
" | 38 #include "public/platform/modules/remoteplayback/WebRemotePlaybackAvailability.h
" |
| 37 #include "public/platform/modules/remoteplayback/WebRemotePlaybackClient.h" | 39 #include "public/platform/modules/remoteplayback/WebRemotePlaybackClient.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 53 | 55 |
| 54 class MockVideoWebMediaPlayer : public EmptyWebMediaPlayer { | 56 class MockVideoWebMediaPlayer : public EmptyWebMediaPlayer { |
| 55 public: | 57 public: |
| 56 // WebMediaPlayer overrides: | 58 // WebMediaPlayer overrides: |
| 57 WebTimeRanges Seekable() const override { return seekable_; } | 59 WebTimeRanges Seekable() const override { return seekable_; } |
| 58 bool HasVideo() const override { return true; } | 60 bool HasVideo() const override { return true; } |
| 59 | 61 |
| 60 WebTimeRanges seekable_; | 62 WebTimeRanges seekable_; |
| 61 }; | 63 }; |
| 62 | 64 |
| 63 class MockWebRemotePlaybackClient : public WebRemotePlaybackClient { | |
| 64 public: | |
| 65 void StateChanged(WebRemotePlaybackState) override {} | |
| 66 void AvailabilityChanged( | |
| 67 WebRemotePlaybackAvailability availability) override { | |
| 68 availability_ = availability; | |
| 69 } | |
| 70 void PromptCancelled() override {} | |
| 71 bool RemotePlaybackAvailable() const override { | |
| 72 return availability_ == WebRemotePlaybackAvailability::kDeviceAvailable; | |
| 73 } | |
| 74 | |
| 75 private: | |
| 76 WebRemotePlaybackAvailability availability_ = | |
| 77 WebRemotePlaybackAvailability::kUnknown; | |
| 78 }; | |
| 79 | |
| 80 class MockLayoutObject : public LayoutObject { | 65 class MockLayoutObject : public LayoutObject { |
| 81 public: | 66 public: |
| 82 MockLayoutObject(Node* node) : LayoutObject(node) {} | 67 MockLayoutObject(Node* node) : LayoutObject(node) {} |
| 83 | 68 |
| 84 const char* GetName() const override { return "MockLayoutObject"; } | 69 const char* GetName() const override { return "MockLayoutObject"; } |
| 85 void UpdateLayout() override {} | 70 void UpdateLayout() override {} |
| 86 FloatRect LocalBoundingBoxRectForAccessibility() const override { | 71 FloatRect LocalBoundingBoxRectForAccessibility() const override { |
| 87 return FloatRect(); | 72 return FloatRect(); |
| 88 } | 73 } |
| 89 }; | 74 }; |
| 90 | 75 |
| 91 class StubLocalFrameClient : public EmptyLocalFrameClient { | 76 class StubLocalFrameClient : public EmptyLocalFrameClient { |
| 92 public: | 77 public: |
| 93 static StubLocalFrameClient* Create() { return new StubLocalFrameClient; } | 78 static StubLocalFrameClient* Create() { return new StubLocalFrameClient; } |
| 94 | 79 |
| 95 std::unique_ptr<WebMediaPlayer> CreateWebMediaPlayer( | 80 std::unique_ptr<WebMediaPlayer> CreateWebMediaPlayer( |
| 96 HTMLMediaElement&, | 81 HTMLMediaElement&, |
| 97 const WebMediaPlayerSource&, | 82 const WebMediaPlayerSource&, |
| 98 WebMediaPlayerClient*) override { | 83 WebMediaPlayerClient*) override { |
| 99 return WTF::WrapUnique(new MockVideoWebMediaPlayer); | 84 return WTF::WrapUnique(new MockVideoWebMediaPlayer); |
| 100 } | 85 } |
| 101 | 86 |
| 102 WebRemotePlaybackClient* CreateWebRemotePlaybackClient( | 87 WebRemotePlaybackClient* CreateWebRemotePlaybackClient( |
| 103 HTMLMediaElement&) override { | 88 HTMLMediaElement& element) override { |
| 104 if (!remote_playback_client_) { | 89 return HTMLMediaElementRemotePlayback::remote(element); |
| 105 remote_playback_client_ = | |
| 106 WTF::WrapUnique(new MockWebRemotePlaybackClient); | |
| 107 } | |
| 108 return remote_playback_client_.get(); | |
| 109 } | 90 } |
| 110 | |
| 111 private: | |
| 112 std::unique_ptr<MockWebRemotePlaybackClient> remote_playback_client_; | |
| 113 }; | 91 }; |
| 114 | 92 |
| 115 Element* GetElementByShadowPseudoId(Node& root_node, | 93 Element* GetElementByShadowPseudoId(Node& root_node, |
| 116 const char* shadow_pseudo_id) { | 94 const char* shadow_pseudo_id) { |
| 117 for (Element& element : ElementTraversal::DescendantsOf(root_node)) { | 95 for (Element& element : ElementTraversal::DescendantsOf(root_node)) { |
| 118 if (element.ShadowPseudoId() == shadow_pseudo_id) | 96 if (element.ShadowPseudoId() == shadow_pseudo_id) |
| 119 return &element; | 97 return &element; |
| 120 } | 98 } |
| 121 return nullptr; | 99 return nullptr; |
| 122 } | 100 } |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 | 199 |
| 222 void SetReady() { | 200 void SetReady() { |
| 223 MediaControls().MediaElement().SetReadyState( | 201 MediaControls().MediaElement().SetReadyState( |
| 224 HTMLMediaElement::kHaveEnoughData); | 202 HTMLMediaElement::kHaveEnoughData); |
| 225 } | 203 } |
| 226 | 204 |
| 227 void MouseDownAt(WebFloatPoint pos); | 205 void MouseDownAt(WebFloatPoint pos); |
| 228 void MouseMoveTo(WebFloatPoint pos); | 206 void MouseMoveTo(WebFloatPoint pos); |
| 229 void MouseUpAt(WebFloatPoint pos); | 207 void MouseUpAt(WebFloatPoint pos); |
| 230 | 208 |
| 209 bool HasAvailabilityCallbacks(RemotePlayback* remote_playback) { |
| 210 return !remote_playback->availability_callbacks_.IsEmpty(); |
| 211 } |
| 212 |
| 231 private: | 213 private: |
| 232 std::unique_ptr<DummyPageHolder> page_holder_; | 214 std::unique_ptr<DummyPageHolder> page_holder_; |
| 233 Persistent<MediaControlsImpl> media_controls_; | 215 Persistent<MediaControlsImpl> media_controls_; |
| 234 HistogramTester histogram_tester_; | 216 HistogramTester histogram_tester_; |
| 235 }; | 217 }; |
| 236 | 218 |
| 237 void MediaControlsImplTest::MouseDownAt(WebFloatPoint pos) { | 219 void MediaControlsImplTest::MouseDownAt(WebFloatPoint pos) { |
| 238 WebMouseEvent mouse_down_event(WebInputEvent::kMouseDown, | 220 WebMouseEvent mouse_down_event(WebInputEvent::kMouseDown, |
| 239 pos /* client pos */, pos /* screen pos */, | 221 pos /* client pos */, pos /* screen pos */, |
| 240 WebPointerProperties::Button::kLeft, 1, | 222 WebPointerProperties::Button::kLeft, 1, |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 Element* cast_button = GetElementByShadowPseudoId( | 324 Element* cast_button = GetElementByShadowPseudoId( |
| 343 MediaControls(), "-internal-media-controls-cast-button"); | 325 MediaControls(), "-internal-media-controls-cast-button"); |
| 344 ASSERT_NE(nullptr, cast_button); | 326 ASSERT_NE(nullptr, cast_button); |
| 345 | 327 |
| 346 ASSERT_FALSE(IsElementVisible(*cast_button)); | 328 ASSERT_FALSE(IsElementVisible(*cast_button)); |
| 347 SimulateRouteAvailable(); | 329 SimulateRouteAvailable(); |
| 348 ASSERT_TRUE(IsElementVisible(*cast_button)); | 330 ASSERT_TRUE(IsElementVisible(*cast_button)); |
| 349 | 331 |
| 350 MediaControls().MediaElement().SetBooleanAttribute( | 332 MediaControls().MediaElement().SetBooleanAttribute( |
| 351 HTMLNames::disableremoteplaybackAttr, true); | 333 HTMLNames::disableremoteplaybackAttr, true); |
| 334 testing::RunPendingTasks(); |
| 352 ASSERT_FALSE(IsElementVisible(*cast_button)); | 335 ASSERT_FALSE(IsElementVisible(*cast_button)); |
| 353 | 336 |
| 354 MediaControls().MediaElement().SetBooleanAttribute( | 337 MediaControls().MediaElement().SetBooleanAttribute( |
| 355 HTMLNames::disableremoteplaybackAttr, false); | 338 HTMLNames::disableremoteplaybackAttr, false); |
| 339 testing::RunPendingTasks(); |
| 356 ASSERT_TRUE(IsElementVisible(*cast_button)); | 340 ASSERT_TRUE(IsElementVisible(*cast_button)); |
| 357 } | 341 } |
| 358 | 342 |
| 359 TEST_F(MediaControlsImplTest, CastOverlayDefault) { | 343 TEST_F(MediaControlsImplTest, CastOverlayDefault) { |
| 360 Element* cast_overlay_button = GetElementByShadowPseudoId( | 344 Element* cast_overlay_button = GetElementByShadowPseudoId( |
| 361 MediaControls(), "-internal-media-controls-overlay-cast-button"); | 345 MediaControls(), "-internal-media-controls-overlay-cast-button"); |
| 362 ASSERT_NE(nullptr, cast_overlay_button); | 346 ASSERT_NE(nullptr, cast_overlay_button); |
| 363 | 347 |
| 364 SimulateRouteAvailable(); | 348 SimulateRouteAvailable(); |
| 365 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); | 349 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); |
| 366 } | 350 } |
| 367 | 351 |
| 368 TEST_F(MediaControlsImplTest, CastOverlayDisableRemotePlaybackAttr) { | 352 TEST_F(MediaControlsImplTest, CastOverlayDisableRemotePlaybackAttr) { |
| 369 Element* cast_overlay_button = GetElementByShadowPseudoId( | 353 Element* cast_overlay_button = GetElementByShadowPseudoId( |
| 370 MediaControls(), "-internal-media-controls-overlay-cast-button"); | 354 MediaControls(), "-internal-media-controls-overlay-cast-button"); |
| 371 ASSERT_NE(nullptr, cast_overlay_button); | 355 ASSERT_NE(nullptr, cast_overlay_button); |
| 372 | 356 |
| 373 ASSERT_FALSE(IsElementVisible(*cast_overlay_button)); | 357 ASSERT_FALSE(IsElementVisible(*cast_overlay_button)); |
| 374 SimulateRouteAvailable(); | 358 SimulateRouteAvailable(); |
| 375 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); | 359 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); |
| 376 | 360 |
| 377 MediaControls().MediaElement().SetBooleanAttribute( | 361 MediaControls().MediaElement().SetBooleanAttribute( |
| 378 HTMLNames::disableremoteplaybackAttr, true); | 362 HTMLNames::disableremoteplaybackAttr, true); |
| 363 testing::RunPendingTasks(); |
| 379 ASSERT_FALSE(IsElementVisible(*cast_overlay_button)); | 364 ASSERT_FALSE(IsElementVisible(*cast_overlay_button)); |
| 380 | 365 |
| 381 MediaControls().MediaElement().SetBooleanAttribute( | 366 MediaControls().MediaElement().SetBooleanAttribute( |
| 382 HTMLNames::disableremoteplaybackAttr, false); | 367 HTMLNames::disableremoteplaybackAttr, false); |
| 368 testing::RunPendingTasks(); |
| 383 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); | 369 ASSERT_TRUE(IsElementVisible(*cast_overlay_button)); |
| 384 } | 370 } |
| 385 | 371 |
| 386 TEST_F(MediaControlsImplTest, CastOverlayMediaControlsDisabled) { | 372 TEST_F(MediaControlsImplTest, CastOverlayMediaControlsDisabled) { |
| 387 Element* cast_overlay_button = GetElementByShadowPseudoId( | 373 Element* cast_overlay_button = GetElementByShadowPseudoId( |
| 388 MediaControls(), "-internal-media-controls-overlay-cast-button"); | 374 MediaControls(), "-internal-media-controls-overlay-cast-button"); |
| 389 ASSERT_NE(nullptr, cast_overlay_button); | 375 ASSERT_NE(nullptr, cast_overlay_button); |
| 390 | 376 |
| 391 EXPECT_FALSE(IsElementVisible(*cast_overlay_button)); | 377 EXPECT_FALSE(IsElementVisible(*cast_overlay_button)); |
| 392 SimulateRouteAvailable(); | 378 SimulateRouteAvailable(); |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 840 // Pressing a key prevents controls from hiding. | 826 // Pressing a key prevents controls from hiding. |
| 841 MediaControls().PanelElement()->DispatchEvent(Event::Create("keypress")); | 827 MediaControls().PanelElement()->DispatchEvent(Event::Create("keypress")); |
| 842 platform->RunForPeriodSeconds(2); | 828 platform->RunForPeriodSeconds(2); |
| 843 EXPECT_TRUE(IsElementVisible(*panel)); | 829 EXPECT_TRUE(IsElementVisible(*panel)); |
| 844 | 830 |
| 845 // Once user interaction stops, controls can hide. | 831 // Once user interaction stops, controls can hide. |
| 846 platform->RunForPeriodSeconds(2); | 832 platform->RunForPeriodSeconds(2); |
| 847 EXPECT_FALSE(IsElementVisible(*panel)); | 833 EXPECT_FALSE(IsElementVisible(*panel)); |
| 848 } | 834 } |
| 849 | 835 |
| 836 TEST_F(MediaControlsImplTest, |
| 837 RemovingFromDocumentRemovesListenersAndCallbacks) { |
| 838 auto page_holder = DummyPageHolder::Create(); |
| 839 |
| 840 HTMLMediaElement* element = |
| 841 HTMLVideoElement::Create(page_holder->GetDocument()); |
| 842 element->SetBooleanAttribute(HTMLNames::controlsAttr, true); |
| 843 page_holder->GetDocument().body()->AppendChild(element); |
| 844 |
| 845 RemotePlayback* remote_playback = |
| 846 HTMLMediaElementRemotePlayback::remote(*element); |
| 847 |
| 848 EXPECT_TRUE(remote_playback->HasEventListeners()); |
| 849 EXPECT_TRUE(HasAvailabilityCallbacks(remote_playback)); |
| 850 |
| 851 WeakPersistent<HTMLMediaElement> weak_persistent_video = element; |
| 852 { |
| 853 Persistent<HTMLMediaElement> persistent_video = element; |
| 854 page_holder->GetDocument().body()->setInnerHTML(""); |
| 855 |
| 856 // When removed from the document, the event listeners should have been |
| 857 // dropped. |
| 858 EXPECT_FALSE(remote_playback->HasEventListeners()); |
| 859 EXPECT_FALSE(HasAvailabilityCallbacks(remote_playback)); |
| 860 } |
| 861 |
| 862 testing::RunPendingTasks(); |
| 863 |
| 864 ThreadState::Current()->CollectAllGarbage(); |
| 865 |
| 866 // It has been GC'd. |
| 867 EXPECT_EQ(nullptr, weak_persistent_video); |
| 868 } |
| 869 |
| 870 TEST_F(MediaControlsImplTest, |
| 871 ReInsertingInDocumentRestoresListenersAndCallbacks) { |
| 872 auto page_holder = DummyPageHolder::Create(); |
| 873 |
| 874 HTMLMediaElement* element = |
| 875 HTMLVideoElement::Create(page_holder->GetDocument()); |
| 876 element->SetBooleanAttribute(HTMLNames::controlsAttr, true); |
| 877 page_holder->GetDocument().body()->AppendChild(element); |
| 878 |
| 879 RemotePlayback* remote_playback = |
| 880 HTMLMediaElementRemotePlayback::remote(*element); |
| 881 |
| 882 // This should be a no-op. We keep a reference on the media element to avoid |
| 883 // an unexpected GC. |
| 884 { |
| 885 Persistent<HTMLMediaElement> video_holder = element; |
| 886 page_holder->GetDocument().body()->RemoveChild(element); |
| 887 page_holder->GetDocument().body()->AppendChild(video_holder.Get()); |
| 888 EXPECT_TRUE(remote_playback->HasEventListeners()); |
| 889 EXPECT_TRUE(HasAvailabilityCallbacks(remote_playback)); |
| 890 } |
| 891 } |
| 892 |
| 850 } // namespace blink | 893 } // namespace blink |
| OLD | NEW |