summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-24 12:15:48 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:30:04 +0000
commitb014812705fc80bff0a5c120dfcef88f349816dc (patch)
tree25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
parent9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff)
downloadqtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc')
-rw-r--r--chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc321
1 files changed, 321 insertions, 0 deletions
diff --git a/chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc b/chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
new file mode 100644
index 00000000000..02999045b0b
--- /dev/null
+++ b/chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
@@ -0,0 +1,321 @@
+// 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/embedded_frame_sink_provider_impl.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "build/build_config.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/host/host_frame_sink_manager.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/test/compositor_frame_helpers.h"
+#include "components/viz/test/fake_host_frame_sink_client.h"
+#include "components/viz/test/mock_compositor_frame_sink_client.h"
+#include "content/browser/compositor/surface_utils.h"
+#include "content/browser/renderer_host/embedded_frame_sink_impl.h"
+#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom.h"
+#include "ui/compositor/compositor.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 viz::FrameSinkId kFrameSinkParent(kRendererClientId, 1);
+constexpr viz::FrameSinkId kFrameSinkA(kRendererClientId, 3);
+constexpr viz::FrameSinkId kFrameSinkB(kRendererClientId, 4);
+
+// Runs RunLoop until |endpoint| encounters a connection error.
+template <class T>
+void WaitForConnectionError(T* endpoint) {
+ base::RunLoop run_loop;
+ endpoint->set_connection_error_handler(run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+// Stub EmbeddedFrameSinkClient that stores the latest SurfaceInfo.
+class StubEmbeddedFrameSinkClient
+ : public blink::mojom::EmbeddedFrameSinkClient {
+ public:
+ StubEmbeddedFrameSinkClient() : binding_(this) {}
+ ~StubEmbeddedFrameSinkClient() override {}
+
+ blink::mojom::EmbeddedFrameSinkClientPtr GetInterfacePtr() {
+ blink::mojom::EmbeddedFrameSinkClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ binding_.set_connection_error_handler(
+ base::BindOnce([](bool* error_variable) { *error_variable = true; },
+ &connection_error_));
+ return client;
+ }
+
+ void Close() { binding_.Close(); }
+
+ const viz::SurfaceInfo& last_surface_info() const {
+ return last_surface_info_;
+ }
+
+ bool connection_error() const { return connection_error_; }
+
+ private:
+ // blink::mojom::EmbeddedFrameSinkClient:
+ void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override {
+ last_surface_info_ = surface_info;
+ }
+
+ mojo::Binding<blink::mojom::EmbeddedFrameSinkClient> binding_;
+ viz::SurfaceInfo last_surface_info_;
+ bool connection_error_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(StubEmbeddedFrameSinkClient);
+};
+
+} // namespace
+
+class EmbeddedFrameSinkProviderImplTest : public testing::Test {
+ public:
+ EmbeddedFrameSinkProviderImpl* provider() { return provider_.get(); }
+
+ // Gets the EmbeddedFrameSinkImpl for |frame_sink_id| or null if it doesn't
+ // exist.
+ EmbeddedFrameSinkImpl* GetEmbeddedFrameSink(
+ const viz::FrameSinkId& frame_sink_id) {
+ auto iter = provider_->frame_sink_map_.find(frame_sink_id);
+ if (iter == provider_->frame_sink_map_.end())
+ return nullptr;
+ return iter->second.get();
+ }
+
+ // Gets list of FrameSinkId for all EmbeddedFrameSinkImpls.
+ std::vector<viz::FrameSinkId> GetAllCanvases() {
+ std::vector<viz::FrameSinkId> frame_sink_ids;
+ for (auto& map_entry : provider_->frame_sink_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 DeleteEmbeddedFrameSinkProviderImpl() { provider_.reset(); }
+
+ void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+ protected:
+ void SetUp() override {
+ host_frame_sink_manager_ = std::make_unique<viz::HostFrameSinkManager>();
+
+ // The FrameSinkManagerImpl implementation is in-process here for tests.
+ frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>();
+ surface_utils::ConnectWithLocalFrameSinkManager(
+ host_frame_sink_manager_.get(), frame_sink_manager_.get());
+
+ provider_ = std::make_unique<EmbeddedFrameSinkProviderImpl>(
+ host_frame_sink_manager_.get(), kRendererClientId);
+
+ host_frame_sink_manager_->RegisterFrameSinkId(kFrameSinkParent,
+ &host_frame_sink_client_);
+ }
+ void TearDown() override {
+ host_frame_sink_manager_->InvalidateFrameSinkId(kFrameSinkParent);
+ provider_.reset();
+ host_frame_sink_manager_.reset();
+ frame_sink_manager_.reset();
+ }
+
+ private:
+ // A MessageLoop is required for mojo bindings which are used to
+ // connect to graphics services.
+ base::MessageLoop message_loop_;
+ viz::FakeHostFrameSinkClient host_frame_sink_client_;
+ std::unique_ptr<viz::HostFrameSinkManager> host_frame_sink_manager_;
+ std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_;
+ std::unique_ptr<EmbeddedFrameSinkProviderImpl> provider_;
+};
+
+// Mimics the workflow of OffscreenCanvas.commit() on renderer process.
+TEST_F(EmbeddedFrameSinkProviderImplTest,
+ SingleHTMLCanvasElementTransferToOffscreen) {
+ // Mimic connection from the renderer main thread to browser.
+ StubEmbeddedFrameSinkClient efs_client;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
+ efs_client.GetInterfacePtr());
+
+ EmbeddedFrameSinkImpl* efs_impl = GetEmbeddedFrameSink(kFrameSinkA);
+
+ // There should be a single EmbeddedFrameSinkImpl and it should have the
+ // provided FrameSinkId.
+ EXPECT_EQ(kFrameSinkA, efs_impl->frame_sink_id());
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+
+ // Mimic connection from the renderer main or worker thread to browser.
+ viz::mojom::CompositorFrameSinkPtr compositor_frame_sink;
+ viz::MockCompositorFrameSinkClient compositor_frame_sink_client;
+ provider()->CreateCompositorFrameSink(
+ kFrameSinkA, compositor_frame_sink_client.BindInterfacePtr(),
+ mojo::MakeRequest(&compositor_frame_sink));
+
+ // Renderer submits a CompositorFrame with |local_id|.
+ const viz::LocalSurfaceId local_id(1, base::UnguessableToken::Create());
+ compositor_frame_sink->SubmitCompositorFrame(
+ local_id, viz::MakeDefaultCompositorFrame(), base::nullopt,
+ base::TimeTicks::Now().since_origin().InMicroseconds());
+
+ RunUntilIdle();
+
+ // EmbeddedFrameSinkImpl in browser should have LocalSurfaceId that was
+ // submitted with the CompositorFrame.
+ EXPECT_EQ(local_id, efs_impl->local_surface_id());
+
+ // EmbeddedFrameSinkClient in the renderer should get the new SurfaceId
+ // including the |local_id|.
+ const auto& surface_info = efs_client.last_surface_info();
+ 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::EmbeddedFrameSinkClient connection
+// destroys the EmbeddedFrameSinkImpl in browser.
+TEST_F(EmbeddedFrameSinkProviderImplTest, ClientClosesConnection) {
+ StubEmbeddedFrameSinkClient efs_client;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
+ efs_client.GetInterfacePtr());
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+
+ // Mimic closing the connection from the renderer.
+ efs_client.Close();
+
+ RunUntilIdle();
+
+ // The renderer closing the connection should destroy the
+ // EmbeddedFrameSinkImpl.
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+}
+
+// Check that destroying EmbeddedFrameSinkProviderImpl closes connection to
+// renderer.
+TEST_F(EmbeddedFrameSinkProviderImplTest, ProviderClosesConnections) {
+ StubEmbeddedFrameSinkClient efs_client;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
+ efs_client.GetInterfacePtr());
+
+ RunUntilIdle();
+
+ // There should be a EmbeddedFrameSinkImpl and |efs_client| should be
+ // bound.
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA));
+ EXPECT_FALSE(efs_client.connection_error());
+
+ // Delete EmbeddedFrameSinkProviderImpl before client disconnects.
+ DeleteEmbeddedFrameSinkProviderImpl();
+
+ RunUntilIdle();
+
+ // This should destroy the EmbeddedFrameSinkImpl and close the connection
+ // to |efs_client| triggering a connection error.
+ EXPECT_TRUE(efs_client.connection_error());
+}
+
+// Check that connecting CompositorFrameSink without first making a
+// EmbeddedFrameSink connection fails.
+TEST_F(EmbeddedFrameSinkProviderImplTest, ClientConnectionWrongOrder) {
+ // Mimic connection from the renderer main or worker thread.
+ viz::mojom::CompositorFrameSinkPtr compositor_frame_sink;
+ viz::MockCompositorFrameSinkClient compositor_frame_sink_client;
+ // Try to connect CompositorFrameSink without first making
+ // EmbeddedFrameSink connection. This should fail.
+ provider()->CreateCompositorFrameSink(
+ kFrameSinkA, compositor_frame_sink_client.BindInterfacePtr(),
+ mojo::MakeRequest(&compositor_frame_sink));
+
+ // The request will fail and trigger a connection error.
+ WaitForConnectionError(&compositor_frame_sink);
+}
+
+// Check that trying to create an EmbeddedFrameSinkImpl when the parent
+// FrameSinkId has already been invalidated fails.
+TEST_F(EmbeddedFrameSinkProviderImplTest, ParentNotRegistered) {
+ StubEmbeddedFrameSinkClient efs_client;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkA, kFrameSinkB,
+ efs_client.GetInterfacePtr());
+
+ viz::mojom::CompositorFrameSinkPtr compositor_frame_sink;
+ viz::MockCompositorFrameSinkClient compositor_frame_sink_client;
+ // The embedder, kFrameSinkA, has already been invalidated and isn't
+ // registered at this point. This request should fail.
+ provider()->CreateCompositorFrameSink(
+ kFrameSinkB, compositor_frame_sink_client.BindInterfacePtr(),
+ mojo::MakeRequest(&compositor_frame_sink));
+
+ // The request will fail and trigger a connection error.
+ WaitForConnectionError(&compositor_frame_sink);
+}
+
+// Check that trying to create an EmbeddedFrameSinkImpl with a client id
+// that doesn't match the renderer fails.
+TEST_F(EmbeddedFrameSinkProviderImplTest, InvalidClientId) {
+ const viz::FrameSinkId invalid_frame_sink_id(4, 3);
+ EXPECT_NE(kRendererClientId, invalid_frame_sink_id.client_id());
+
+ StubEmbeddedFrameSinkClient efs_client;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, invalid_frame_sink_id,
+ efs_client.GetInterfacePtr());
+
+ RunUntilIdle();
+
+ // No EmbeddedFrameSinkImpl should have been created.
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+
+ // The connection for |efs_client| will have failed and triggered a
+ // connection error.
+ EXPECT_TRUE(efs_client.connection_error());
+}
+
+// Mimic renderer with two offscreen canvases.
+TEST_F(EmbeddedFrameSinkProviderImplTest,
+ MultiHTMLCanvasElementTransferToOffscreen) {
+ StubEmbeddedFrameSinkClient efs_client_a;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkA,
+ efs_client_a.GetInterfacePtr());
+
+ StubEmbeddedFrameSinkClient efs_client_b;
+ provider()->RegisterEmbeddedFrameSink(kFrameSinkParent, kFrameSinkB,
+ efs_client_b.GetInterfacePtr());
+
+ RunUntilIdle();
+
+ // There should be two EmbeddedFrameSinkImpls created.
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkA, kFrameSinkB));
+
+ // Mimic closing first connection from the renderer.
+ efs_client_a.Close();
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), ElementsAre(kFrameSinkB));
+
+ // Mimic closing second connection from the renderer.
+ efs_client_b.Close();
+
+ RunUntilIdle();
+
+ EXPECT_THAT(GetAllCanvases(), IsEmpty());
+}
+
+} // namespace content