1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/java/gin_java_bridge_dispatcher.h"
6
7#include "base/strings/string_number_conversions.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/common/gin_java_bridge_messages.h"
10#include "content/public/renderer/render_frame.h"
11#include "content/renderer/java/gin_java_bridge_object.h"
12#include "third_party/WebKit/public/web/WebFrame.h"
13#include "third_party/WebKit/public/web/WebView.h"
14
15namespace content {
16
17GinJavaBridgeDispatcher::GinJavaBridgeDispatcher(RenderFrame* render_frame)
18    : RenderFrameObserver(render_frame),
19      inside_did_clear_window_object_(false) {
20}
21
22GinJavaBridgeDispatcher::~GinJavaBridgeDispatcher() {
23}
24
25bool GinJavaBridgeDispatcher::OnMessageReceived(const IPC::Message& msg) {
26  bool handled = true;
27  IPC_BEGIN_MESSAGE_MAP(GinJavaBridgeDispatcher, msg)
28    IPC_MESSAGE_HANDLER(GinJavaBridgeMsg_AddNamedObject, OnAddNamedObject)
29    IPC_MESSAGE_HANDLER(GinJavaBridgeMsg_RemoveNamedObject, OnRemoveNamedObject)
30    IPC_MESSAGE_UNHANDLED(handled = false)
31  IPC_END_MESSAGE_MAP()
32  return handled;
33}
34
35namespace {
36
37class ScopedFlag {
38 public:
39  ScopedFlag(bool* flag) : flag_(flag) {
40    DCHECK(!*flag_);
41    *flag_ = true;
42  }
43  ~ScopedFlag() {
44    DCHECK(*flag_);
45    *flag_ = false;
46  }
47 private:
48  bool* flag_;
49
50  DISALLOW_COPY_AND_ASSIGN(ScopedFlag);
51};
52
53}  // namespace
54
55void GinJavaBridgeDispatcher::DidClearWindowObject() {
56  // Accessing window object when adding properties to it may trigger
57  // a nested call to DidClearWindowObject.
58  if (inside_did_clear_window_object_)
59    return;
60  ScopedFlag flag(&inside_did_clear_window_object_);
61  for (NamedObjectMap::const_iterator iter = named_objects_.begin();
62       iter != named_objects_.end(); ++iter) {
63    // Always create a new GinJavaBridgeObject, so we don't pull any of the V8
64    // wrapper's custom properties into the context of the page we have
65    // navigated to. The old GinJavaBridgeObject will be automatically
66    // deleted after its wrapper will be collected.
67    // On the browser side, we ignore wrapper deletion events for named objects,
68    // as they are only removed upon embedder's request (RemoveNamedObject).
69    if (objects_.Lookup(iter->second))
70      objects_.Remove(iter->second);
71    GinJavaBridgeObject* object = GinJavaBridgeObject::InjectNamed(
72        render_frame()->GetWebFrame(), AsWeakPtr(), iter->first, iter->second);
73    if (object) {
74      objects_.AddWithID(object, iter->second);
75    } else {
76      // Inform the host about wrapper creation failure.
77      render_frame()->Send(new GinJavaBridgeHostMsg_ObjectWrapperDeleted(
78          routing_id(), iter->second));
79    }
80  }
81}
82
83void GinJavaBridgeDispatcher::OnAddNamedObject(
84    const std::string& name,
85    ObjectID object_id) {
86  // Added objects only become available after page reload, so here they
87  // are only added into the internal map.
88  named_objects_.insert(std::make_pair(name, object_id));
89}
90
91void GinJavaBridgeDispatcher::OnRemoveNamedObject(const std::string& name) {
92  // Removal becomes in effect on next reload. We simply removing the entry
93  // from the map here.
94  NamedObjectMap::iterator iter = named_objects_.find(name);
95  DCHECK(iter != named_objects_.end());
96  named_objects_.erase(iter);
97}
98
99void GinJavaBridgeDispatcher::GetJavaMethods(
100    ObjectID object_id,
101    std::set<std::string>* methods) {
102  render_frame()->Send(new GinJavaBridgeHostMsg_GetMethods(
103      routing_id(), object_id, methods));
104}
105
106bool GinJavaBridgeDispatcher::HasJavaMethod(ObjectID object_id,
107                                            const std::string& method_name) {
108  bool result;
109  render_frame()->Send(new GinJavaBridgeHostMsg_HasMethod(
110      routing_id(), object_id, method_name, &result));
111  return result;
112}
113
114scoped_ptr<base::Value> GinJavaBridgeDispatcher::InvokeJavaMethod(
115    ObjectID object_id,
116    const std::string& method_name,
117    const base::ListValue& arguments,
118    GinJavaBridgeError* error) {
119  base::ListValue result_wrapper;
120  render_frame()->Send(
121      new GinJavaBridgeHostMsg_InvokeMethod(routing_id(),
122                                            object_id,
123                                            method_name,
124                                            arguments,
125                                            &result_wrapper,
126                                            error));
127  base::Value* result;
128  if (result_wrapper.Get(0, &result)) {
129    return scoped_ptr<base::Value>(result->DeepCopy());
130  } else {
131    return scoped_ptr<base::Value>();
132  }
133}
134
135GinJavaBridgeObject* GinJavaBridgeDispatcher::GetObject(ObjectID object_id) {
136  GinJavaBridgeObject* result = objects_.Lookup(object_id);
137  if (!result) {
138    result = GinJavaBridgeObject::InjectAnonymous(AsWeakPtr(), object_id);
139    if (result)
140      objects_.AddWithID(result, object_id);
141  }
142  return result;
143}
144
145void GinJavaBridgeDispatcher::OnGinJavaBridgeObjectDeleted(ObjectID object_id) {
146  if (!objects_.Lookup(object_id))
147    return;
148  objects_.Remove(object_id);
149  render_frame()->Send(
150      new GinJavaBridgeHostMsg_ObjectWrapperDeleted(routing_id(), object_id));
151}
152
153}  // namespace content
154