diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-24 12:15:48 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:30:04 +0000 |
commit | b014812705fc80bff0a5c120dfcef88f349816dc (patch) | |
tree | 25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc | |
parent | 9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff) | |
download | qtwebengine-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.cc | 321 |
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 |