// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SURFACE_EMBED_RENDERER_SURFACE_EMBED_WEB_PLUGIN_H_
#define COMPONENTS_SURFACE_EMBED_RENDERER_SURFACE_EMBED_WEB_PLUGIN_H_

#include <memory>

#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/surface_layer.h"
#include "components/surface_embed/common/surface_embed.mojom.h"
#include "components/surface_embed/renderer/surface_embed_paint_holding_helper.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "ui/gfx/geometry/rect.h"

namespace blink {
struct WebPluginParams;
}  // namespace blink

namespace cc {
class DisplayItemList;
class PictureLayer;
}  // namespace cc

namespace content {
class RenderFrame;
}  // namespace content

namespace surface_embed {

// WebPlugin implementation that embeds a WebContents surface, identified via
// the `kInternalPluginMimeType` mime type and the data-content-id attribute on
// an <embed> element. The 1:1 browser process counterpart is the
// SurfaceEmbedHost.
class SurfaceEmbedWebPlugin : public blink::WebPlugin,
                              public mojom::SurfaceEmbed,
                              public cc::ContentLayerClient {
 public:
  static SurfaceEmbedWebPlugin* Create(content::RenderFrame* render_frame,
                                       const blink::WebPluginParams& params);

  SurfaceEmbedWebPlugin(const SurfaceEmbedWebPlugin&) = delete;
  SurfaceEmbedWebPlugin& operator=(const SurfaceEmbedWebPlugin&) = delete;

  // blink::WebPlugin implementation:
  bool Initialize(blink::WebPluginContainer* container) override;
  void Destroy() override;
  blink::WebPluginContainer* Container() const override;
  void UpdateAllLifecyclePhases(blink::DocumentUpdateReason reason) override;
  void Paint(cc::PaintCanvas* canvas, const gfx::Rect& rect) override;
  viz::FrameSinkId GetFrameSinkId() override;
  void UpdateGeometry(const gfx::Rect& window_rect,
                      const gfx::Rect& clip_rect,
                      const gfx::Rect& unobscured_rect,
                      bool is_visible) override;
  void UpdateFocus(bool focused, blink::mojom::FocusType focus_type) override;
  void UpdateVisibility(bool visible) override;
  void UpdateRenderThrottlingStatus(bool is_throttled,
                                    bool subtree_throttled,
                                    bool display_locked) override;
  blink::WebInputEventResult HandleInputEvent(
      const blink::WebCoalescedInputEvent& event,
      ui::Cursor* cursor) override;
  void DidReceiveResponse(const blink::WebURLResponse& response) override;
  void DidReceiveData(base::span<const char> data) override;
  void DidFinishLoading() override;
  void DidFailLoading(const blink::WebURLError& error) override;
  bool SupportsKeyboardFocus() const override;

  class AccessibilityObserver;

 private:
  // Destroy via ->Destroy().
  ~SurfaceEmbedWebPlugin() override;
  SurfaceEmbedWebPlugin(const base::UnguessableToken& contents_id,
                        content::RenderFrame* render_frame,
                        const blink::WebPluginParams& params);

  void InitializeSurfaceLayer();

  // Synchronizes visual properties (e.g. LocalSurfaceId, viewport size) with
  // the browser process.
  void SynchronizeVisualProperties(bool allow_paint_holding);

  // Called when the mojo channels disconnect.
  void OnHostDisconnected();

  void SendAccessibilityInfo();

  void OnAccessibilityModeEnabled();

  // mojom::SurfaceEmbed implementation:
  void SetFrameSinkId(const ::viz::FrameSinkId& frame_sink_id,
                      bool allow_paint_holding) override;
  void UpdateLocalSurfaceIdFromChild(
      const ::viz::LocalSurfaceId& local_surface_id) override;
  void ChildProcessGone() override;
  void RequestFocusOnEmbedElement(
      RequestFocusOnEmbedElementCallback callback) override;
  void AdvanceFocusFromEmbedElement(bool reverse) override;

  // cc::ContentLayerClient, used only if we're painting a sad plugin.
  scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() override;
  bool FillsBoundsCompletely() const override;

  void ReleaseCrashedLayer();

  // The child contents ID parsed from the `data-content-id` attribute.
  base::UnguessableToken contents_id_;

  raw_ptr<blink::WebPluginContainer> container_ = nullptr;
  scoped_refptr<cc::SurfaceLayer> layer_;
  // Layer we show as a fallback if the renderer process crashed.
  // Only set if needed.
  scoped_refptr<cc::PictureLayer> crashed_layer_;

  std::optional<blink::FrameVisualProperties> sent_visual_properties_;
  std::optional<bool> sent_last_is_visible_;

  gfx::Rect last_window_rect_;
  gfx::Rect last_clip_rect_;
  gfx::Rect last_unobscured_rect_;
  bool last_is_visible_ = false;
  bool last_is_throttled_ = false;
  bool last_subtree_throttled_ = false;
  bool last_display_locked_ = false;
  bool frame_sink_id_changed_ = false;

  viz::FrameSinkId frame_sink_id_;
  std::unique_ptr<viz::ParentLocalSurfaceIdAllocator>
      parent_local_surface_id_allocator_;
  SurfaceEmbedPaintHoldingHelper paint_holding_helper_;

  mojo::AssociatedRemote<mojom::SurfaceEmbedHost> host_;
  mojo::AssociatedReceiver<mojom::SurfaceEmbed> receiver_{this};

  std::unique_ptr<AccessibilityObserver> accessibility_observer_;
};

}  // namespace surface_embed

#endif  // COMPONENTS_SURFACE_EMBED_RENDERER_SURFACE_EMBED_WEB_PLUGIN_H_
