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 |