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

#ifndef EXTENSIONS_BROWSER_EXTENSION_FUNCTION_DISPATCHER_H_
#define EXTENSIONS_BROWSER_EXTENSION_FUNCTION_DISPATCHER_H_

#include <map>
#include <set>
#include <string>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "extensions/browser/extension_function.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/mojom/context_type.mojom-forward.h"
#include "extensions/common/mojom/frame.mojom.h"
#include "extensions/common/mojom/service_worker_host.mojom.h"

namespace content {
class BrowserContext;
class RenderFrameHost;
class RenderProcessHost;
class WebContents;
}

namespace extensions {

class Extension;
class ExtensionAPI;
class WindowController;

// ExtensionFunctionDispatcher receives requests to execute functions from
// Chrome extensions running in a RenderFrameHost and dispatches them to the
// appropriate handler. It lives entirely on the UI thread.
//
// ExtensionFunctionDispatcher should be a member of some class that hosts
// RenderFrameHosts and wants them to be able to display extension content.
// This class should also implement ExtensionFunctionDispatcher::Delegate.
//
// Note that a single ExtensionFunctionDispatcher does *not* correspond to a
// single RFH, a single extension, or a single URL. This is by design so that
// we can gracefully handle cases like WebContents, where the RFH, extension,
// and URL can all change over the lifetime of the tab. Instead, these items
// are all passed into each request.
class ExtensionFunctionDispatcher {
 public:
  class Delegate {
   public:
    // Returns the WindowController associated with this delegate, or NULL if no
    // window is associated with the delegate.
    virtual WindowController* GetExtensionWindowController();

   protected:
    virtual ~Delegate() {}
  };

  // Public constructor. Callers must ensure that:
  // - This object outlives any RenderFrameHost's passed to created
  //   ExtensionFunctions.
  explicit ExtensionFunctionDispatcher(
      content::BrowserContext* browser_context);
  ~ExtensionFunctionDispatcher();

  // Dispatches a request and the response is sent in `callback` that is a reply
  // of mojom::LocalFrameHost::Request.
  void Dispatch(mojom::RequestParamsPtr params,
                content::RenderFrameHost& frame,
                mojom::LocalFrameHost::RequestCallback callback);

  // Message handlers.
  // Dispatches a request for service worker and the response is sent to the
  // corresponding render process in an ExtensionMsg_ResponseWorker message.
  void DispatchForServiceWorker(
      mojom::RequestParamsPtr params,
      int render_process_id,
      mojom::ServiceWorkerHost::RequestWorkerCallback callback);

  // Called when an ExtensionFunction is done executing, after it has sent
  // a response (if any) to the extension.
  void OnExtensionFunctionCompleted(ExtensionFunction& extension_function);

  // See the Delegate class for documentation on these methods.
  // TODO(devlin): None of these belong here. We should kill
  // ExtensionFunctionDispatcher::Delegate.
  WindowController* GetExtensionWindowController();

  // The BrowserContext that this dispatcher is associated with.
  content::BrowserContext* browser_context() { return browser_context_; }

  void set_delegate(Delegate* delegate) { delegate_ = delegate; }

  // Adds a function object to the set of objects waiting for
  // responses from the renderer.
  void AddResponseTarget(ExtensionFunction* func);

  // Processes a response ack from a renderer.
  void ProcessResponseAck(const base::Uuid& request_uuid);

  base::WeakPtr<ExtensionFunctionDispatcher> AsWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  // Helper to create an ExtensionFunction to handle the function given by
  // `params`.
  // Does not set subclass properties, or include_incognito.
  scoped_refptr<ExtensionFunction> CreateExtensionFunction(
      const mojom::RequestParams& params_without_args,
      base::ListValue arguments,
      const Extension* extension,
      int requesting_process_id,
      bool is_worker_request,
      const GURL* render_frame_host_url,
      mojom::ContextType context_type,
      ExtensionAPI* api,
      ExtensionFunction::ResponseCallback callback,
      content::RenderFrameHost* render_frame_host);

  void DispatchWithCallbackInternal(
      mojom::RequestParamsPtr params,
      content::RenderFrameHost* render_frame_host,
      content::RenderProcessHost& render_process_host,
      ExtensionFunction::ResponseCallback callback);

  void RemoveWorkerCallbacksForProcess(int render_process_id);

  raw_ptr<content::BrowserContext, AcrossTasksDanglingUntriaged>
      browser_context_;

  raw_ptr<Delegate, AcrossTasksDanglingUntriaged> delegate_;

  // The set of ExtensionFunction instances waiting for responses from
  // the renderer. These are removed once the response is processed.
  // The lifetimes of the instances are managed by the instances themselves.
  std::set<raw_ptr<ExtensionFunction, SetExperimental>> response_targets_;

  base::WeakPtrFactory<ExtensionFunctionDispatcher> weak_ptr_factory_{this};
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_EXTENSION_FUNCTION_DISPATCHER_H_
