1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// 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
|