Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc

Issue 2851243002: Finish offscreen canvas using FrameSinkMangerHost. (Closed)
Patch Set: Add public dep. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "content/browser/renderer_host/offscreen_canvas_provider_impl.h"
6
7 #include <algorithm>
8 #include <utility>
9 #include <vector>
10
11 #include "base/memory/ptr_util.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
15 #include "cc/output/compositor_frame.h"
16 #include "content/browser/compositor/test/no_transport_image_transport_factory.h "
17 #include "content/browser/renderer_host/offscreen_canvas_surface_impl.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "third_party/WebKit/public/platform/modules/offscreencanvas/offscreen_c anvas_surface.mojom.h"
21
22 #if !defined(OS_ANDROID)
23 #include "content/browser/compositor/image_transport_factory.h"
24 #endif
25
26 using testing::ElementsAre;
27 using testing::IsEmpty;
28
29 namespace content {
30 namespace {
31
32 constexpr uint32_t kRendererClientId = 3;
33 constexpr cc::FrameSinkId kFrameSinkParent(kRendererClientId, 1);
34 constexpr cc::FrameSinkId kFrameSinkA(kRendererClientId, 3);
35 constexpr cc::FrameSinkId kFrameSinkB(kRendererClientId, 4);
36
37 // Stub OffscreenCanvasSurfaceClient that stores the latest SurfaceInfo.
38 class StubOffscreenCanvasSurfaceClient
39 : public blink::mojom::OffscreenCanvasSurfaceClient {
40 public:
41 StubOffscreenCanvasSurfaceClient() : binding_(this) {}
42 ~StubOffscreenCanvasSurfaceClient() override {}
43
44 blink::mojom::OffscreenCanvasSurfaceClientPtr GetInterfacePtr() {
45 return binding_.CreateInterfacePtrAndBind();
46 }
47
48 const cc::SurfaceInfo& GetLastSurfaceInfo() const {
49 return last_surface_info_;
50 }
51
52 private:
53 // blink::mojom::OffscreenCanvasSurfaceClient:
54 void OnSurfaceCreated(const cc::SurfaceInfo& surface_info) override {
55 last_surface_info_ = surface_info;
56 }
57
58 mojo::Binding<blink::mojom::OffscreenCanvasSurfaceClient> binding_;
59 cc::SurfaceInfo last_surface_info_;
60
61 DISALLOW_COPY_AND_ASSIGN(StubOffscreenCanvasSurfaceClient);
62 };
63
64 // Stub MojoCompositorFrameSinkClient that does nothing.
65 class StubCompositorFrameSinkClient
66 : public cc::mojom::MojoCompositorFrameSinkClient {
67 public:
68 StubCompositorFrameSinkClient() : binding_(this) {}
69 ~StubCompositorFrameSinkClient() override {}
70
71 cc::mojom::MojoCompositorFrameSinkClientPtr GetInterfacePtr() {
72 return binding_.CreateInterfacePtrAndBind();
73 }
74
75 private:
76 // cc::mojom::MojoCompositorFrameSinkClient:
77 void DidReceiveCompositorFrameAck(
78 const cc::ReturnedResourceArray& resources) override {}
79 void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) override {}
80 void ReclaimResources(const cc::ReturnedResourceArray& resources) override {}
81
82 mojo::Binding<cc::mojom::MojoCompositorFrameSinkClient> binding_;
83
84 DISALLOW_COPY_AND_ASSIGN(StubCompositorFrameSinkClient);
85 };
86
87 // Create a CompositorFrame suitable to send over IPC.
88 cc::CompositorFrame MakeCompositorFrame() {
89 cc::CompositorFrame frame;
90 frame.metadata.begin_frame_ack.source_id =
91 cc::BeginFrameArgs::kManualSourceId;
92 frame.metadata.begin_frame_ack.sequence_number =
93 cc::BeginFrameArgs::kStartingFrameNumber;
94 frame.metadata.device_scale_factor = 1.0f;
95
96 auto render_pass = cc::RenderPass::Create();
97 render_pass->id = 1;
98 render_pass->output_rect = gfx::Rect(100, 100);
99 frame.render_pass_list.push_back(std::move(render_pass));
100
101 return frame;
102 }
103
104 // Creates a closure that sets |error_variable| true when run.
105 base::Closure ConnectionErrorClosure(bool* error_variable) {
106 return base::Bind([](bool* error_variable) { *error_variable = true; },
107 error_variable);
108 }
109
110 } // namespace
111
112 class OffscreenCanvasProviderImplTest : public testing::Test {
113 public:
114 OffscreenCanvasProviderImpl* provider() { return provider_.get(); }
115
116 // Gets the OffscreenCanvasSurfaceImpl for |frame_sink_id| or null if it
117 // it doesn't exist.
118 OffscreenCanvasSurfaceImpl* GetOffscreenCanvasSurface(
119 const cc::FrameSinkId& frame_sink_id) {
120 auto iter = provider_->canvas_map_.find(frame_sink_id);
121 if (iter == provider_->canvas_map_.end())
122 return nullptr;
123 return iter->second.get();
124 }
125
126 // Gets list of FrameSinkId for all offscreen canvases.
127 std::vector<cc::FrameSinkId> GetAllCanvases() {
128 std::vector<cc::FrameSinkId> frame_sink_ids;
129 for (auto& map_entry : provider_->canvas_map_)
130 frame_sink_ids.push_back(map_entry.second->frame_sink_id());
131 std::sort(frame_sink_ids.begin(), frame_sink_ids.end());
132 return frame_sink_ids;
133 }
134
135 void DeleteOffscreenCanvasProviderImpl() { provider_.reset(); }
136
137 void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
138
139 protected:
140 void SetUp() override {
141 #if !defined(OS_ANDROID)
142 ImageTransportFactory::InitializeForUnitTests(
143 std::unique_ptr<ImageTransportFactory>(
144 new NoTransportImageTransportFactory));
145 ImageTransportFactory::GetInstance()
146 ->GetFrameSinkManagerHost()
147 ->ConnectToFrameSinkManager();
148 #endif
149 provider_ =
150 base::MakeUnique<OffscreenCanvasProviderImpl>(kRendererClientId);
151 }
152 void TearDown() override {
153 provider_.reset();
154 #if !defined(OS_ANDROID)
155 ImageTransportFactory::Terminate();
156 #endif
157 }
158
159 private:
160 base::MessageLoop message_loop_;
161 std::unique_ptr<OffscreenCanvasProviderImpl> provider_;
162 };
163
164 // Mimics the workflow of OffscreenCanvas.commit() on renderer process.
165 TEST_F(OffscreenCanvasProviderImplTest,
166 SingleHTMLCanvasElementTransferToOffscreen) {
167 // Mimic connection from the renderer main thread to browser.
168 StubOffscreenCanvasSurfaceClient surface_client;
169 blink::mojom::OffscreenCanvasSurfacePtr surface;
170 provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
171 surface_client.GetInterfacePtr(),
172 mojo::MakeRequest(&surface));
173
174 OffscreenCanvasSurfaceImpl* surface_impl =
175 GetOffscreenCanvasSurface(kFrameSinkA);
176
177 // There should be a single OffscreenCanvasSurfaceImpl and it should have the
178 // provided FrameSinkId and parent FrameSinkId.
179 EXPECT_EQ(kFrameSinkA, surface_impl->frame_sink_id());
180 EXPECT_EQ(kFrameSinkParent, surface_impl->parent_frame_sink_id());
181 EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
182
183 // Mimic connection from the renderer main or worker thread to browser.
184 cc::mojom::MojoCompositorFrameSinkPtr compositor_frame_sink;
185 StubCompositorFrameSinkClient compositor_frame_sink_client;
186 provider()->CreateCompositorFrameSink(
187 kFrameSinkA, compositor_frame_sink_client.GetInterfacePtr(),
188 mojo::MakeRequest(&compositor_frame_sink));
189
190 // Renderer submits a CompositorFrame with |local_id|.
191 const cc::LocalSurfaceId local_id(1, base::UnguessableToken::Create());
192 compositor_frame_sink->SubmitCompositorFrame(local_id, MakeCompositorFrame());
193
194 RunUntilIdle();
195
196 // OffscreenCanvasSurfaceImpl in browser should have LocalSurfaceId that was
197 // submitted with the CompositorFrame.
198 EXPECT_EQ(local_id, surface_impl->local_surface_id());
199
200 // OffscreenCanvasSurfaceClient in the renderer should get the new SurfaceId
201 // including the |local_id|.
202 const auto& surface_info = surface_client.GetLastSurfaceInfo();
203 EXPECT_EQ(kFrameSinkA, surface_info.id().frame_sink_id());
204 EXPECT_EQ(local_id, surface_info.id().local_surface_id());
205 }
206
207 // Check that renderer closing the mojom::OffscreenCanvasSurface connection
208 // destroys the OffscreenCanvasSurfaceImpl in browser.
209 TEST_F(OffscreenCanvasProviderImplTest, ClientClosesConnection) {
210 StubOffscreenCanvasSurfaceClient surface_client;
211 blink::mojom::OffscreenCanvasSurfacePtr surface;
212 provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
213 surface_client.GetInterfacePtr(),
214 mojo::MakeRequest(&surface));
215
216 RunUntilIdle();
217
218 EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
219
220 // Mimic closing the connection from the renderer.
221 surface.reset();
222
223 RunUntilIdle();
224
225 // The renderer closing the connection should destroy the
226 // OffscreenCanvasSurfaceImpl.
227 EXPECT_THAT(GetAllCanvases(), IsEmpty());
228 }
229
230 // Check that destroying OffscreenCanvasProviderImpl closes connection to
231 // renderer.
232 TEST_F(OffscreenCanvasProviderImplTest, ProviderClosesConnections) {
233 StubOffscreenCanvasSurfaceClient surface_client;
234 blink::mojom::OffscreenCanvasSurfacePtr surface;
235 provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
236 surface_client.GetInterfacePtr(),
237 mojo::MakeRequest(&surface));
238
239 // Observe connection errors on |surface|.
240 bool connection_error = false;
241 surface.set_connection_error_handler(
242 ConnectionErrorClosure(&connection_error));
243
244 RunUntilIdle();
245
246 // There should be a OffscreenCanvasSurfaceImpl and |surface| should be bound.
247 EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
248 EXPECT_TRUE(surface.is_bound());
249 EXPECT_FALSE(connection_error);
250
251 // Delete OffscreenCanvasProviderImpl before client disconnects.
252 DeleteOffscreenCanvasProviderImpl();
253
254 RunUntilIdle();
255
256 // This should destroy the OffscreenCanvasSurfaceImpl and close the connection
257 // to |surface| triggering a connection error.
258 EXPECT_TRUE(connection_error);
259 }
260
261 // Check that connecting MojoCompositorFrameSink without first making a
262 // OffscreenCanvasSurface connection fails.
263 TEST_F(OffscreenCanvasProviderImplTest, ClientConnectionWrongOrder) {
264 // Mimic connection from the renderer main or worker thread.
265 cc::mojom::MojoCompositorFrameSinkPtr compositor_frame_sink;
266 StubCompositorFrameSinkClient compositor_frame_sink_client;
267 // Try to connect MojoCompositorFrameSink without first making
268 // OffscreenCanvasSurface connection. This should fail.
269 provider()->CreateCompositorFrameSink(
270 kFrameSinkA, compositor_frame_sink_client.GetInterfacePtr(),
271 mojo::MakeRequest(&compositor_frame_sink));
272
273 // Observe connection errors on |compositor_frame_sink|.
274 bool connection_error = false;
275 compositor_frame_sink.set_connection_error_handler(
276 ConnectionErrorClosure(&connection_error));
277
278 RunUntilIdle();
279
280 // The connection for |compositor_frame_sink| will have failed and triggered a
281 // connection error.
282 EXPECT_TRUE(connection_error);
283 }
284
285 // Check that trying to create an OffscreenCanvasSurfaceImpl with a client id
286 // that doesn't match the renderer fails.
287 TEST_F(OffscreenCanvasProviderImplTest, InvalidClientId) {
288 const cc::FrameSinkId invalid_frame_sink_id(4, 3);
289 EXPECT_NE(kRendererClientId, invalid_frame_sink_id.client_id());
290
291 StubOffscreenCanvasSurfaceClient surface_client;
292 blink::mojom::OffscreenCanvasSurfacePtr surface;
293 provider()->CreateOffscreenCanvasSurface(
294 kFrameSinkParent, invalid_frame_sink_id, surface_client.GetInterfacePtr(),
295 mojo::MakeRequest(&surface));
296
297 // Observe connection errors on |surface|.
298 bool connection_error = false;
299 surface.set_connection_error_handler(
300 ConnectionErrorClosure(&connection_error));
301
302 RunUntilIdle();
303
304 // No OffscreenCanvasSurfaceImpl should have been created.
305 EXPECT_THAT(GetAllCanvases(), IsEmpty());
306
307 // The connection for |surface| will have failed and triggered a connection
308 // error.
309 EXPECT_TRUE(connection_error);
310 }
311
312 // Mimic renderer with two offscreen canvases.
313 TEST_F(OffscreenCanvasProviderImplTest,
314 MultiHTMLCanvasElementTransferToOffscreen) {
315 StubOffscreenCanvasSurfaceClient surface_client_a;
316 blink::mojom::OffscreenCanvasSurfacePtr surface_a;
317 provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
318 surface_client_a.GetInterfacePtr(),
319 mojo::MakeRequest(&surface_a));
320
321 StubOffscreenCanvasSurfaceClient surface_client_b;
322 blink::mojom::OffscreenCanvasSurfacePtr surface_b;
323 provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkB,
324 surface_client_b.GetInterfacePtr(),
325 mojo::MakeRequest(&surface_b));
326
327 RunUntilIdle();
328
329 // There should be two OffscreenCanvasSurfaceImpls created.
330 EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA, kFrameSinkB));
331
332 // Mimic closing first connection from the renderer.
333 surface_a.reset();
334
335 RunUntilIdle();
336
337 EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkB));
338
339 // Mimic closing second connection from the renderer.
340 surface_b.reset();
341
342 RunUntilIdle();
343
344 EXPECT_THAT(GetAllCanvases(), IsEmpty());
345 }
346
347 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698