// Copyright 2015 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.

#ifndef CONTENT_BROWSER_ANDROID_JAVA_GIN_JAVA_BRIDGE_MESSAGE_FILTER_H_
#define CONTENT_BROWSER_ANDROID_JAVA_GIN_JAVA_BRIDGE_MESSAGE_FILTER_H_

#include <stdint.h>

#include <map>
#include <set>

#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "content/browser/android/java/gin_java_bound_object.h"
#include "content/common/android/gin_java_bridge_errors.h"
#include "content/public/browser/browser_message_filter.h"

namespace base {
class ListValue;
}

namespace IPC {
class Message;
}

namespace content {

class GinJavaBridgeDispatcherHost;
class RenderFrameHost;

class GinJavaBridgeMessageFilter : public BrowserMessageFilter {
 public:
  // BrowserMessageFilter
  void OnDestruct() const override;
  bool OnMessageReceived(const IPC::Message& message) override;
  base::TaskRunner* OverrideTaskRunnerForMessage(
      const IPC::Message& message) override;

  // Called on the UI thread.
  void AddRoutingIdForHost(GinJavaBridgeDispatcherHost* host,
                           RenderFrameHost* render_frame_host);
  void RemoveHost(GinJavaBridgeDispatcherHost* host);

  static scoped_refptr<GinJavaBridgeMessageFilter> FromHost(
      GinJavaBridgeDispatcherHost* host, bool create_if_not_exists);

  // Removes the filter, which triggers its deletion. Needs to be called when
  // the corresponding RenderProcessHost cleans itself up, e.g. on renderer
  // process exit.
  static void RemoveFilter(GinJavaBridgeDispatcherHost* host);

 private:
  friend class BrowserThread;
  friend class base::DeleteHelper<GinJavaBridgeMessageFilter>;

  // ContentViewCore (who owns GinJavaBridgeDispatcherHost) outlives
  // WebContents, and GinJavaBridgeDispatcherHost removes itself from the map on
  // WebContents destruction, so there is no risk that the pointer would become
  // stale.
  //
  // The filter keeps its own routing map of RenderFrames for two reasons:
  //  1. Message dispatching must be done on the background thread,
  //     without resorting to the UI thread, which can be in fact currently
  //     blocked, waiting for an event from an injected Java object.
  //  2. As RenderFrames pass away earlier than JavaScript wrappers,
  //     messages from the latter can arrive after the RenderFrame has been
  //     removed from the WebContents' routing table.
  typedef std::map<int32_t, GinJavaBridgeDispatcherHost*> HostMap;

  GinJavaBridgeMessageFilter();
  ~GinJavaBridgeMessageFilter() override;

  // Called on the background thread.
  GinJavaBridgeDispatcherHost* FindHost();
  void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
                    std::set<std::string>* returned_method_names);
  void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
                   const std::string& method_name,
                   bool* result);
  void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
                      const std::string& method_name,
                      const base::ListValue& arguments,
                      base::ListValue* result,
                      content::GinJavaBridgeError* error_code);
  void OnObjectWrapperDeleted(GinJavaBoundObject::ObjectID object_id);

  // Accessed both from UI and background threads.
  HostMap hosts_;
  base::Lock hosts_lock_;

  // The routing id of the RenderFrameHost whose request we are processing.
  // Used on the background thread.
  int32_t current_routing_id_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_ANDROID_JAVA_GIN_JAVA_BRIDGE_MESSAGE_FILTER_H_
