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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc
diff --git a/content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc b/content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..06e1f5a51daaadabba38aa729a4ea075ca243b05
--- /dev/null
+++ b/content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc
@@ -0,0 +1,347 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/offscreen_canvas_provider_impl.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
+#include "cc/output/compositor_frame.h"
+#include "content/browser/compositor/test/no_transport_image_transport_factory.h"
+#include "content/browser/renderer_host/offscreen_canvas_surface_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom.h"
+
+#if !defined(OS_ANDROID)
+#include "content/browser/compositor/image_transport_factory.h"
+#endif
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+namespace content {
+namespace {
+
+constexpr uint32_t kRendererClientId = 3;
+constexpr cc::FrameSinkId kFrameSinkParent(kRendererClientId, 1);
+constexpr cc::FrameSinkId kFrameSinkA(kRendererClientId, 3);
+constexpr cc::FrameSinkId kFrameSinkB(kRendererClientId, 4);
+
+// Stub OffscreenCanvasSurfaceClient that stores the latest SurfaceInfo.
+class StubOffscreenCanvasSurfaceClient
+ : public blink::mojom::OffscreenCanvasSurfaceClient {
+ public:
+ StubOffscreenCanvasSurfaceClient() : binding_(this) {}
+ ~StubOffscreenCanvasSurfaceClient() override {}
+
+ blink::mojom::OffscreenCanvasSurfaceClientPtr GetInterfacePtr() {
+ return binding_.CreateInterfacePtrAndBind();
+ }
+
+ const cc::SurfaceInfo& GetLastSurfaceInfo() const {
+ return last_surface_info_;
+ }
+
+ private:
+ // blink::mojom::OffscreenCanvasSurfaceClient:
+ void OnSurfaceCreated(const cc::SurfaceInfo& surface_info) override {
+ last_surface_info_ = surface_info;
+ }
+
+ mojo::Binding<blink::mojom::OffscreenCanvasSurfaceClient> binding_;
+ cc::SurfaceInfo last_surface_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubOffscreenCanvasSurfaceClient);
+};
+
+// Stub MojoCompositorFrameSinkClient that does nothing.
+class StubCompositorFrameSinkClient
+ : public cc::mojom::MojoCompositorFrameSinkClient {
+ public:
+ StubCompositorFrameSinkClient() : binding_(this) {}
+ ~StubCompositorFrameSinkClient() override {}
+
+ cc::mojom::MojoCompositorFrameSinkClientPtr GetInterfacePtr() {
+ return binding_.CreateInterfacePtrAndBind();
+ }
+
+ private:
+ // cc::mojom::MojoCompositorFrameSinkClient:
+ void DidReceiveCompositorFrameAck(
+ const cc::ReturnedResourceArray& resources) override {}
+ void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) override {}
+ void ReclaimResources(const cc::ReturnedResourceArray& resources) override {}
+
+ mojo::Binding<cc::mojom::MojoCompositorFrameSinkClient> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubCompositorFrameSinkClient);
+};
+
+// Create a CompositorFrame suitable to send over IPC.
+cc::CompositorFrame MakeCompositorFrame() {
+ cc::CompositorFrame frame;
+ frame.metadata.begin_frame_ack.source_id =
+ cc::BeginFrameArgs::kManualSourceId;
+ frame.metadata.begin_frame_ack.sequence_number =
+ cc::BeginFrameArgs::kStartingFrameNumber;
+ frame.metadata.device_scale_factor = 1.0f;
+
+ auto render_pass = cc::RenderPass::Create();
+ render_pass->id = 1;
+ render_pass->output_rect = gfx::Rect(100, 100);
+ frame.render_pass_list.push_back(std::move(render_pass));
+
+ return frame;
+}
+
+// Creates a closure that sets |error_variable| true when run.
+base::Closure ConnectionErrorClosure(bool* error_variable) {
+ return base::Bind([](bool* error_variable) { *error_variable = true; },
+ error_variable);
+}
+
+} // namespace
+
+class OffscreenCanvasProviderImplTest : public testing::Test {
+ public:
+ OffscreenCanvasProviderImpl* provider() { return provider_.get(); }
+
+ // Gets the OffscreenCanvasSurfaceImpl for |frame_sink_id| or null if it
+ // it doesn't exist.
+ OffscreenCanvasSurfaceImpl* GetOffscreenCanvasSurface(
+ const cc::FrameSinkId& frame_sink_id) {
+ auto iter = provider_->canvas_map_.find(frame_sink_id);
+ if (iter == provider_->canvas_map_.end())
+ return nullptr;
+ return iter->second.get();
+ }
+
+ // Gets list of FrameSinkId for all offscreen canvases.
+ std::vector<cc::FrameSinkId> GetAllCanvases() {
+ std::vector<cc::FrameSinkId> frame_sink_ids;
+ for (auto& map_entry : provider_->canvas_map_)
+ frame_sink_ids.push_back(map_entry.second->frame_sink_id());
+ std::sort(frame_sink_ids.begin(), frame_sink_ids.end());
+ return frame_sink_ids;
+ }
+
+ void DeleteOffscreenCanvasProviderImpl() { provider_.reset(); }
+
+ void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+ protected:
+ void SetUp() override {
+#if !defined(OS_ANDROID)
+ ImageTransportFactory::InitializeForUnitTests(
+ std::unique_ptr<ImageTransportFactory>(
+ new NoTransportImageTransportFactory));
+ ImageTransportFactory::GetInstance()
+ ->GetFrameSinkManagerHost()
+ ->ConnectToFrameSinkManager();
+#endif
+ provider_ =
+ base::MakeUnique<OffscreenCanvasProviderImpl>(kRendererClientId);
+ }
+ void TearDown() override {
+ provider_.reset();
+#if !defined(OS_ANDROID)
+ ImageTransportFactory::Terminate();
+#endif
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ std::unique_ptr<OffscreenCanvasProviderImpl> provider_;
+};
+
+// Mimics the workflow of OffscreenCanvas.commit() on renderer process.
+TEST_F(OffscreenCanvasProviderImplTest,
+ SingleHTMLCanvasElementTransferToOffscreen) {
+ // Mimic connection from the renderer main thread to browser.
+ StubOffscreenCanvasSurfaceClient surface_client;
+ blink::mojom::OffscreenCanvasSurfacePtr surface;
+ provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
+ surface_client.GetInterfacePtr(),
+ mojo::MakeRequest(&surface));
+
+ OffscreenCanvasSurfaceImpl* surface_impl =
+ GetOffscreenCanvasSurface(kFrameSinkA);
+
+ // There should be a single OffscreenCanvasSurfaceImpl and it should have the
+ // provided FrameSinkId and parent FrameSinkId.
+ EXPECT_EQ(kFrameSinkA, surface_impl->frame_sink_id());
+ EXPECT_EQ(kFrameSinkParent, surface_impl->parent_frame_sink_id());
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+
+ // Mimic connection from the renderer main or worker thread to browser.
+ cc::mojom::MojoCompositorFrameSinkPtr compositor_frame_sink;
+ StubCompositorFrameSinkClient compositor_frame_sink_client;
+ provider()->CreateCompositorFrameSink(
+ kFrameSinkA, compositor_frame_sink_client.GetInterfacePtr(),
+ mojo::MakeRequest(&compositor_frame_sink));
+
+ // Renderer submits a CompositorFrame with |local_id|.
+ const cc::LocalSurfaceId local_id(1, base::UnguessableToken::Create());
+ compositor_frame_sink->SubmitCompositorFrame(local_id, MakeCompositorFrame());
+
+ RunUntilIdle();
+
+ // OffscreenCanvasSurfaceImpl in browser should have LocalSurfaceId that was
+ // submitted with the CompositorFrame.
+ EXPECT_EQ(local_id, surface_impl->local_surface_id());
+
+ // OffscreenCanvasSurfaceClient in the renderer should get the new SurfaceId
+ // including the |local_id|.
+ const auto& surface_info = surface_client.GetLastSurfaceInfo();
+ EXPECT_EQ(kFrameSinkA, surface_info.id().frame_sink_id());
+ EXPECT_EQ(local_id, surface_info.id().local_surface_id());
+}
+
+// Check that renderer closing the mojom::OffscreenCanvasSurface connection
+// destroys the OffscreenCanvasSurfaceImpl in browser.
+TEST_F(OffscreenCanvasProviderImplTest, ClientClosesConnection) {
+ StubOffscreenCanvasSurfaceClient surface_client;
+ blink::mojom::OffscreenCanvasSurfacePtr surface;
+ provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
+ surface_client.GetInterfacePtr(),
+ mojo::MakeRequest(&surface));
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+
+ // Mimic closing the connection from the renderer.
+ surface.reset();
+
+ RunUntilIdle();
+
+ // The renderer closing the connection should destroy the
+ // OffscreenCanvasSurfaceImpl.
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+}
+
+// Check that destroying OffscreenCanvasProviderImpl closes connection to
+// renderer.
+TEST_F(OffscreenCanvasProviderImplTest, ProviderClosesConnections) {
+ StubOffscreenCanvasSurfaceClient surface_client;
+ blink::mojom::OffscreenCanvasSurfacePtr surface;
+ provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
+ surface_client.GetInterfacePtr(),
+ mojo::MakeRequest(&surface));
+
+ // Observe connection errors on |surface|.
+ bool connection_error = false;
+ surface.set_connection_error_handler(
+ ConnectionErrorClosure(&connection_error));
+
+ RunUntilIdle();
+
+ // There should be a OffscreenCanvasSurfaceImpl and |surface| should be bound.
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+ EXPECT_TRUE(surface.is_bound());
+ EXPECT_FALSE(connection_error);
+
+ // Delete OffscreenCanvasProviderImpl before client disconnects.
+ DeleteOffscreenCanvasProviderImpl();
+
+ RunUntilIdle();
+
+ // This should destroy the OffscreenCanvasSurfaceImpl and close the connection
+ // to |surface| triggering a connection error.
+ EXPECT_TRUE(connection_error);
+}
+
+// Check that connecting MojoCompositorFrameSink without first making a
+// OffscreenCanvasSurface connection fails.
+TEST_F(OffscreenCanvasProviderImplTest, ClientConnectionWrongOrder) {
+ // Mimic connection from the renderer main or worker thread.
+ cc::mojom::MojoCompositorFrameSinkPtr compositor_frame_sink;
+ StubCompositorFrameSinkClient compositor_frame_sink_client;
+ // Try to connect MojoCompositorFrameSink without first making
+ // OffscreenCanvasSurface connection. This should fail.
+ provider()->CreateCompositorFrameSink(
+ kFrameSinkA, compositor_frame_sink_client.GetInterfacePtr(),
+ mojo::MakeRequest(&compositor_frame_sink));
+
+ // Observe connection errors on |compositor_frame_sink|.
+ bool connection_error = false;
+ compositor_frame_sink.set_connection_error_handler(
+ ConnectionErrorClosure(&connection_error));
+
+ RunUntilIdle();
+
+ // The connection for |compositor_frame_sink| will have failed and triggered a
+ // connection error.
+ EXPECT_TRUE(connection_error);
+}
+
+// Check that trying to create an OffscreenCanvasSurfaceImpl with a client id
+// that doesn't match the renderer fails.
+TEST_F(OffscreenCanvasProviderImplTest, InvalidClientId) {
+ const cc::FrameSinkId invalid_frame_sink_id(4, 3);
+ EXPECT_NE(kRendererClientId, invalid_frame_sink_id.client_id());
+
+ StubOffscreenCanvasSurfaceClient surface_client;
+ blink::mojom::OffscreenCanvasSurfacePtr surface;
+ provider()->CreateOffscreenCanvasSurface(
+ kFrameSinkParent, invalid_frame_sink_id, surface_client.GetInterfacePtr(),
+ mojo::MakeRequest(&surface));
+
+ // Observe connection errors on |surface|.
+ bool connection_error = false;
+ surface.set_connection_error_handler(
+ ConnectionErrorClosure(&connection_error));
+
+ RunUntilIdle();
+
+ // No OffscreenCanvasSurfaceImpl should have been created.
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+
+ // The connection for |surface| will have failed and triggered a connection
+ // error.
+ EXPECT_TRUE(connection_error);
+}
+
+// Mimic renderer with two offscreen canvases.
+TEST_F(OffscreenCanvasProviderImplTest,
+ MultiHTMLCanvasElementTransferToOffscreen) {
+ StubOffscreenCanvasSurfaceClient surface_client_a;
+ blink::mojom::OffscreenCanvasSurfacePtr surface_a;
+ provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkA,
+ surface_client_a.GetInterfacePtr(),
+ mojo::MakeRequest(&surface_a));
+
+ StubOffscreenCanvasSurfaceClient surface_client_b;
+ blink::mojom::OffscreenCanvasSurfacePtr surface_b;
+ provider()->CreateOffscreenCanvasSurface(kFrameSinkParent, kFrameSinkB,
+ surface_client_b.GetInterfacePtr(),
+ mojo::MakeRequest(&surface_b));
+
+ RunUntilIdle();
+
+ // There should be two OffscreenCanvasSurfaceImpls created.
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA, kFrameSinkB));
+
+ // Mimic closing first connection from the renderer.
+ surface_a.reset();
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkB));
+
+ // Mimic closing second connection from the renderer.
+ surface_b.reset();
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698