// Copyright 2013 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_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_
#define COMPONENTS_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_

#include <list>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner_helpers.h"
#include "base/task/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-forward.h"
#include "third_party/blink/public/mojom/widget/platform_widget.mojom.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_navigation_control.h"
#include "third_party/blink/public/web/web_non_composited_widget_client.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_view_client.h"
#include "third_party/blink/public/web/web_view_observer.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/ime/mojom/text_input_state.mojom.h"

namespace blink {
namespace web_pref {
struct WebPreferences;
}  // namespace web_pref
struct RendererPreferences;
class WebLocalFrame;
class WebMouseEvent;
}

// This class implements the WebPlugin interface by forwarding drawing and
// handling input events to a WebView.
// It can be used as a placeholder for an actual plugin, using HTML for the UI.
// To show HTML data inside the WebViewPlugin,
// call web_view->mainFrame()->loadHTMLString() with the HTML data and a fake
// chrome:// URL as origin.

class WebViewPlugin : public blink::WebPlugin, public blink::WebViewObserver {
 public:
  class Delegate {
   public:
    // Called to get the V8 handle used to bind the lifetime to the frame.
    virtual v8::Local<v8::Value> GetV8Handle(v8::Isolate*) = 0;

    // Called upon a context menu event.
    virtual void ShowContextMenu(const blink::WebMouseEvent&) = 0;

    // Called when the WebViewPlugin is destroyed.
    virtual void PluginDestroyed() = 0;

    // Called to enable JavaScript pass-through to a throttled plugin, which is
    // loaded but idle. Doesn't work for blocked plugins, which is not loaded.
    virtual v8::Local<v8::Object> GetV8ScriptableObject(v8::Isolate*) const = 0;

    // Called when the unobscured rect of the plugin is updated.
    virtual void OnUnobscuredRectUpdate(const gfx::Rect& unobscured_rect) {}

    virtual bool IsErrorPlaceholder() = 0;
  };

  // Convenience method to set up a new WebViewPlugin using |preferences|
  // and displaying |html_data|. |url| should be a (fake) data:text/html URL;
  // it is only used for navigation and never actually resolved.
  static WebViewPlugin* Create(
      blink::WebView* web_view,
      Delegate* delegate,
      const blink::web_pref::WebPreferences& preferences,
      const std::string& html_data,
      const GURL& url);

  blink::WebLocalFrame* main_frame() { return web_view_helper_.main_frame(); }

  const blink::WebString& old_title() const { return old_title_; }

  // When loading a plugin document (i.e. a full page plugin not embedded in
  // another page), we save all data that has been received, and replay it with
  // this method on the actual plugin.
  void ReplayReceivedData(blink::WebPlugin* plugin);

  // WebPlugin methods:
  blink::WebPluginContainer* Container() const override;
  // The WebViewPlugin, by design, never fails to initialize. It's used to
  // display placeholders and error messages, so it must never fail.
  bool Initialize(blink::WebPluginContainer*) override;
  void Destroy() override;

  v8::Local<v8::Object> V8ScriptableObject(v8::Isolate* isolate) override;

  bool IsErrorPlaceholder() override;

  void UpdateAllLifecyclePhases(blink::DocumentUpdateReason reason) override;
  void Paint(cc::PaintCanvas* canvas, const gfx::Rect& rect) override;

  // Coordinates are relative to the containing window.
  void UpdateGeometry(const gfx::Rect& window_rect,
                      const gfx::Rect& clip_rect,
                      const gfx::Rect& unobscured_rect,
                      bool is_visible) override;

  void UpdateFocus(bool foucsed, blink::mojom::FocusType focus_type) override;
  void UpdateVisibility(bool) 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;

 private:
  friend class base::DeleteHelper<WebViewPlugin>;
  WebViewPlugin(blink::WebView* web_view,
                Delegate* delegate,
                const blink::web_pref::WebPreferences& preferences);
  ~WebViewPlugin() override;

  blink::WebView* web_view() { return web_view_helper_.web_view(); }

  // blink::WebViewObserver methods:
  void OnDestruct() override {}
  void OnZoomLevelChanged() override;

