| 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 |