blob: 5e64ee2fbd48ad26f3689e65a0e9242de11dd3a0 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/no_state_prefetch/renderer/no_state_prefetch_utils.h"
#include "base/memory/weak_ptr.h"
#include "components/no_state_prefetch/renderer/no_state_prefetch_helper.h"
#include "content/public/common/page_visibility_state.h"
#include "content/public/renderer/render_frame.h"
#include "media/mojo/mojom/media_player.mojom.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_view_observer.h"
namespace prerender {
namespace {
const char kDeferredMediaLoadStateKey[] = "kDeferredMediaLoadStateKey";
class DeferredMediaLoadState : public base::SupportsUserData::Data {
public:
DeferredMediaLoadState() = default;
~DeferredMediaLoadState() override = default;
DeferredMediaLoadState(const DeferredMediaLoadState&) = delete;
DeferredMediaLoadState& operator=(const DeferredMediaLoadState&) = delete;
static void Create(content::RenderFrame* render_frame) {
CHECK(render_frame);
if (!render_frame->GetUserData(kDeferredMediaLoadStateKey)) {
render_frame->SetUserData(kDeferredMediaLoadStateKey,
std::make_unique<DeferredMediaLoadState>());
}
}
static void Reset(content::RenderFrame* render_frame) {
CHECK(render_frame);
render_frame->RemoveUserData(kDeferredMediaLoadStateKey);
}
static bool ShouldDeferMediaLoad(content::RenderFrame* render_frame) {
// If `render_frame` is null, defer media load as the WebFrame
// might be gone.
if (!render_frame) {
return true;
}
return render_frame->GetUserData(kDeferredMediaLoadStateKey);
}
};
// Defers media player loading in background pages until they're visible unless
// the tab has previously played content before.
class MediaLoadDeferrer : public blink::WebViewObserver {
public:
MediaLoadDeferrer(content::RenderFrame* render_frame,
blink::WebView* web_view,
base::OnceClosure continue_loading_cb)
: blink::WebViewObserver(web_view),
continue_loading_cb_(std::move(continue_loading_cb)) {
mojo::PendingReceiver<media::mojom::MediaPlayerObserverClient>
media_player_observer_client_receiver =
media_player_observer_client_.BindNewPipeAndPassReceiver();
render_frame->GetBrowserInterfaceBroker().GetInterface(
std::move(media_player_observer_client_receiver));
media_player_observer_client_->GetHasPlayedBefore(
base::BindOnce(&MediaLoadDeferrer::OnGetHasPlayedBeforeCallback,
weak_factory_.GetWeakPtr()));
}
MediaLoadDeferrer(const MediaLoadDeferrer&) = delete;
MediaLoadDeferrer& operator=(const MediaLoadDeferrer&) = delete;
~MediaLoadDeferrer() override = default;
// blink::WebViewObserver implementation:
void OnDestruct() override { delete this; }
void OnPageVisibilityChanged(
content::PageVisibilityState visibility_state) override {
if (visibility_state != content::PageVisibilityState::kVisible) {
return;
}
std::move(continue_loading_cb_).Run();
delete this;
}
void OnGetHasPlayedBeforeCallback(bool has_played_before) {
blink::WebFrame* web_frame =
GetWebView() ? GetWebView()->MainFrame() : nullptr;
// If the page has played media before and doesn't require deferred
// media load, load the player now.
if (has_played_before && web_frame && web_frame->IsWebLocalFrame() &&
!DeferredMediaLoadState::ShouldDeferMediaLoad(
content::RenderFrame::FromWebFrame(web_frame->ToWebLocalFrame()))) {
std::move(continue_loading_cb_).Run();
delete this;
}
}
private:
mojo::Remote<media::mojom::MediaPlayerObserverClient>
media_player_observer_client_;
base::OnceClosure continue_loading_cb_;
base::WeakPtrFactory<MediaLoadDeferrer> weak_factory_{this};
};
} // namespace
bool DeferMediaLoad(content::RenderFrame* render_frame,
bool has_played_media_before,
base::OnceClosure closure) {
blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
// Don't allow autoplay/autoload of media resources in a page that is hidden
// and has no Document Picture-in-Picture window and either never played any
// media before or the media load should be deferred in the frame. We want to
// allow future loads even when hidden to allow playlist-like functionality.
//
// NOTE: This is also used to defer media loading for NoStatePrefetch.
if ((web_frame->View()->GetVisibilityState() !=
content::PageVisibilityState::kVisible &&
(!has_played_media_before ||
DeferredMediaLoadState::ShouldDeferMediaLoad(render_frame)) &&
!web_frame->GetDocument().HasDocumentPictureInPictureWindow()) ||
NoStatePrefetchHelper::IsPrefetching(render_frame)) {
new MediaLoadDeferrer(render_frame, web_frame->View(), std::move(closure));
return true;
}
std::move(closure).Run();
return false;
}
void SetShouldDeferMediaLoad(content::RenderFrame* render_frame,
bool should_defer) {
if (should_defer) {
DeferredMediaLoadState::Create(render_frame);
} else {
DeferredMediaLoadState::Reset(render_frame);
}
}
} // namespace prerender