| 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 "core/html/shadow/MediaControls.h" | 5 #include "core/html/shadow/MediaControls.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" |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 | 164 |
| 165 // If scripts are not enabled, controls will always be shown. | 165 // If scripts are not enabled, controls will always be shown. |
| 166 m_pageHolder->frame().settings()->setScriptEnabled(true); | 166 m_pageHolder->frame().settings()->setScriptEnabled(true); |
| 167 } | 167 } |
| 168 | 168 |
| 169 void simulateRouteAvailabe() { | 169 void simulateRouteAvailabe() { |
| 170 m_mediaControls->mediaElement().remoteRouteAvailabilityChanged( | 170 m_mediaControls->mediaElement().remoteRouteAvailabilityChanged( |
| 171 WebRemotePlaybackAvailability::DeviceAvailable); | 171 WebRemotePlaybackAvailability::DeviceAvailable); |
| 172 } | 172 } |
| 173 | 173 |
| 174 void ensureLayout() { | 174 void ensureSizing() { |
| 175 // Force a relayout, so that the controls know the width. Otherwise, | 175 // Fire the size-change callback to ensure that the controls have |
| 176 // they don't know if, for example, the cast button will fit. | 176 // been properly notified of the video size. |
| 177 m_mediaControls->mediaElement().clientWidth(); | 177 m_mediaControls->notifyElementSizeChanged( |
| 178 m_mediaControls->mediaElement().getBoundingClientRect()); |
| 178 } | 179 } |
| 179 | 180 |
| 180 void simulateHideMediaControlsTimerFired() { | 181 void simulateHideMediaControlsTimerFired() { |
| 181 m_mediaControls->hideMediaControlsTimerFired(nullptr); | 182 m_mediaControls->hideMediaControlsTimerFired(nullptr); |
| 182 } | 183 } |
| 183 | 184 |
| 184 void simulateLoadedMetadata() { m_mediaControls->onLoadedMetadata(); } | 185 void simulateLoadedMetadata() { m_mediaControls->onLoadedMetadata(); } |
| 185 | 186 |
| 186 MediaControls& mediaControls() { return *m_mediaControls; } | 187 MediaControls& mediaControls() { return *m_mediaControls; } |
| 187 MockVideoWebMediaPlayer* webMediaPlayer() { | 188 MockVideoWebMediaPlayer* webMediaPlayer() { |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 Document& document = this->document(); | 256 Document& document = this->document(); |
| 256 int oldElementCount = document.styleEngine().styleForElementCount(); | 257 int oldElementCount = document.styleEngine().styleForElementCount(); |
| 257 // Also assert that there are no layouts yet. | 258 // Also assert that there are no layouts yet. |
| 258 ASSERT_EQ(0, oldElementCount); | 259 ASSERT_EQ(0, oldElementCount); |
| 259 mediaControls().reset(); | 260 mediaControls().reset(); |
| 260 int newElementCount = document.styleEngine().styleForElementCount(); | 261 int newElementCount = document.styleEngine().styleForElementCount(); |
| 261 ASSERT_EQ(oldElementCount, newElementCount); | 262 ASSERT_EQ(oldElementCount, newElementCount); |
| 262 } | 263 } |
| 263 | 264 |
| 264 TEST_F(MediaControlsTest, CastButtonRequiresRoute) { | 265 TEST_F(MediaControlsTest, CastButtonRequiresRoute) { |
| 265 ensureLayout(); | 266 ensureSizing(); |
| 266 mediaControls().mediaElement().setBooleanAttribute(HTMLNames::controlsAttr, | 267 mediaControls().mediaElement().setBooleanAttribute(HTMLNames::controlsAttr, |
| 267 true); | 268 true); |
| 268 | 269 |
| 269 Element* castButton = getElementByShadowPseudoId( | 270 Element* castButton = getElementByShadowPseudoId( |
| 270 mediaControls(), "-internal-media-controls-cast-button"); | 271 mediaControls(), "-internal-media-controls-cast-button"); |
| 271 ASSERT_NE(nullptr, castButton); | 272 ASSERT_NE(nullptr, castButton); |
| 272 | 273 |
| 273 ASSERT_FALSE(isElementVisible(*castButton)); | 274 ASSERT_FALSE(isElementVisible(*castButton)); |
| 274 | 275 |
| 275 simulateRouteAvailabe(); | 276 simulateRouteAvailabe(); |
| 276 ASSERT_TRUE(isElementVisible(*castButton)); | 277 ASSERT_TRUE(isElementVisible(*castButton)); |
| 277 } | 278 } |
| 278 | 279 |
| 279 TEST_F(MediaControlsTest, CastButtonDisableRemotePlaybackAttr) { | 280 TEST_F(MediaControlsTest, CastButtonDisableRemotePlaybackAttr) { |
| 280 ensureLayout(); | 281 ensureSizing(); |
| 281 mediaControls().mediaElement().setBooleanAttribute(HTMLNames::controlsAttr, | 282 mediaControls().mediaElement().setBooleanAttribute(HTMLNames::controlsAttr, |
| 282 true); | 283 true); |
| 283 | 284 |
| 284 Element* castButton = getElementByShadowPseudoId( | 285 Element* castButton = getElementByShadowPseudoId( |
| 285 mediaControls(), "-internal-media-controls-cast-button"); | 286 mediaControls(), "-internal-media-controls-cast-button"); |
| 286 ASSERT_NE(nullptr, castButton); | 287 ASSERT_NE(nullptr, castButton); |
| 287 | 288 |
| 288 ASSERT_FALSE(isElementVisible(*castButton)); | 289 ASSERT_FALSE(isElementVisible(*castButton)); |
| 289 simulateRouteAvailabe(); | 290 simulateRouteAvailabe(); |
| 290 ASSERT_TRUE(isElementVisible(*castButton)); | 291 ASSERT_TRUE(isElementVisible(*castButton)); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 357 mediaControls().show(); | 358 mediaControls().show(); |
| 358 mediaControls().toggleOverflowMenu(); | 359 mediaControls().toggleOverflowMenu(); |
| 359 EXPECT_TRUE(isElementVisible(*overflowList)); | 360 EXPECT_TRUE(isElementVisible(*overflowList)); |
| 360 | 361 |
| 361 simulateHideMediaControlsTimerFired(); | 362 simulateHideMediaControlsTimerFired(); |
| 362 EXPECT_TRUE(isElementVisible(*overflowList)); | 363 EXPECT_TRUE(isElementVisible(*overflowList)); |
| 363 EXPECT_TRUE(isElementVisible(*panel)); | 364 EXPECT_TRUE(isElementVisible(*panel)); |
| 364 } | 365 } |
| 365 | 366 |
| 366 TEST_F(MediaControlsTest, DownloadButtonDisplayed) { | 367 TEST_F(MediaControlsTest, DownloadButtonDisplayed) { |
| 367 ensureLayout(); | 368 ensureSizing(); |
| 368 | 369 |
| 369 Element* downloadButton = getElementByShadowPseudoId( | 370 Element* downloadButton = getElementByShadowPseudoId( |
| 370 mediaControls(), "-internal-media-controls-download-button"); | 371 mediaControls(), "-internal-media-controls-download-button"); |
| 371 ASSERT_NE(nullptr, downloadButton); | 372 ASSERT_NE(nullptr, downloadButton); |
| 372 | 373 |
| 373 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); | 374 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); |
| 374 testing::runPendingTasks(); | 375 testing::runPendingTasks(); |
| 375 simulateLoadedMetadata(); | 376 simulateLoadedMetadata(); |
| 376 | 377 |
| 377 // Download button should normally be displayed. | 378 // Download button should normally be displayed. |
| 378 EXPECT_TRUE(isElementVisible(*downloadButton)); | 379 EXPECT_TRUE(isElementVisible(*downloadButton)); |
| 379 } | 380 } |
| 380 | 381 |
| 381 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedEmptyUrl) { | 382 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedEmptyUrl) { |
| 382 ensureLayout(); | 383 ensureSizing(); |
| 383 | 384 |
| 384 Element* downloadButton = getElementByShadowPseudoId( | 385 Element* downloadButton = getElementByShadowPseudoId( |
| 385 mediaControls(), "-internal-media-controls-download-button"); | 386 mediaControls(), "-internal-media-controls-download-button"); |
| 386 ASSERT_NE(nullptr, downloadButton); | 387 ASSERT_NE(nullptr, downloadButton); |
| 387 | 388 |
| 388 // Download button should not be displayed when URL is empty. | 389 // Download button should not be displayed when URL is empty. |
| 389 mediaControls().mediaElement().setSrc(""); | 390 mediaControls().mediaElement().setSrc(""); |
| 390 testing::runPendingTasks(); | 391 testing::runPendingTasks(); |
| 391 simulateLoadedMetadata(); | 392 simulateLoadedMetadata(); |
| 392 EXPECT_FALSE(isElementVisible(*downloadButton)); | 393 EXPECT_FALSE(isElementVisible(*downloadButton)); |
| 393 } | 394 } |
| 394 | 395 |
| 395 TEST_F(MediaControlsTest, DownloadButtonDisplayedHiddenAndDisplayed) { | 396 TEST_F(MediaControlsTest, DownloadButtonDisplayedHiddenAndDisplayed) { |
| 396 ensureLayout(); | 397 ensureSizing(); |
| 397 | 398 |
| 398 Element* downloadButton = getElementByShadowPseudoId( | 399 Element* downloadButton = getElementByShadowPseudoId( |
| 399 mediaControls(), "-internal-media-controls-download-button"); | 400 mediaControls(), "-internal-media-controls-download-button"); |
| 400 ASSERT_NE(nullptr, downloadButton); | 401 ASSERT_NE(nullptr, downloadButton); |
| 401 | 402 |
| 402 // Initially show button. | 403 // Initially show button. |
| 403 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); | 404 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); |
| 404 testing::runPendingTasks(); | 405 testing::runPendingTasks(); |
| 405 simulateLoadedMetadata(); | 406 simulateLoadedMetadata(); |
| 406 EXPECT_TRUE(isElementVisible(*downloadButton)); | 407 EXPECT_TRUE(isElementVisible(*downloadButton)); |
| 407 histogramTester().expectBucketCount("Media.Controls.Download", | 408 histogramTester().expectBucketCount("Media.Controls.Download", |
| 408 DownloadActionMetrics::Shown, 1); | 409 DownloadActionMetrics::Shown, 1); |
| 409 | 410 |
| 410 // Hide button. | 411 // Hide button. |
| 411 mediaControls().mediaElement().setSrc(""); | 412 mediaControls().mediaElement().setSrc(""); |
| 412 testing::runPendingTasks(); | 413 testing::runPendingTasks(); |
| 413 EXPECT_FALSE(isElementVisible(*downloadButton)); | 414 EXPECT_FALSE(isElementVisible(*downloadButton)); |
| 414 histogramTester().expectBucketCount("Media.Controls.Download", | 415 histogramTester().expectBucketCount("Media.Controls.Download", |
| 415 DownloadActionMetrics::Shown, 1); | 416 DownloadActionMetrics::Shown, 1); |
| 416 | 417 |
| 417 // Showing button again should not increment Shown count. | 418 // Showing button again should not increment Shown count. |
| 418 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); | 419 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); |
| 419 testing::runPendingTasks(); | 420 testing::runPendingTasks(); |
| 420 EXPECT_TRUE(isElementVisible(*downloadButton)); | 421 EXPECT_TRUE(isElementVisible(*downloadButton)); |
| 421 histogramTester().expectBucketCount("Media.Controls.Download", | 422 histogramTester().expectBucketCount("Media.Controls.Download", |
| 422 DownloadActionMetrics::Shown, 1); | 423 DownloadActionMetrics::Shown, 1); |
| 423 } | 424 } |
| 424 | 425 |
| 425 TEST_F(MediaControlsTest, DownloadButtonRecordsClickOnlyOnce) { | 426 TEST_F(MediaControlsTest, DownloadButtonRecordsClickOnlyOnce) { |
| 426 ensureLayout(); | 427 ensureSizing(); |
| 427 | 428 |
| 428 MediaControlDownloadButtonElement* downloadButton = | 429 MediaControlDownloadButtonElement* downloadButton = |
| 429 static_cast<MediaControlDownloadButtonElement*>( | 430 static_cast<MediaControlDownloadButtonElement*>( |
| 430 getElementByShadowPseudoId( | 431 getElementByShadowPseudoId( |
| 431 mediaControls(), "-internal-media-controls-download-button")); | 432 mediaControls(), "-internal-media-controls-download-button")); |
| 432 ASSERT_NE(nullptr, downloadButton); | 433 ASSERT_NE(nullptr, downloadButton); |
| 433 | 434 |
| 434 // Initially show button. | 435 // Initially show button. |
| 435 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); | 436 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); |
| 436 testing::runPendingTasks(); | 437 testing::runPendingTasks(); |
| 437 simulateLoadedMetadata(); | 438 simulateLoadedMetadata(); |
| 438 EXPECT_TRUE(isElementVisible(*downloadButton)); | 439 EXPECT_TRUE(isElementVisible(*downloadButton)); |
| 439 histogramTester().expectBucketCount("Media.Controls.Download", | 440 histogramTester().expectBucketCount("Media.Controls.Download", |
| 440 DownloadActionMetrics::Shown, 1); | 441 DownloadActionMetrics::Shown, 1); |
| 441 | 442 |
| 442 // Click button once. | 443 // Click button once. |
| 443 downloadButton->dispatchSimulatedClick( | 444 downloadButton->dispatchSimulatedClick( |
| 444 Event::createBubble(EventTypeNames::click), SendNoEvents); | 445 Event::createBubble(EventTypeNames::click), SendNoEvents); |
| 445 histogramTester().expectBucketCount("Media.Controls.Download", | 446 histogramTester().expectBucketCount("Media.Controls.Download", |
| 446 DownloadActionMetrics::Clicked, 1); | 447 DownloadActionMetrics::Clicked, 1); |
| 447 | 448 |
| 448 // Clicking button again should not increment Clicked count. | 449 // Clicking button again should not increment Clicked count. |
| 449 downloadButton->dispatchSimulatedClick( | 450 downloadButton->dispatchSimulatedClick( |
| 450 Event::createBubble(EventTypeNames::click), SendNoEvents); | 451 Event::createBubble(EventTypeNames::click), SendNoEvents); |
| 451 histogramTester().expectBucketCount("Media.Controls.Download", | 452 histogramTester().expectBucketCount("Media.Controls.Download", |
| 452 DownloadActionMetrics::Clicked, 1); | 453 DownloadActionMetrics::Clicked, 1); |
| 453 } | 454 } |
| 454 | 455 |
| 455 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedInfiniteDuration) { | 456 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedInfiniteDuration) { |
| 456 ensureLayout(); | 457 ensureSizing(); |
| 457 | 458 |
| 458 Element* downloadButton = getElementByShadowPseudoId( | 459 Element* downloadButton = getElementByShadowPseudoId( |
| 459 mediaControls(), "-internal-media-controls-download-button"); | 460 mediaControls(), "-internal-media-controls-download-button"); |
| 460 ASSERT_NE(nullptr, downloadButton); | 461 ASSERT_NE(nullptr, downloadButton); |
| 461 | 462 |
| 462 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); | 463 mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); |
| 463 testing::runPendingTasks(); | 464 testing::runPendingTasks(); |
| 464 | 465 |
| 465 // Download button should not be displayed when duration is infinite. | 466 // Download button should not be displayed when duration is infinite. |
| 466 mediaControls().mediaElement().durationChanged( | 467 mediaControls().mediaElement().durationChanged( |
| 467 std::numeric_limits<double>::infinity(), false /* requestSeek */); | 468 std::numeric_limits<double>::infinity(), false /* requestSeek */); |
| 468 simulateLoadedMetadata(); | 469 simulateLoadedMetadata(); |
| 469 EXPECT_FALSE(isElementVisible(*downloadButton)); | 470 EXPECT_FALSE(isElementVisible(*downloadButton)); |
| 470 } | 471 } |
| 471 | 472 |
| 472 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedHLS) { | 473 TEST_F(MediaControlsTest, DownloadButtonNotDisplayedHLS) { |
| 473 ensureLayout(); | 474 ensureSizing(); |
| 474 | 475 |
| 475 Element* downloadButton = getElementByShadowPseudoId( | 476 Element* downloadButton = getElementByShadowPseudoId( |
| 476 mediaControls(), "-internal-media-controls-download-button"); | 477 mediaControls(), "-internal-media-controls-download-button"); |
| 477 ASSERT_NE(nullptr, downloadButton); | 478 ASSERT_NE(nullptr, downloadButton); |
| 478 | 479 |
| 479 // Download button should not be displayed for HLS streams. | 480 // Download button should not be displayed for HLS streams. |
| 480 mediaControls().mediaElement().setSrc("https://example.com/foo.m3u8"); | 481 mediaControls().mediaElement().setSrc("https://example.com/foo.m3u8"); |
| 481 testing::runPendingTasks(); | 482 testing::runPendingTasks(); |
| 482 simulateLoadedMetadata(); | 483 simulateLoadedMetadata(); |
| 483 EXPECT_FALSE(isElementVisible(*downloadButton)); | 484 EXPECT_FALSE(isElementVisible(*downloadButton)); |
| 484 } | 485 } |
| 485 | 486 |
| 486 TEST_F(MediaControlsTest, TimelineSeekToRoundedEnd) { | 487 TEST_F(MediaControlsTest, TimelineSeekToRoundedEnd) { |
| 487 ensureLayout(); | 488 ensureSizing(); |
| 488 | 489 |
| 489 MediaControlTimelineElement* timeline = | 490 MediaControlTimelineElement* timeline = |
| 490 static_cast<MediaControlTimelineElement*>(getElementByShadowPseudoId( | 491 static_cast<MediaControlTimelineElement*>(getElementByShadowPseudoId( |
| 491 mediaControls(), "-webkit-media-controls-timeline")); | 492 mediaControls(), "-webkit-media-controls-timeline")); |
| 492 ASSERT_NE(nullptr, timeline); | 493 ASSERT_NE(nullptr, timeline); |
| 493 | 494 |
| 494 // Tests the case where the real length of the video, |exactDuration|, gets | 495 // Tests the case where the real length of the video, |exactDuration|, gets |
| 495 // rounded up slightly to |roundedUpDuration| when setting the timeline's | 496 // rounded up slightly to |roundedUpDuration| when setting the timeline's |
| 496 // |max| attribute (crbug.com/695065). | 497 // |max| attribute (crbug.com/695065). |
| 497 double exactDuration = 596.586667; | 498 double exactDuration = 596.586667; |
| 498 double roundedUpDuration = 596.587; | 499 double roundedUpDuration = 596.587; |
| 499 loadMediaWithDuration(exactDuration); | 500 loadMediaWithDuration(exactDuration); |
| 500 | 501 |
| 501 // Simulate a click slightly past the end of the track of the timeline's | 502 // Simulate a click slightly past the end of the track of the timeline's |
| 502 // underlying <input type="range">. This would set the |value| to the |max| | 503 // underlying <input type="range">. This would set the |value| to the |max| |
| 503 // attribute, which can be slightly rounded relative to the duration. | 504 // attribute, which can be slightly rounded relative to the duration. |
| 504 timeline->setValueAsNumber(roundedUpDuration, ASSERT_NO_EXCEPTION); | 505 timeline->setValueAsNumber(roundedUpDuration, ASSERT_NO_EXCEPTION); |
| 505 ASSERT_EQ(roundedUpDuration, timeline->valueAsNumber()); | 506 ASSERT_EQ(roundedUpDuration, timeline->valueAsNumber()); |
| 506 EXPECT_EQ(0.0, mediaControls().mediaElement().currentTime()); | 507 EXPECT_EQ(0.0, mediaControls().mediaElement().currentTime()); |
| 507 timeline->dispatchInputEvent(); | 508 timeline->dispatchInputEvent(); |
| 508 EXPECT_EQ(exactDuration, mediaControls().mediaElement().currentTime()); | 509 EXPECT_EQ(exactDuration, mediaControls().mediaElement().currentTime()); |
| 509 } | 510 } |
| 510 | 511 |
| 511 TEST_F(MediaControlsTest, TimelineImmediatelyUpdatesCurrentTime) { | 512 TEST_F(MediaControlsTest, TimelineImmediatelyUpdatesCurrentTime) { |
| 512 ensureLayout(); | 513 ensureSizing(); |
| 513 | 514 |
| 514 MediaControlTimelineElement* timeline = | 515 MediaControlTimelineElement* timeline = |
| 515 static_cast<MediaControlTimelineElement*>(getElementByShadowPseudoId( | 516 static_cast<MediaControlTimelineElement*>(getElementByShadowPseudoId( |
| 516 mediaControls(), "-webkit-media-controls-timeline")); | 517 mediaControls(), "-webkit-media-controls-timeline")); |
| 517 ASSERT_NE(nullptr, timeline); | 518 ASSERT_NE(nullptr, timeline); |
| 518 MediaControlCurrentTimeDisplayElement* currentTimeDisplay = | 519 MediaControlCurrentTimeDisplayElement* currentTimeDisplay = |
| 519 static_cast<MediaControlCurrentTimeDisplayElement*>( | 520 static_cast<MediaControlCurrentTimeDisplayElement*>( |
| 520 getElementByShadowPseudoId( | 521 getElementByShadowPseudoId( |
| 521 mediaControls(), "-webkit-media-controls-current-time-display")); | 522 mediaControls(), "-webkit-media-controls-current-time-display")); |
| 522 ASSERT_NE(nullptr, currentTimeDisplay); | 523 ASSERT_NE(nullptr, currentTimeDisplay); |
| 523 | 524 |
| 524 double duration = 600; | 525 double duration = 600; |
| 525 loadMediaWithDuration(duration); | 526 loadMediaWithDuration(duration); |
| 526 | 527 |
| 527 // Simulate seeking the underlying range to 50%. Current time display should | 528 // Simulate seeking the underlying range to 50%. Current time display should |
| 528 // update synchronously (rather than waiting for media to finish seeking). | 529 // update synchronously (rather than waiting for media to finish seeking). |
| 529 timeline->setValueAsNumber(duration / 2, ASSERT_NO_EXCEPTION); | 530 timeline->setValueAsNumber(duration / 2, ASSERT_NO_EXCEPTION); |
| 530 timeline->dispatchInputEvent(); | 531 timeline->dispatchInputEvent(); |
| 531 EXPECT_EQ(duration / 2, currentTimeDisplay->currentValue()); | 532 EXPECT_EQ(duration / 2, currentTimeDisplay->currentValue()); |
| 532 } | 533 } |
| 533 | 534 |
| 534 } // namespace blink | 535 } // namespace blink |
| OLD | NEW |