text: // Copyright 2016 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 "third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.h"
  
  #include "base/test/scoped_feature_list.h"
  #include "build/build_config.h"
  #include "components/viz/test/test_context_provider.h"
  #include "components/viz/test/test_gles2_interface.h"
  #include "mojo/public/cpp/bindings/receiver.h"
  #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "third_party/blink/public/common/features.h"
  #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h"
  #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
  #include "third_party/blink/renderer/core/dom/document.h"
  #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
  #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
  #include "third_party/blink/renderer/core/frame/local_dom_window.h"
  #include "third_party/blink/renderer/core/frame/local_frame_view.h"
  #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
  #include "third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h"
  #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
  #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
  #include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
  #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
  #include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
  #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
  #include "third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h"
  #include "third_party/blink/renderer/platform/graphics/test/mock_embedded_frame_sink_provider.h"
  #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
  
  using ::testing::_;
  using ::testing::Values;
  
  namespace blink {
  
  namespace {
  
  // This class allows for overriding GenerateFrameSinkId() so that the
  // HTMLCanvasElement's SurfaceLayerBridge will get a syntactically correct
  // FrameSinkId.  It also returns a valid GpuMemoryBufferManager so that low
  // latency mode is enabled.
  class LowLatencyTestPlatform : public GpuMemoryBufferTestPlatform {
   public:
    viz::FrameSinkId GenerateFrameSinkId() override {
      // Doesn't matter what we return as long as is not zero.
      constexpr uint32_t kClientId = 2;
      constexpr uint32_t kSinkId = 1;
      return viz::FrameSinkId(kClientId, kSinkId);
    }
  };
  
  }  // unnamed namespace
  
  class HTMLCanvasElementModuleTest : public ::testing::Test,
                                      public ::testing::WithParamInterface<bool> {
   protected:
    void SetUp() override {
      web_view_helper_.Initialize();
      GetDocument().documentElement()->setInnerHTML(
          String::FromUTF8("<body><canvas id='c'></canvas></body>"));
      canvas_element_ = To<HTMLCanvasElement>(GetDocument().getElementById("c"));
    }
  
    Document& GetDocument() const {
      return *web_view_helper_.GetWebView()
                  ->MainFrameImpl()
                  ->GetFrame()
                  ->DomWindow()
                  ->document();
    }
  
    HTMLCanvasElement& canvas_element() const { return *canvas_element_; }
    OffscreenCanvas* TransferControlToOffscreen(ExceptionState& exception_state) {
      return HTMLCanvasElementModule::TransferControlToOffscreenInternal(
          GetDocument().ToExecutionContext(), canvas_element(), exception_state);
    }
  
    frame_test_helpers::WebViewHelper web_view_helper_;
    Persistent<HTMLCanvasElement> canvas_element_;
    Persistent<CanvasRenderingContext> context_;
  };
  
  // Tests if the Canvas Id is associated correctly.
  TEST_F(HTMLCanvasElementModuleTest, TransferControlToOffscreen) {
    NonThrowableExceptionState exception_state;
    const OffscreenCanvas* offscreen_canvas =
        TransferControlToOffscreen(exception_state);
    const DOMNodeId canvas_id = offscreen_canvas->PlaceholderCanvasId();
    EXPECT_EQ(canvas_id, DOMNodeIds::IdForNode(&(canvas_element())));
  }
  
  // Verifies that a desynchronized canvas has the appropriate opacity/blending
  // information sent to the CompositorFrameSink.
  TEST_P(HTMLCanvasElementModuleTest, LowLatencyCanvasCompositorFrameOpacity) {
    // TODO(crbug.com/922218): enable desynchronized on Mac.
  #if !defined(OS_MACOSX)
    // This test relies on GpuMemoryBuffers being supported and enabled for low
    // latency canvas.  The latter is true only on ChromeOS in production.
    ScopedTestingPlatformSupport<LowLatencyTestPlatform> platform;
    base::test::ScopedFeatureList feature_list;
    feature_list.InitAndEnableFeature(features::kLowLatencyCanvas2dImageChromium);
  
    auto context_provider = viz::TestContextProvider::Create();
    context_provider->UnboundTestContextGL()
        ->set_supports_gpu_memory_buffer_format(
            CanvasColorParams().GetBufferFormat(), true);
    InitializeSharedGpuContext(context_provider.get());
  
    // To intercept SubmitCompositorFrame/SubmitCompositorFrameSync messages sent
    // by a canvas's CanvasResourceDispatcher, we have to override the Mojo
    // EmbeddedFrameSinkProvider interface impl and its CompositorFrameSinkClient.
    MockEmbeddedFrameSinkProvider mock_embedded_frame_sink_provider;
    mojo::Receiver<mojom::blink::EmbeddedFrameSinkProvider>
        embedded_frame_sink_provider_receiver(&mock_embedded_frame_sink_provider);
    auto override =
        mock_embedded_frame_sink_provider.CreateScopedOverrideMojoInterface(
            &embedded_frame_sink_provider_receiver);
  
    const bool context_alpha = GetParam();
    CanvasContextCreationAttributesCore attrs;
    attrs.alpha = context_alpha;
    attrs.desynchronized = true;
    // |context_| creation triggers a SurfaceLayerBridge creation which connects
    // to a MockEmbeddedFrameSinkProvider to create a new CompositorFrameSink,
    // that will receive a SetNeedsBeginFrame() upon construction.
    mock_embedded_frame_sink_provider
        .set_num_expected_set_needs_begin_frame_on_sink_construction(1);
    EXPECT_CALL(mock_embedded_frame_sink_provider, CreateCompositorFrameSink_(_));
    context_ = canvas_element().GetCanvasRenderingContext(String("2d"), attrs);
    EXPECT_EQ(context_->CreationAttributes().alpha, attrs.alpha);
    EXPECT_TRUE(context_->CreationAttributes().desynchronized);
    EXPECT_TRUE(canvas_element().LowLatencyEnabled());
    EXPECT_TRUE(canvas_element().SurfaceLayerBridge());
    platform->RunUntilIdle();
  
    // This call simulates having drawn something before FinalizeFrame().
    canvas_element().DidDraw();
  
    EXPECT_CALL(mock_embedded_frame_sink_provider.mock_compositor_frame_sink(),
                SubmitCompositorFrame_(_))
        .WillOnce(::testing::WithArg<0>(
            ::testing::Invoke([context_alpha](const viz::CompositorFrame* frame) {
              ASSERT_EQ(frame->render_pass_list.size(), 1u);
  
              const auto& quad_list = frame->render_pass_list[0]->quad_list;
              ASSERT_EQ(quad_list.size(), 1u);
              EXPECT_EQ(quad_list.front()->needs_blending, context_alpha);
  
              const auto& shared_quad_state_list =
                  frame->render_pass_list[0]->shared_quad_state_list;
              ASSERT_EQ(shared_quad_state_list.size(), 1u);
              EXPECT_NE(shared_quad_state_list.front()->are_contents_opaque,
                        context_alpha);
            })));
    canvas_element().PreFinalizeFrame();
    context_->FinalizeFrame();
    canvas_element().PostFinalizeFrame();
    platform->RunUntilIdle();
  
    SharedGpuContext::ResetForTesting();
  #endif
  }
  
  INSTANTIATE_TEST_SUITE_P(All, HTMLCanvasElementModuleTest, Values(true, false));
  }  // namespace blink
  