Commit 707b821e authored by chcunningham's avatar chcunningham Committed by Commit Bot

[MSE] Stop firing 'stalled' event.

See Intent to Deprecate
https://groups.google.com/a/chromium.org/d/msg/blink-dev/x54XtrTyOP8/4-5QZlZzDAAJ

MSE's resource is considered 'local' (we don't  manage the donwload -
the app does), so the HTML5 spec text around firing 'stalled' does not
apply. See discussion in https://crbug.com/517240

No change to 'stalled' event for 'remote' (src="somefile.webm") resources.

This CL also stops delaying the 'load' event for deferred media player
Load() execution. Load() may be deferred for an arbitrary number of
seconds, so we should not hold up the window's 'load' event. Prior to
this change, 'stalled' was serving the unintended roll of unblocking the
the 'load' event for deferred load()s that took > 3 seconds. With stall
now removed for MSE, this weird behavior was uncovered and remedied.

Bug: 517240, 836951
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I97f2bf972c79582d038e1215f5d630042656bd1c
Reviewed-on: https://chromium-review.googlesource.com/982564
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarMatthew Wolenetz <wolenetz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#562371}
parent fce61433
......@@ -282,24 +282,23 @@ bool IsStandaloneExtensionProcess() {
}
// Defers media player loading in background pages until they're visible.
// TODO(dalecurtis): Include an idle listener too. http://crbug.com/509135
class MediaLoadDeferrer : public content::RenderFrameObserver {
public:
MediaLoadDeferrer(content::RenderFrame* render_frame,
const base::Closure& continue_loading_cb)
base::OnceClosure continue_loading_cb)
: content::RenderFrameObserver(render_frame),
continue_loading_cb_(continue_loading_cb) {}
continue_loading_cb_(std::move(continue_loading_cb)) {}
~MediaLoadDeferrer() override {}
private:
// content::RenderFrameObserver implementation:
void WasShown() override {
continue_loading_cb_.Run();
std::move(continue_loading_cb_).Run();
delete this;
}
void OnDestruct() override { delete this; }
const base::Closure continue_loading_cb_;
base::OnceClosure continue_loading_cb_;
DISALLOW_COPY_AND_ASSIGN(MediaLoadDeferrer);
};
......@@ -678,25 +677,23 @@ WebPlugin* ChromeContentRendererClient::CreatePluginReplacement(
return placeholder->plugin();
}
void ChromeContentRendererClient::DeferMediaLoad(
bool ChromeContentRendererClient::DeferMediaLoad(
content::RenderFrame* render_frame,
bool has_played_media_before,
const base::Closure& closure) {
base::OnceClosure closure) {
// Don't allow autoplay/autoload of media resources in a RenderFrame that is
// hidden and has never played any media before. We want to allow future
// loads even when hidden to allow playlist-like functionality.
//
// NOTE: This is also used to defer media loading for prerender.
// NOTE: Switch can be used to allow autoplay, unless frame is prerendered.
//
// TODO(dalecurtis): Include an idle check too. http://crbug.com/509135
if ((render_frame->IsHidden() && !has_played_media_before) ||
prerender::PrerenderHelper::IsPrerendering(render_frame)) {
new MediaLoadDeferrer(render_frame, closure);
return;
new MediaLoadDeferrer(render_frame, std::move(closure));
return true;
}
closure.Run();
std::move(closure).Run();
return false;
}
#if BUILDFLAG(ENABLE_PLUGINS)
......
......@@ -141,9 +141,9 @@ class ChromeContentRendererClient
const blink::WebURLError& error,
base::string16* error_description) override;
void DeferMediaLoad(content::RenderFrame* render_frame,
bool DeferMediaLoad(content::RenderFrame* render_frame,
bool has_played_media_before,
const base::Closure& closure) override;
base::OnceClosure closure) override;
void PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) override;
void PostCompositorThreadCreated(
......
......@@ -270,19 +270,19 @@ CastContentRendererClient::GetPrescientNetworking() {
return prescient_networking_dispatcher_.get();
}
void CastContentRendererClient::DeferMediaLoad(
bool CastContentRendererClient::DeferMediaLoad(
content::RenderFrame* render_frame,
bool render_frame_has_played_media_before,
const base::Closure& closure) {
RunWhenInForeground(render_frame, closure);
base::OnceClosure closure) {
return RunWhenInForeground(render_frame, std::move(closure));
}
void CastContentRendererClient::RunWhenInForeground(
bool CastContentRendererClient::RunWhenInForeground(
content::RenderFrame* render_frame,
const base::Closure& closure) {
base::OnceClosure closure) {
auto* media_load_deferrer = CastMediaLoadDeferrer::Get(render_frame);
DCHECK(media_load_deferrer);
media_load_deferrer->RunWhenInForeground(closure);
return media_load_deferrer->RunWhenInForeground(std::move(closure));
}
bool CastContentRendererClient::AllowIdleMediaSuspend() {
......
......@@ -67,9 +67,9 @@ class CastContentRendererClient
bool IsSupportedVideoConfig(const ::media::VideoConfig& config) override;
bool IsSupportedBitstreamAudioCodec(::media::AudioCodec codec) override;
blink::WebPrescientNetworking* GetPrescientNetworking() override;
void DeferMediaLoad(content::RenderFrame* render_frame,
bool DeferMediaLoad(content::RenderFrame* render_frame,
bool render_frame_has_played_media_before,
const base::Closure& closure) override;
base::OnceClosure closure) override;
bool AllowIdleMediaSuspend() override;
void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override;
std::unique_ptr<blink::WebSpeechSynthesizer> OverrideSpeechSynthesizer(
......@@ -78,8 +78,10 @@ class CastContentRendererClient
protected:
CastContentRendererClient();
virtual void RunWhenInForeground(content::RenderFrame* render_frame,
const base::Closure& closure);
// Returns true if running is deferred until in foreground; false if running
// occurs immediately.
virtual bool RunWhenInForeground(content::RenderFrame* render_frame,
base::OnceClosure closure);
private:
// mojom::ApplicationMediaCapabilitiesObserver implementation:
......
......@@ -36,17 +36,16 @@ void CastMediaLoadDeferrer::OnInterfaceRequestForFrame(
registry_.TryBindInterface(interface_name, interface_pipe);
}
// Runs |closure| if the page/frame is switched to foreground.
void CastMediaLoadDeferrer::RunWhenInForeground(
const base::RepeatingClosure& closure) {
bool CastMediaLoadDeferrer::RunWhenInForeground(base::OnceClosure closure) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!render_frame_action_blocked_) {
closure.Run();
return;
std::move(closure).Run();
return false;
}
LOG(WARNING) << "A render frame action is being blocked.";
pending_closures_.push_back(closure);
pending_closures_.push_back(std::move(closure));
return true;
}
// MediaLoadDeferrer implementation
......@@ -59,10 +58,10 @@ void CastMediaLoadDeferrer::UpdateMediaLoadStatus(bool blocked) {
}
// Move callbacks in case OnBlockMediaLoading() is called somehow
// during iteration.
std::vector<base::RepeatingClosure> callbacks;
std::vector<base::OnceClosure> callbacks;
callbacks.swap(pending_closures_);
for (const auto& cb : callbacks) {
cb.Run();
for (auto& cb : callbacks) {
std::move(cb).Run();
}
LOG(INFO) << "Render frame actions are unblocked.";
}
......
......@@ -33,8 +33,10 @@ class CastMediaLoadDeferrer
explicit CastMediaLoadDeferrer(content::RenderFrame* render_frame);
~CastMediaLoadDeferrer() override;
// Runs |closure| if the page/frame is switched to foreground.
void RunWhenInForeground(const base::RepeatingClosure& closure);
// Runs |closure| if the page/frame is switched to foreground. Returns true if
// the running of |closure| is deferred (not yet in foreground); false
// |closure| can be run immediately.
bool RunWhenInForeground(base::OnceClosure closure);
private:
// content::RenderFrameObserver implementation:
......@@ -50,7 +52,7 @@ class CastMediaLoadDeferrer
chromecast::shell::mojom::MediaLoadDeferrerRequest request);
bool render_frame_action_blocked_;
std::vector<base::RepeatingClosure> pending_closures_;
std::vector<base::OnceClosure> pending_closures_;
mojo::BindingSet<chromecast::shell::mojom::MediaLoadDeferrer> bindings_;
......
......@@ -51,11 +51,11 @@ bool ContentRendererClient::ShouldTrackUseCounter(const GURL& url) {
return true;
}
void ContentRendererClient::DeferMediaLoad(
RenderFrame* render_frame,
bool has_played_media_before,
const base::Closure& closure) {
closure.Run();
bool ContentRendererClient::DeferMediaLoad(RenderFrame* render_frame,
bool has_played_media_before,
base::OnceClosure closure) {
std::move(closure).Run();
return false;
}
std::unique_ptr<blink::WebMIDIAccessor>
......
......@@ -155,9 +155,10 @@ class CONTENT_EXPORT ContentRendererClient {
// can run |closure| immediately if they don't wish to defer media resource
// loading. If |has_played_media_before| is true, the render frame has
// previously started media playback (i.e. played audio and video).
virtual void DeferMediaLoad(RenderFrame* render_frame,
// Returns true if running of |closure| is deferred; false if run immediately.
virtual bool DeferMediaLoad(RenderFrame* render_frame,
bool has_played_media_before,
const base::Closure& closure);
base::OnceClosure closure);
// Allows the embedder to override creating a WebMIDIAccessor. If it
// returns NULL the content layer will create the MIDI accessor.
......
......@@ -305,10 +305,10 @@ blink::WebMediaPlayer* MediaFactory::CreateMediaPlayer(
std::unique_ptr<media::WebMediaPlayerParams> params(
new media::WebMediaPlayerParams(
std::move(media_log),
base::Bind(&ContentRendererClient::DeferMediaLoad,
base::Unretained(GetContentClient()->renderer()),
static_cast<RenderFrame*>(render_frame_),
GetWebMediaPlayerDelegate()->has_played_media()),
base::BindRepeating(&ContentRendererClient::DeferMediaLoad,
base::Unretained(GetContentClient()->renderer()),
static_cast<RenderFrame*>(render_frame_),
GetWebMediaPlayerDelegate()->has_played_media()),
audio_renderer_sink, render_thread->GetMediaThreadTaskRunner(),
render_thread->GetWorkerTaskRunner(),
render_thread->compositor_task_runner(),
......
......@@ -323,9 +323,10 @@ WebMediaPlayerMS::~WebMediaPlayerMS() {
delegate_->RemoveObserver(delegate_id_);
}
void WebMediaPlayerMS::Load(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode /*cors_mode*/) {
blink::WebMediaPlayer::LoadTiming WebMediaPlayerMS::Load(
LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode /*cors_mode*/) {
DVLOG(1) << __func__;
DCHECK(thread_checker_.CalledOnValidThread());
......@@ -376,7 +377,7 @@ void WebMediaPlayerMS::Load(LoadType load_type,
if (!video_frame_provider_ && !audio_renderer_) {
SetNetworkState(WebMediaPlayer::kNetworkStateNetworkError);
return;
return blink::WebMediaPlayer::LoadTiming::kImmediate;
}
if (audio_renderer_) {
......@@ -413,6 +414,8 @@ void WebMediaPlayerMS::Load(LoadType load_type,
SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
SetReadyState(WebMediaPlayer::kReadyStateHaveEnoughData);
}
return blink::WebMediaPlayer::LoadTiming::kImmediate;
}
void WebMediaPlayerMS::TrackAdded(const blink::WebMediaStreamTrack& track) {
......
......@@ -86,9 +86,10 @@ class CONTENT_EXPORT WebMediaPlayerMS
~WebMediaPlayerMS() override;
void Load(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) override;
blink::WebMediaPlayer::LoadTiming Load(
LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) override;
// Playback controls.
void Play() override;
......
......@@ -37,7 +37,11 @@ class MockWebMediaPlayer : public blink::WebMediaPlayer,
MockWebMediaPlayer() = default;
~MockWebMediaPlayer() override = default;
void Load(LoadType, const blink::WebMediaPlayerSource&, CORSMode) override {}
LoadTiming Load(LoadType,
const blink::WebMediaPlayerSource&,
CORSMode) override {
return LoadTiming::kImmediate;
}
void Play() override {}
void Pause() override {}
void Seek(double seconds) override {}
......
......@@ -381,21 +381,27 @@ void WebMediaPlayerImpl::DemuxerDestructionHelper(
std::move(demuxer)));
}
void WebMediaPlayerImpl::Load(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) {
WebMediaPlayer::LoadTiming WebMediaPlayerImpl::Load(
LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) {
DVLOG(1) << __func__;
// Only URL or MSE blob URL is supported.
DCHECK(source.IsURL());
blink::WebURL url = source.GetAsURL();
DVLOG(1) << __func__ << "(" << load_type << ", " << GURL(url) << ", "
<< cors_mode << ")";
bool is_deferred = false;
if (!defer_load_cb_.is_null()) {
defer_load_cb_.Run(base::Bind(&WebMediaPlayerImpl::DoLoad, AsWeakPtr(),
load_type, url, cors_mode));
return;
is_deferred = defer_load_cb_.Run(base::BindOnce(
&WebMediaPlayerImpl::DoLoad, AsWeakPtr(), load_type, url, cors_mode));
} else {
DoLoad(load_type, url, cors_mode);
}
DoLoad(load_type, url, cors_mode);
return is_deferred ? LoadTiming::kDeferred : LoadTiming::kImmediate;
}
void WebMediaPlayerImpl::OnWebLayerUpdated() {}
......
......@@ -124,9 +124,9 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
void UnregisterContentsLayer(cc::Layer* layer) override;
void OnSurfaceIdUpdated(viz::SurfaceId surface_id) override;
void Load(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) override;
WebMediaPlayer::LoadTiming Load(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode) override;
// Playback controls.
void Play() override;
......
......@@ -43,8 +43,10 @@ class SurfaceManager;
// to plumb arguments through various abstraction layers.
class MEDIA_BLINK_EXPORT WebMediaPlayerParams {
public:
typedef base::Callback<void(const base::Closure&)> DeferLoadCB;
typedef base::Callback<Context3D()> Context3DCB;
// Returns true if load will deferred. False if it will run immediately.
using DeferLoadCB = base::RepeatingCallback<bool(base::OnceClosure)>;
using Context3DCB = base::Callback<Context3D()>;
// Callback to obtain the media ContextProvider.
// Requires being called on the media thread.
......
......@@ -22,5 +22,6 @@ PASS Test abort after readyState is ended following init segment and media segme
PASS Test abort after appendBuffer update ends.
PASS Test appending null.
PASS Test appending after removeSourceBuffer().
PASS Test slow appending does not trigger stalled events.
Harness: the test ran to completion.
......@@ -586,6 +586,37 @@
test.done();
}, "Test appending after removeSourceBuffer().");
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{
// Media elements using MSE should not fire the stalled event. See discussion at
// https://github.com/w3c/media-source/issues/88#issuecomment-374406928
mediaElement.addEventListener("stalled", test.unreached_func("Unexpected 'stalled' event."));
// Prime the media element with initial appends.
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
var mediaSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.media[0]);
test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
sourceBuffer.appendBuffer(initSegment);
test.waitForExpectedEvents(function()
{
assert_equals(mediaSource.readyState, "open", "readyState is open after init segment appended.");
test.expectEvent(sourceBuffer, "updateend", "mediaSegment append ended.");
sourceBuffer.appendBuffer(mediaSegment);
});
// Verify state and wait for the 'stalled' event.
test.waitForExpectedEvents(function()
{
assert_equals(sourceBuffer.buffered.length, 1, "sourceBuffer has a buffered range");
assert_equals(mediaSource.readyState, "open", "readyState is open after media segment appended.");
// Set timeout to 4 seconds. This creates an opportunity for UAs to _improperly_ fire the stalled event.
// For media elements doing progressive download (not MSE), stalled is thrown after ~3 seconds of the
// download failing to progress.
test.step_timeout(function() { test.done(); }, 4000);
});
}, "Test slow appending does not trigger stalled events.");
</script>
</body>
</html>
......@@ -108,6 +108,10 @@ class WebMediaPlayer {
kTexSubImage3D
};
// Returned by Load() to signal when a players choose to defer (e.g. as part
// of pre-rendering)
enum LoadTiming { kImmediate, kDeferred };
// For last-uploaded-frame-metadata API. https://crbug.com/639174
struct VideoFrameUploadMetadata {
int frame_id = -1;
......@@ -126,7 +130,7 @@ class WebMediaPlayer {
virtual ~WebMediaPlayer() = default;
virtual void Load(LoadType, const WebMediaPlayerSource&, CORSMode) = 0;
virtual LoadTiming Load(LoadType, const WebMediaPlayerSource&, CORSMode) = 0;
// Playback controls.
virtual void Play() = 0;
......
......@@ -1289,7 +1289,13 @@ void HTMLMediaElement::StartPlayerLoad() {
web_media_player_->RequestRemotePlaybackDisabled(
FastHasAttribute(disableremoteplaybackAttr));
web_media_player_->Load(GetLoadType(), source, CorsMode());
auto load_timing = web_media_player_->Load(GetLoadType(), source, CorsMode());
if (load_timing == WebMediaPlayer::LoadTiming::kDeferred) {
// Deferred media loading is not part of the spec, but intuition is that
// this should not hold up the Window's "load" event (similar to user
// gesture requirements).
SetShouldDelayLoadEvent(false);
}
if (IsFullscreen())
web_media_player_->EnteredFullscreen();
......@@ -1923,7 +1929,13 @@ void HTMLMediaElement::ProgressEventTimerFired(TimerBase*) {
sent_stalled_event_ = false;
if (GetLayoutObject())
GetLayoutObject()->UpdateFromElement();
} else if (timedelta > 3.0 && !sent_stalled_event_) {
} else if (!media_source_ && timedelta > 3.0 && !sent_stalled_event_) {
// Note the !media_source_ condition above. The 'stalled' event is not
// fired when using MSE. MSE's resource is considered 'local' (we don't
// manage the donwload - the app does), so the HTML5 spec text around
// 'stalled' does not apply. See discussion in https://crbug.com/517240
// We also don't need to take any action wrt delaying-the-load-event.
// MediaSource disables the delayed load when first attached.
ScheduleEvent(EventTypeNames::stalled);
sent_stalled_event_ = true;
SetShouldDelayLoadEvent(false);
......
......@@ -7,6 +7,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/autoplay.mojom-blink.h"
#include "third_party/blink/public/platform/web_media_player_source.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/media/html_audio_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
......@@ -17,27 +18,44 @@
#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
using ::testing::AnyNumber;
using ::testing::Return;
using ::testing::_;
namespace blink {
class MockWebMediaPlayer : public EmptyWebMediaPlayer {
public:
MOCK_CONST_METHOD0(Duration, double());
MOCK_CONST_METHOD0(CurrentTime, double());
MOCK_METHOD3(
Load,
WebMediaPlayer::LoadTiming(LoadType load_type,
const blink::WebMediaPlayerSource& source,
CORSMode cors_mode));
};
class WebMediaStubLocalFrameClient : public EmptyLocalFrameClient {
public:
static WebMediaStubLocalFrameClient* Create() {
return new WebMediaStubLocalFrameClient;
static WebMediaStubLocalFrameClient* Create(
std::unique_ptr<WebMediaPlayer> player) {
return new WebMediaStubLocalFrameClient(std::move(player));
}
WebMediaStubLocalFrameClient(std::unique_ptr<WebMediaPlayer> player)
: player_(std::move(player)) {}
std::unique_ptr<WebMediaPlayer> CreateWebMediaPlayer(
HTMLMediaElement&,
const WebMediaPlayerSource&,
WebMediaPlayerClient* client,
WebLayerTreeView*) override {
return std::make_unique<MockWebMediaPlayer>();
DCHECK(player_) << " Empty injected player - already used?";
return std::move(player_);
}
private:
std::unique_ptr<WebMediaPlayer> player_;
};
enum class MediaTestParam { kAudio, kVideo };
......@@ -45,8 +63,20 @@ enum class MediaTestParam { kAudio, kVideo };
class HTMLMediaElementTest : public testing::TestWithParam<MediaTestParam> {
protected:
void SetUp() override {
// Sniff the media player pointer to facilitate mocking.
auto mock_media_player = std::make_unique<MockWebMediaPlayer>();
media_player_ = mock_media_player.get();
// Most tests do not care about this call, nor its return value. Those that
// do will clear this expectation and set custom expectations/returns.
EXPECT_CALL(*MockMediaPlayer(), Load(_, _, _))
.Times(AnyNumber())
.WillRepeatedly(Return(WebMediaPlayer::LoadTiming::kImmediate));
dummy_page_holder_ = DummyPageHolder::Create(
IntSize(), nullptr, WebMediaStubLocalFrameClient::Create(), nullptr);
IntSize(), nullptr,
WebMediaStubLocalFrameClient::Create(std::move(mock_media_player)),
nullptr);
if (GetParam() == MediaTestParam::kAudio)
media_ = HTMLAudioElement::Create(dummy_page_holder_->GetDocument());
......@@ -60,10 +90,14 @@ class HTMLMediaElementTest : public testing::TestWithParam<MediaTestParam> {
Media()->current_src_ = url;
}
MockWebMediaPlayer* MockMediaPlayer() { return media_player_; }
bool WasAutoplayInitiated() { return Media()->WasAutoplayInitiated(); }
bool CouldPlayIfEnoughData() { return Media()->CouldPlayIfEnoughData(); }
bool ShouldDelayLoadEvent() { return Media()->should_delay_load_event_; }
void SetReadyState(HTMLMediaElement::ReadyState state) {
Media()->SetReadyState(state);
}
......@@ -78,6 +112,9 @@ class HTMLMediaElementTest : public testing::TestWithParam<MediaTestParam> {
private:
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
Persistent<HTMLMediaElement> media_;
// Owned by WebMediaStubLocalFrameClient.
MockWebMediaPlayer* media_player_;
};
INSTANTIATE_TEST_CASE_P(Audio,
......@@ -327,4 +364,42 @@ TEST_P(HTMLMediaElementTest, AutoplayInitiated_NoGestureRequired_NoGesture) {
EXPECT_TRUE(WasAutoplayInitiated());
}
TEST_P(HTMLMediaElementTest,
DeferredMediaPlayerLoadDoesNotDelayWindowLoadEvent) {
// Source isn't really important, we just need something to let load algorithm
// run up to the point of calling WebMediaPlayer::Load().
Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
// WebMediaPlayer will signal that it will defer loading to some later time.
testing::Mock::VerifyAndClearExpectations(MockMediaPlayer());
EXPECT_CALL(*MockMediaPlayer(), Load(_, _, _))
.WillOnce(Return(WebMediaPlayer::LoadTiming::kDeferred));
// Window's 'load' event starts out "delayed".
EXPECT_TRUE(ShouldDelayLoadEvent());
Media()->load();
test::RunPendingTasks();
// No longer delayed because WMP loading is deferred.
EXPECT_FALSE(ShouldDelayLoadEvent());
}
TEST_P(HTMLMediaElementTest, ImmediateMediaPlayerLoadDoesDelayWindowLoadEvent) {
// Source isn't really important, we just need something to let load algorithm
// run up to the point of calling WebMediaPlayer::Load().
Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
// WebMediaPlayer will signal that it will do the load immediately.
EXPECT_CALL(*MockMediaPlayer(), Load(_, _, _))
.WillOnce(Return(WebMediaPlayer::LoadTiming::kImmediate));
// Window's 'load' event starts out "delayed".
EXPECT_TRUE(ShouldDelayLoadEvent());
Media()->load();
test::RunPendingTasks();
// Still delayed because WMP loading is not deferred.
EXPECT_TRUE(ShouldDelayLoadEvent());
}
} // namespace blink
......@@ -26,7 +26,7 @@ class StubWebMediaPlayer : public EmptyWebMediaPlayer {
const cc::Layer* GetCcLayer() { return layer_.get(); }
// WebMediaPlayer
void Load(LoadType, const WebMediaPlayerSource&, CORSMode) override {
LoadTiming Load(LoadType, const WebMediaPlayerSource&, CORSMode) override {
network_state_ = kNetworkStateLoaded;
client_->NetworkStateChanged();
ready_state_ = kReadyStateHaveEnoughData;
......@@ -34,6 +34,7 @@ class StubWebMediaPlayer : public EmptyWebMediaPlayer {
layer_ = cc::Layer::Create();
layer_->SetIsDrawable(true);
client_->SetCcLayer(layer_.get());
return LoadTiming::kImmediate;
}
NetworkState GetNetworkState() const override { return network_state_; }
ReadyState GetReadyState() const override { return ready_state_; }
......
......@@ -9,6 +9,11 @@
namespace blink {
WebMediaPlayer::LoadTiming
EmptyWebMediaPlayer::Load(LoadType, const WebMediaPlayerSource&, CORSMode) {
return LoadTiming::kImmediate;
}
WebTimeRanges EmptyWebMediaPlayer::Buffered() const {
return WebTimeRanges();
}
......
......@@ -16,7 +16,7 @@ class EmptyWebMediaPlayer : public WebMediaPlayer {
public:
~EmptyWebMediaPlayer() override = default;
void Load(LoadType, const WebMediaPlayerSource&, CORSMode) override {}
LoadTiming Load(LoadType, const WebMediaPlayerSource&, CORSMode) override;
void Play() override {}
void Pause() override {}
void Seek(double seconds) override {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment