1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/paint/video_painter.h"
6
7 #include "base/unguessable_token.h"
8 #include "cc/layers/layer.h"
9 #include "components/paint_preview/common/paint_preview_tracker.h"
10 #include "third_party/blink/public/platform/platform.h"
11 #include "third_party/blink/public/platform/web_size.h"
12 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
13 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
14 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
15 #include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
16 #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
17 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
18 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
19
20 // Integration tests of video painting code (in CAP mode).
21
22 namespace blink {
23 namespace {
24
ExtractLinks(const cc::PaintOpBuffer * buffer,std::vector<std::pair<GURL,SkRect>> * links)25 void ExtractLinks(const cc::PaintOpBuffer* buffer,
26 std::vector<std::pair<GURL, SkRect>>* links) {
27 for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
28 if (it->GetType() == cc::PaintOpType::Annotate) {
29 auto* annotate_op = static_cast<cc::AnnotateOp*>(*it);
30 links->push_back(std::make_pair(
31 GURL(std::string(
32 reinterpret_cast<const char*>(annotate_op->data->data()),
33 annotate_op->data->size())),
34 annotate_op->rect));
35 } else if (it->GetType() == cc::PaintOpType::DrawRecord) {
36 auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
37 ExtractLinks(record_op->record.get(), links);
38 }
39 }
40 }
41
42 class StubWebMediaPlayer : public EmptyWebMediaPlayer {
43 public:
StubWebMediaPlayer(WebMediaPlayerClient * client)44 StubWebMediaPlayer(WebMediaPlayerClient* client) : client_(client) {}
45
GetCcLayer()46 const cc::Layer* GetCcLayer() { return layer_.get(); }
47
48 // WebMediaPlayer
Load(LoadType,const WebMediaPlayerSource &,CorsMode)49 LoadTiming Load(LoadType, const WebMediaPlayerSource&, CorsMode) override {
50 network_state_ = kNetworkStateLoaded;
51 client_->NetworkStateChanged();
52 ready_state_ = kReadyStateHaveEnoughData;
53 client_->ReadyStateChanged();
54 layer_ = cc::Layer::Create();
55 layer_->SetIsDrawable(true);
56 layer_->SetHitTestable(true);
57 client_->SetCcLayer(layer_.get());
58 return LoadTiming::kImmediate;
59 }
GetNetworkState() const60 NetworkState GetNetworkState() const override { return network_state_; }
GetReadyState() const61 ReadyState GetReadyState() const override { return ready_state_; }
62
63 private:
64 WebMediaPlayerClient* client_;
65 scoped_refptr<cc::Layer> layer_;
66 NetworkState network_state_ = kNetworkStateEmpty;
67 ReadyState ready_state_ = kReadyStateHaveNothing;
68 };
69
70 class VideoStubLocalFrameClient : public EmptyLocalFrameClient {
71 public:
72 // LocalFrameClient
CreateWebMediaPlayer(HTMLMediaElement &,const WebMediaPlayerSource &,WebMediaPlayerClient * client)73 std::unique_ptr<WebMediaPlayer> CreateWebMediaPlayer(
74 HTMLMediaElement&,
75 const WebMediaPlayerSource&,
76 WebMediaPlayerClient* client) override {
77 return std::make_unique<StubWebMediaPlayer>(client);
78 }
79 };
80
81 class VideoPainterTestForCAP : private ScopedCompositeAfterPaintForTest,
82 public PaintControllerPaintTestBase {
83 public:
VideoPainterTestForCAP()84 VideoPainterTestForCAP()
85 : ScopedCompositeAfterPaintForTest(true),
86 PaintControllerPaintTestBase(
87 MakeGarbageCollected<VideoStubLocalFrameClient>()) {}
88
SetUp()89 void SetUp() override {
90 EnableCompositing();
91 PaintControllerPaintTestBase::SetUp();
92 GetDocument().SetURL(KURL(NullURL(), "https://example.com/"));
93 }
94
HasLayerAttached(const cc::Layer & layer)95 bool HasLayerAttached(const cc::Layer& layer) {
96 return GetChromeClient().HasLayer(layer);
97 }
98 };
99
TEST_F(VideoPainterTestForCAP,VideoLayerAppearsInLayerTree)100 TEST_F(VideoPainterTestForCAP, VideoLayerAppearsInLayerTree) {
101 // Insert a <video> and allow it to begin loading.
102 SetBodyInnerHTML("<video width=300 height=300 src=test.ogv>");
103 test::RunPendingTasks();
104
105 // Force the page to paint.
106 UpdateAllLifecyclePhasesForTest();
107
108 // Fetch the layer associated with the <video>, and check that it was
109 // correctly configured in the layer tree.
110 auto* element = To<HTMLMediaElement>(GetDocument().body()->firstChild());
111 StubWebMediaPlayer* player =
112 static_cast<StubWebMediaPlayer*>(element->GetWebMediaPlayer());
113 const cc::Layer* layer = player->GetCcLayer();
114 ASSERT_TRUE(layer);
115 EXPECT_TRUE(HasLayerAttached(*layer));
116 // The layer bounds reflects the aspect ratio and object-fit of the video.
117 EXPECT_EQ(gfx::Vector2dF(0, 75), layer->offset_to_transform_parent());
118 EXPECT_EQ(gfx::Size(300, 150), layer->bounds());
119 }
120
121 class VideoPaintPreviewTest : public testing::Test,
122 public PaintTestConfigurations {
123 public:
SetUp()124 void SetUp() override {
125 web_view_helper_.Initialize();
126
127 WebLocalFrameImpl& frame_impl = GetLocalMainFrame();
128 frame_impl.ViewImpl()->MainFrameViewWidget()->Resize(
129 gfx::Size(bounds().size()));
130
131 frame_test_helpers::LoadFrame(&GetLocalMainFrame(), "about:blank");
132 GetDocument().View()->SetParentVisible(true);
133 GetDocument().View()->SetSelfVisible(true);
134 }
135
SetBodyInnerHTML(const std::string & content)136 void SetBodyInnerHTML(const std::string& content) {
137 frame_test_helpers::LoadHTMLString(&GetLocalMainFrame(), content,
138 KURL("http://test.com"));
139 }
140
GetDocument()141 Document& GetDocument() { return *GetFrame()->GetDocument(); }
142
GetLocalMainFrame()143 WebLocalFrameImpl& GetLocalMainFrame() {
144 return *web_view_helper_.LocalMainFrame();
145 }
146
bounds()147 const gfx::Rect& bounds() { return bounds_; }
148
149 private:
GetFrame()150 LocalFrame* GetFrame() { return GetLocalMainFrame().GetFrame(); }
151
152 frame_test_helpers::WebViewHelper web_view_helper_;
153 gfx::Rect bounds_ = {0, 0, 640, 480};
154 };
155
156 INSTANTIATE_PAINT_TEST_SUITE_P(VideoPaintPreviewTest);
157
TEST_P(VideoPaintPreviewTest,URLIsRecordedWhenPaintingPreview)158 TEST_P(VideoPaintPreviewTest, URLIsRecordedWhenPaintingPreview) {
159 // Insert a <video> and allow it to begin loading. The image was taken from
160 // the RFC for the data URI scheme https://tools.ietf.org/html/rfc2397.
161 SetBodyInnerHTML(R"HTML(
162 <style>body{margin:0}</style>
163 <video width=300 height=300 src="test.ogv" poster="data:image/gif;base64,R0
164 lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yq
165 mCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP
166 5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGe
167 jmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1O
168 IcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7"
169 controls>
170 )HTML");
171 test::RunPendingTasks();
172
173 auto token = base::UnguessableToken::Create();
174 const base::UnguessableToken embedding_token =
175 base::UnguessableToken::Create();
176 const bool is_main_frame = true;
177
178 cc::PaintRecorder recorder;
179 paint_preview::PaintPreviewTracker tracker(token, embedding_token,
180 is_main_frame);
181 cc::PaintCanvas* canvas =
182 recorder.beginRecording(bounds().width(), bounds().height());
183 canvas->SetPaintPreviewTracker(&tracker);
184
185 GetLocalMainFrame().CapturePaintPreview(WebRect(bounds()), canvas,
186 /*include_linked_destinations=*/true);
187 auto record = recorder.finishRecordingAsPicture();
188 std::vector<std::pair<GURL, SkRect>> links;
189 ExtractLinks(record.get(), &links);
190
191 ASSERT_EQ(1lu, links.size());
192 EXPECT_EQ("http://test.com/", links[0].first);
193 EXPECT_EQ(SkRect::MakeWH(300, 300), links[0].second);
194 }
195
196 } // namespace
197 } // namespace blink
198