  void LoadHTML(const std::string& html_data, const GURL& url);
  void UpdatePluginForNewGeometry(const gfx::Rect& window_rect,
                                  const gfx::Rect& unobscured_rect);

  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();

  // Manages its own lifetime.
  raw_ptr<Delegate> delegate_;

  ui::Cursor current_cursor_;

  // Owns us.
  raw_ptr<blink::WebPluginContainer> container_;

  gfx::Rect rect_;

  blink::WebURLResponse response_;
  std::list<std::string> data_;
  std::unique_ptr<blink::WebURLError> error_;
  blink::WebString old_title_;
  bool finished_loading_;
  bool focused_;
  bool is_painting_;
  bool is_resizing_;

  // A helper that handles interaction from WebViewPlugin's internal WebView.
  class WebViewHelper : public blink::WebViewClient,
                        public blink::WebNonCompositedWidgetClient,
                        public blink::WebLocalFrameClient,
                        public blink::mojom::WidgetHost {
   public:
    WebViewHelper(
        WebViewPlugin* plugin,
        const blink::web_pref::WebPreferences& parent_web_preferences,
        const blink::RendererPreferences& parent_renderer_preferences);
    ~WebViewHelper() override;

    blink::WebView* web_view() { return web_view_; }
    blink::WebNavigationControl* main_frame() { return frame_; }

    // WebViewClient methods:
    void InvalidateContainer() override;

    // WebNonCompositedWidgetClient overrides.
    void ScheduleNonCompositedAnimation() override;

    // WebLocalFrameClient methods:
    void BindToFrame(blink::WebNavigationControl* frame) override;
    void DidClearWindowObject() override;
    void FrameDetached(blink::DetachReason) override;
    scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
        override;

    // blink::mojom::WidgetHost implementation.
    void SetCursor(const ui::Cursor& cursor) override;
    void UpdateTooltipUnderCursor(const std::u16string& tooltip_text,
                                  base::i18n::TextDirection hint) override;
    void UpdateTooltipFromKeyboard(const std::u16string& tooltip_text,
                                   base::i18n::TextDirection hint,
                                   const gfx::Rect& bounds) override;
    void ClearKeyboardTriggeredTooltip() override;
    void TextInputStateChanged(ui::mojom::TextInputStatePtr state) override {}
    void SelectionBoundsChanged(const gfx::Rect& anchor_rect,
                                base::i18n::TextDirection anchor_dir,
                                const gfx::Rect& focus_rect,
                                base::i18n::TextDirection focus_dir,
                                const gfx::Rect& bounding_box,
                                bool is_anchor_first) override {}
    void CreateFrameSink(
        mojo::PendingReceiver<viz::mojom::CompositorFrameSink>
            compositor_frame_sink_receiver,
        mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient>,
        mojo::PendingRemote<blink::mojom::RenderInputRouterClient>) override {}
    void RegisterRenderFrameMetadataObserver(
        mojo::PendingReceiver<cc::mojom::RenderFrameMetadataObserverClient>
            render_frame_metadata_observer_client_receiver,
        mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserver>
            render_frame_metadata_observer) override {}

    // This function sets the "title" attribute to the text value passed by
    // parameter on the container's element, if possible.
    void UpdateTooltip(const std::u16string& tooltip_text);

   private:
    raw_ptr<WebViewPlugin> plugin_;
    raw_ptr<blink::WebNavigationControl> frame_ = nullptr;

    std::unique_ptr<blink::scheduler::WebAgentGroupScheduler>
        agent_group_scheduler_;

    // Owned by us, deleted via |close()|.
    raw_ptr<blink::WebView, DanglingUntriaged> web_view_;

    mojo::AssociatedReceiver<blink::mojom::WidgetHost>
        blink_widget_host_receiver_{this};
    mojo::AssociatedRemote<blink::mojom::Widget> blink_widget_;
  };
  WebViewHelper web_view_helper_;

  // Should be invalidated when destroy() is called.
  base::WeakPtrFactory<WebViewPlugin> weak_factory_{this};
};

#endif  // COMPONENTS_PLUGINS_RENDERER_WEBVIEW_PLUGIN_H_
