OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/extensions/event_bindings.h" | 5 #include "chrome/renderer/extensions/event_bindings.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/singleton.h" | 8 #include "base/singleton.h" |
| 9 #include "chrome/common/render_messages.h" |
9 #include "chrome/renderer/extensions/bindings_utils.h" | 10 #include "chrome/renderer/extensions/bindings_utils.h" |
10 #include "chrome/renderer/extensions/event_bindings.h" | 11 #include "chrome/renderer/extensions/event_bindings.h" |
11 #include "chrome/renderer/js_only_v8_extensions.h" | 12 #include "chrome/renderer/js_only_v8_extensions.h" |
12 #include "chrome/renderer/render_thread.h" | 13 #include "chrome/renderer/render_thread.h" |
13 #include "grit/renderer_resources.h" | 14 #include "grit/renderer_resources.h" |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
| 18 // Keep a local cache of RenderThread so that we can mock it out for unit tests. |
| 19 static RenderThreadBase* render_thread = NULL; |
| 20 |
| 21 static RenderThreadBase* GetRenderThread() { |
| 22 return render_thread ? render_thread : RenderThread::current(); |
| 23 } |
| 24 |
17 // Keep a list of contexts that have registered themselves with us. This lets | 25 // Keep a list of contexts that have registered themselves with us. This lets |
18 // us know where to dispatch events when we receive them. | 26 // us know where to dispatch events when we receive them. |
19 typedef std::list< v8::Persistent<v8::Context> > ContextList; | 27 typedef std::list< v8::Persistent<v8::Context> > ContextList; |
20 struct ExtensionData { | 28 struct ExtensionData { |
21 ContextList contexts; | 29 ContextList contexts; |
| 30 std::map<std::string, int> listener_count; |
22 }; | 31 }; |
23 ContextList& GetRegisteredContexts() { | 32 ContextList& GetRegisteredContexts() { |
24 return Singleton<ExtensionData>::get()->contexts; | 33 return Singleton<ExtensionData>::get()->contexts; |
25 } | 34 } |
| 35 int EventIncrementListenerCount(const std::string& event_name) { |
| 36 ExtensionData *data = Singleton<ExtensionData>::get(); |
| 37 return ++(data->listener_count[event_name]); |
| 38 } |
| 39 int EventDecrementListenerCount(const std::string& event_name) { |
| 40 ExtensionData *data = Singleton<ExtensionData>::get(); |
| 41 return --(data->listener_count[event_name]); |
| 42 } |
| 43 |
26 | 44 |
27 const char* kExtensionDeps[] = { JsonJsV8Extension::kName }; | 45 const char* kExtensionDeps[] = { JsonJsV8Extension::kName }; |
28 const char* kContextAttachCount = "chromium.attachCount"; | 46 const char* kContextAttachCount = "chromium.attachCount"; |
29 | 47 |
30 class ExtensionImpl : public v8::Extension { | 48 class ExtensionImpl : public v8::Extension { |
31 public: | 49 public: |
32 ExtensionImpl() | 50 ExtensionImpl() |
33 : v8::Extension(EventBindings::kName, | 51 : v8::Extension(EventBindings::kName, |
34 GetStringResource<IDR_EVENT_BINDINGS_JS>(), | 52 GetStringResource<IDR_EVENT_BINDINGS_JS>(), |
35 arraysize(kExtensionDeps), kExtensionDeps) { | 53 arraysize(kExtensionDeps), kExtensionDeps) { |
36 } | 54 } |
37 ~ExtensionImpl() {} | 55 ~ExtensionImpl() {} |
38 | 56 |
39 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | 57 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
40 v8::Handle<v8::String> name) { | 58 v8::Handle<v8::String> name) { |
41 if (name->Equals(v8::String::New("AttachEvent"))) { | 59 if (name->Equals(v8::String::New("AttachEvent"))) { |
42 return v8::FunctionTemplate::New(AttachEvent); | 60 return v8::FunctionTemplate::New(AttachEvent); |
43 } else if (name->Equals(v8::String::New("DetachEvent"))) { | 61 } else if (name->Equals(v8::String::New("DetachEvent"))) { |
44 return v8::FunctionTemplate::New(DetachEvent); | 62 return v8::FunctionTemplate::New(DetachEvent); |
45 } | 63 } |
46 return v8::Handle<v8::FunctionTemplate>(); | 64 return v8::Handle<v8::FunctionTemplate>(); |
47 } | 65 } |
48 | 66 |
49 // Attach an event name to an object. | 67 // Attach an event name to an object. |
50 // TODO(mpcomplete): I'm just using this to register the v8 Context right now. | |
51 // The idea is to eventually notify the browser about what events are being | |
52 // listened to, so it can dispatch appropriately. | |
53 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { | 68 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { |
| 69 DCHECK(args.Length() == 1); |
| 70 // TODO(erikkay) should enforce that event name is a string in the bindings |
| 71 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); |
| 72 |
54 v8::Persistent<v8::Context> context = | 73 v8::Persistent<v8::Context> context = |
55 v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); | 74 v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); |
56 v8::Local<v8::Object> global = context->Global(); | 75 v8::Local<v8::Object> global = context->Global(); |
57 | 76 |
58 // Remember how many times this context has been attached, so we can | 77 // Remember how many times this context has been attached, so we can |
59 // register the context on first attach and unregister on last detach. | 78 // register the context on first attach and unregister on last detach. |
60 v8::Local<v8::Value> attach_count = global->GetHiddenValue( | 79 v8::Local<v8::Value> attach_count = global->GetHiddenValue( |
61 v8::String::New(kContextAttachCount)); | 80 v8::String::New(kContextAttachCount)); |
62 int32_t account_count_value = | 81 int32_t account_count_value = |
63 (!attach_count.IsEmpty() && attach_count->IsNumber()) ? | 82 (!attach_count.IsEmpty() && attach_count->IsNumber()) ? |
64 attach_count->Int32Value() : 0; | 83 attach_count->Int32Value() : 0; |
65 if (account_count_value == 0) { | 84 if (account_count_value == 0) { |
66 // First time attaching. | 85 // First time attaching. |
67 GetRegisteredContexts().push_back(context); | 86 GetRegisteredContexts().push_back(context); |
68 context.MakeWeak(NULL, WeakContextCallback); | 87 context.MakeWeak(NULL, WeakContextCallback); |
69 } | 88 } |
70 global->SetHiddenValue( | 89 global->SetHiddenValue( |
71 v8::String::New(kContextAttachCount), | 90 v8::String::New(kContextAttachCount), |
72 v8::Integer::New(account_count_value + 1)); | 91 v8::Integer::New(account_count_value + 1)); |
73 | 92 |
| 93 if (args[0]->IsString()) { |
| 94 std::string event_name(*v8::String::AsciiValue(args[0])); |
| 95 if (EventIncrementListenerCount(event_name) == 1) { |
| 96 GetRenderThread()->Send( |
| 97 new ViewHostMsg_ExtensionAddListener(event_name)); |
| 98 } |
| 99 } |
| 100 |
74 return v8::Undefined(); | 101 return v8::Undefined(); |
75 } | 102 } |
76 | 103 |
77 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { | 104 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { |
| 105 DCHECK(args.Length() == 1); |
| 106 // TODO(erikkay) should enforce that event name is a string in the bindings |
| 107 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); |
| 108 |
78 v8::Local<v8::Context> context = v8::Context::GetCurrent(); | 109 v8::Local<v8::Context> context = v8::Context::GetCurrent(); |
79 v8::Local<v8::Object> global = context->Global(); | 110 v8::Local<v8::Object> global = context->Global(); |
80 v8::Local<v8::Value> attach_count = global->GetHiddenValue( | 111 v8::Local<v8::Value> attach_count = global->GetHiddenValue( |
81 v8::String::New(kContextAttachCount)); | 112 v8::String::New(kContextAttachCount)); |
82 DCHECK(!attach_count.IsEmpty() && attach_count->IsNumber()); | 113 DCHECK(!attach_count.IsEmpty() && attach_count->IsNumber()); |
83 int32_t account_count_value = attach_count->Int32Value(); | 114 int32_t account_count_value = attach_count->Int32Value(); |
84 DCHECK(account_count_value > 0); | 115 DCHECK(account_count_value > 0); |
85 if (account_count_value == 1) { | 116 if (account_count_value == 1) { |
86 // Clean up after last detach. | 117 // Clean up after last detach. |
87 UnregisterContext(context); | 118 UnregisterContext(context); |
88 } | 119 } |
89 global->SetHiddenValue( | 120 global->SetHiddenValue( |
90 v8::String::New(kContextAttachCount), | 121 v8::String::New(kContextAttachCount), |
91 v8::Integer::New(account_count_value - 1)); | 122 v8::Integer::New(account_count_value - 1)); |
92 | 123 |
| 124 if (args[0]->IsString()) { |
| 125 std::string event_name(*v8::String::AsciiValue(args[0])); |
| 126 if (EventDecrementListenerCount(event_name) == 0) { |
| 127 GetRenderThread()->Send( |
| 128 new ViewHostMsg_ExtensionRemoveListener(event_name)); |
| 129 } |
| 130 } |
| 131 |
93 return v8::Undefined(); | 132 return v8::Undefined(); |
94 } | 133 } |
95 | 134 |
96 // Called when a registered context is garbage collected. | 135 // Called when a registered context is garbage collected. |
97 static void UnregisterContext(v8::Handle<void> context) { | 136 static void UnregisterContext(v8::Handle<void> context) { |
98 ContextList& contexts = GetRegisteredContexts(); | 137 ContextList& contexts = GetRegisteredContexts(); |
99 ContextList::iterator it = std::find(contexts.begin(), contexts.end(), | 138 ContextList::iterator it = std::find(contexts.begin(), contexts.end(), |
100 context); | 139 context); |
101 if (it == contexts.end()) { | 140 if (it == contexts.end()) { |
102 NOTREACHED(); | 141 NOTREACHED(); |
(...skipping 12 matching lines...) Expand all Loading... |
115 }; | 154 }; |
116 | 155 |
117 } // namespace | 156 } // namespace |
118 | 157 |
119 const char* EventBindings::kName = "chrome/EventBindings"; | 158 const char* EventBindings::kName = "chrome/EventBindings"; |
120 | 159 |
121 v8::Extension* EventBindings::Get() { | 160 v8::Extension* EventBindings::Get() { |
122 return new ExtensionImpl(); | 161 return new ExtensionImpl(); |
123 } | 162 } |
124 | 163 |
| 164 // static |
| 165 void EventBindings::SetRenderThread(RenderThreadBase* thread) { |
| 166 render_thread = thread; |
| 167 } |
| 168 |
125 void EventBindings::CallFunction(const std::string& function_name, | 169 void EventBindings::CallFunction(const std::string& function_name, |
126 int argc, v8::Handle<v8::Value>* argv) { | 170 int argc, v8::Handle<v8::Value>* argv) { |
127 for (ContextList::iterator it = GetRegisteredContexts().begin(); | 171 for (ContextList::iterator it = GetRegisteredContexts().begin(); |
128 it != GetRegisteredContexts().end(); ++it) { | 172 it != GetRegisteredContexts().end(); ++it) { |
129 DCHECK(!it->IsEmpty()); | 173 DCHECK(!it->IsEmpty()); |
130 v8::Context::Scope context_scope(*it); | 174 v8::Context::Scope context_scope(*it); |
131 v8::Local<v8::Object> global = (*it)->Global(); | 175 v8::Local<v8::Object> global = (*it)->Global(); |
132 | 176 |
133 v8::Local<v8::Script> script = v8::Script::Compile( | 177 v8::Local<v8::Script> script = v8::Script::Compile( |
134 v8::String::New(function_name.c_str())); | 178 v8::String::New(function_name.c_str())); |
135 v8::Local<v8::Value> function_obj = script->Run(); | 179 v8::Local<v8::Value> function_obj = script->Run(); |
136 if (!function_obj->IsFunction()) | 180 if (!function_obj->IsFunction()) |
137 continue; | 181 continue; |
138 | 182 |
139 v8::Local<v8::Function> function = | 183 v8::Local<v8::Function> function = |
140 v8::Local<v8::Function>::Cast(function_obj); | 184 v8::Local<v8::Function>::Cast(function_obj); |
141 if (!function.IsEmpty()) | 185 if (!function.IsEmpty()) |
142 function->Call(v8::Object::New(), argc, argv); | 186 function->Call(v8::Object::New(), argc, argv); |
143 } | 187 } |
144 } | 188 } |
OLD | NEW |