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