Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Side by Side Diff: chrome/renderer/extensions/event_bindings.cc

Issue 147033: Refactor extension bindings to share code, avoid exposing hidden variables (Closed)
Patch Set: at head Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/common/render_messages.h"
10 #include "chrome/common/url_constants.h"
10 #include "chrome/renderer/extensions/bindings_utils.h" 11 #include "chrome/renderer/extensions/bindings_utils.h"
11 #include "chrome/renderer/extensions/event_bindings.h" 12 #include "chrome/renderer/extensions/event_bindings.h"
12 #include "chrome/renderer/js_only_v8_extensions.h" 13 #include "chrome/renderer/js_only_v8_extensions.h"
13 #include "chrome/renderer/render_thread.h" 14 #include "chrome/renderer/render_thread.h"
15 #include "chrome/renderer/render_view.h"
14 #include "grit/renderer_resources.h" 16 #include "grit/renderer_resources.h"
15 #include "webkit/glue/webframe.h" 17 #include "webkit/glue/webframe.h"
16 18
19 using bindings_utils::CallFunctionInContext;
20 using bindings_utils::ContextInfo;
21 using bindings_utils::ContextList;
22 using bindings_utils::GetContexts;
23 using bindings_utils::GetStringResource;
24 using bindings_utils::ExtensionBase;
25 using bindings_utils::GetPendingRequestMap;
26 using bindings_utils::PendingRequest;
27 using bindings_utils::PendingRequestMap;
28
17 namespace { 29 namespace {
18 30
19 // Keep a local cache of RenderThread so that we can mock it out for unit tests. 31 // Keep a local cache of RenderThread so that we can mock it out for unit tests.
20 static RenderThreadBase* render_thread = NULL; 32 static RenderThreadBase* render_thread = NULL;
21 33
22 static RenderThreadBase* GetRenderThread() { 34 // Set to true if these bindings are registered. Will be false when extensions
23 return render_thread ? render_thread : RenderThread::current(); 35 // are disabled.
24 } 36 static bool bindings_registered = false;
25 37
26 // Keep a list of contexts that have registered themselves with us. This lets
27 // us know where to dispatch events when we receive them.
28 typedef std::list< v8::Persistent<v8::Context> > ContextList;
29 struct ExtensionData { 38 struct ExtensionData {
30 ContextList contexts;
31 std::map<std::string, int> listener_count; 39 std::map<std::string, int> listener_count;
32 }; 40 };
33 ContextList& GetRegisteredContexts() {
34 return Singleton<ExtensionData>::get()->contexts;
35 }
36 int EventIncrementListenerCount(const std::string& event_name) { 41 int EventIncrementListenerCount(const std::string& event_name) {
37 ExtensionData *data = Singleton<ExtensionData>::get(); 42 ExtensionData *data = Singleton<ExtensionData>::get();
38 return ++(data->listener_count[event_name]); 43 return ++(data->listener_count[event_name]);
39 } 44 }
40 int EventDecrementListenerCount(const std::string& event_name) { 45 int EventDecrementListenerCount(const std::string& event_name) {
41 ExtensionData *data = Singleton<ExtensionData>::get(); 46 ExtensionData *data = Singleton<ExtensionData>::get();
42 return --(data->listener_count[event_name]); 47 return --(data->listener_count[event_name]);
43 } 48 }
44 49
45 const char* kContextAttachCount = "chromium.attachCount"; 50 class ExtensionImpl : public ExtensionBase {
46
47 class ExtensionImpl : public v8::Extension {
48 public: 51 public:
49 ExtensionImpl() 52 ExtensionImpl()
50 : v8::Extension(EventBindings::kName, 53 : ExtensionBase(EventBindings::kName,
51 GetStringResource<IDR_EVENT_BINDINGS_JS>(), 54 GetStringResource<IDR_EVENT_BINDINGS_JS>(),
52 0, NULL) { 55 0, NULL) {
53 } 56 }
54 ~ExtensionImpl() {} 57 ~ExtensionImpl() {}
55 58
56 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 59 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
57 v8::Handle<v8::String> name) { 60 v8::Handle<v8::String> name) {
58 if (name->Equals(v8::String::New("AttachEvent"))) { 61 if (name->Equals(v8::String::New("AttachEvent"))) {
59 return v8::FunctionTemplate::New(AttachEvent); 62 return v8::FunctionTemplate::New(AttachEvent);
60 } else if (name->Equals(v8::String::New("DetachEvent"))) { 63 } else if (name->Equals(v8::String::New("DetachEvent"))) {
61 return v8::FunctionTemplate::New(DetachEvent); 64 return v8::FunctionTemplate::New(DetachEvent);
65 } else if (name->Equals(v8::String::New("GetNextRequestId"))) {
66 return v8::FunctionTemplate::New(GetNextRequestId);
62 } 67 }
63 return v8::Handle<v8::FunctionTemplate>(); 68 return ExtensionBase::GetNativeFunction(name);
64 } 69 }
65 70
66 // Attach an event name to an object. 71 // Attach an event name to an object.
67 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { 72 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) {
68 DCHECK(args.Length() == 1); 73 DCHECK(args.Length() == 1);
69 // TODO(erikkay) should enforce that event name is a string in the bindings 74 // TODO(erikkay) should enforce that event name is a string in the bindings
70 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); 75 DCHECK(args[0]->IsString() || args[0]->IsUndefined());
71 76
72 v8::Persistent<v8::Context> context =
73 v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
74 v8::Local<v8::Object> global = context->Global();
75
76 // Remember how many times this context has been attached, so we can
77 // register the context on first attach and unregister on last detach.
78 v8::Local<v8::Value> attach_count = global->GetHiddenValue(
79 v8::String::New(kContextAttachCount));
80 int32_t account_count_value =
81 (!attach_count.IsEmpty() && attach_count->IsNumber()) ?
82 attach_count->Int32Value() : 0;
83 if (account_count_value == 0) {
84 // First time attaching.
85 GetRegisteredContexts().push_back(context);
86 context.MakeWeak(NULL, WeakContextCallback);
87 }
88 global->SetHiddenValue(
89 v8::String::New(kContextAttachCount),
90 v8::Integer::New(account_count_value + 1));
91
92 if (args[0]->IsString()) { 77 if (args[0]->IsString()) {
93 std::string event_name(*v8::String::AsciiValue(args[0])); 78 std::string event_name(*v8::String::AsciiValue(args[0]));
94 if (EventIncrementListenerCount(event_name) == 1) { 79 if (EventIncrementListenerCount(event_name) == 1) {
95 GetRenderThread()->Send( 80 EventBindings::GetRenderThread()->Send(
96 new ViewHostMsg_ExtensionAddListener(event_name)); 81 new ViewHostMsg_ExtensionAddListener(event_name));
97 } 82 }
98 } 83 }
99 84
100 return v8::Undefined(); 85 return v8::Undefined();
101 } 86 }
102 87
103 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { 88 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) {
104 DCHECK(args.Length() == 1); 89 DCHECK(args.Length() == 1);
105 // TODO(erikkay) should enforce that event name is a string in the bindings 90 // TODO(erikkay) should enforce that event name is a string in the bindings
106 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); 91 DCHECK(args[0]->IsString() || args[0]->IsUndefined());
107 92
108 v8::Local<v8::Context> context = v8::Context::GetCurrent();
109 v8::Local<v8::Object> global = context->Global();
110 v8::Local<v8::Value> attach_count = global->GetHiddenValue(
111 v8::String::New(kContextAttachCount));
112 DCHECK(!attach_count.IsEmpty() && attach_count->IsNumber());
113 int32_t account_count_value = attach_count->Int32Value();
114 DCHECK(account_count_value > 0);
115 if (account_count_value == 1) {
116 // Clean up after last detach.
117 UnregisterContext(context);
118 }
119 global->SetHiddenValue(
120 v8::String::New(kContextAttachCount),
121 v8::Integer::New(account_count_value - 1));
122
123 if (args[0]->IsString()) { 93 if (args[0]->IsString()) {
124 std::string event_name(*v8::String::AsciiValue(args[0])); 94 std::string event_name(*v8::String::AsciiValue(args[0]));
125 if (EventDecrementListenerCount(event_name) == 0) { 95 if (EventDecrementListenerCount(event_name) == 0) {
126 GetRenderThread()->Send( 96 EventBindings::GetRenderThread()->Send(
127 new ViewHostMsg_ExtensionRemoveListener(event_name)); 97 new ViewHostMsg_ExtensionRemoveListener(event_name));
128 } 98 }
129 } 99 }
130 100
131 return v8::Undefined(); 101 return v8::Undefined();
132 } 102 }
133 103
134 // Called when a registered context is garbage collected. 104 static v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args) {
135 static void UnregisterContext(v8::Handle<void> context) { 105 static int next_request_id = 0;
136 ContextList& contexts = GetRegisteredContexts(); 106 return v8::Integer::New(next_request_id++);
137 ContextList::iterator it = std::find(contexts.begin(), contexts.end(),
138 context);
139 if (it == contexts.end()) {
140 NOTREACHED();
141 return;
142 }
143
144 it->Dispose();
145 it->Clear();
146 contexts.erase(it);
147 }
148
149 // Called when a registered context is garbage collected.
150 static void WeakContextCallback(v8::Persistent<v8::Value> obj, void*) {
151 UnregisterContext(obj);
152 } 107 }
153 }; 108 };
154 109
155 } // namespace 110 } // namespace
156 111
157 const char* EventBindings::kName = "chrome/EventBindings"; 112 const char* EventBindings::kName = "chrome/EventBindings";
158 113
159 v8::Extension* EventBindings::Get() { 114 v8::Extension* EventBindings::Get() {
115 bindings_registered = true;
160 return new ExtensionImpl(); 116 return new ExtensionImpl();
161 } 117 }
162 118
163 // static 119 // static
164 void EventBindings::SetRenderThread(RenderThreadBase* thread) { 120 void EventBindings::SetRenderThread(RenderThreadBase* thread) {
165 render_thread = thread; 121 render_thread = thread;
166 } 122 }
167 123
168 // static 124 // static
125 RenderThreadBase* EventBindings::GetRenderThread() {
126 return render_thread ? render_thread : RenderThread::current();
127 }
128
169 void EventBindings::HandleContextCreated(WebFrame* frame) { 129 void EventBindings::HandleContextCreated(WebFrame* frame) {
130 if (!bindings_registered)
131 return;
132
170 v8::HandleScope handle_scope; 133 v8::HandleScope handle_scope;
171 v8::Local<v8::Context> context = frame->GetScriptContext(); 134 v8::Local<v8::Context> context = frame->GetScriptContext();
172 DCHECK(!context.IsEmpty()); 135 DCHECK(!context.IsEmpty());
173 // TODO(mpcomplete): register it 136 DCHECK(bindings_utils::FindContext(context) == GetContexts().end());
137
138 GURL url = frame->GetView()->GetMainFrame()->GetURL();
139 std::string extension_id;
140 if (url.SchemeIs(chrome::kExtensionScheme))
141 extension_id = url.host();
142
143 v8::Persistent<v8::Context> persistent_context =
144 v8::Persistent<v8::Context>::New(context);
145 GetContexts().push_back(linked_ptr<ContextInfo>(
146 new ContextInfo(persistent_context, extension_id)));
174 } 147 }
175 148
176 // static 149 // static
177 void EventBindings::HandleContextDestroyed(WebFrame* frame) { 150 void EventBindings::HandleContextDestroyed(WebFrame* frame) {
151 if (!bindings_registered)
152 return;
153
178 v8::HandleScope handle_scope; 154 v8::HandleScope handle_scope;
179 v8::Local<v8::Context> context = frame->GetScriptContext(); 155 v8::Local<v8::Context> context = frame->GetScriptContext();
180 DCHECK(!context.IsEmpty()); 156 DCHECK(!context.IsEmpty());
181 // TODO(mpcomplete): unregister it, dispatch event 157
158 ContextList::iterator it = bindings_utils::FindContext(context);
159 DCHECK(it != GetContexts().end());
160
161 // Notify the bindings that they're going away.
162 CallFunctionInContext(context, "dispatchOnUnload", 0, NULL);
163
164 // Remove all pending requests for this context.
165 PendingRequestMap& pending_requests = GetPendingRequestMap();
166 for (PendingRequestMap::iterator it = pending_requests.begin();
167 it != pending_requests.end(); ) {
168 PendingRequestMap::iterator current = it++;
169 if (current->second->context == context) {
170 current->second->context.Dispose();
171 current->second->context.Clear();
172 pending_requests.erase(current);
173 }
174 }
175
176 // Remove it from our registered contexts.
177 (*it)->context.Dispose();
178 (*it)->context.Clear();
179 GetContexts().erase(it);
182 } 180 }
183 181
182 // static
184 void EventBindings::CallFunction(const std::string& function_name, 183 void EventBindings::CallFunction(const std::string& function_name,
185 int argc, v8::Handle<v8::Value>* argv) { 184 int argc, v8::Handle<v8::Value>* argv) {
186 for (ContextList::iterator it = GetRegisteredContexts().begin(); 185 v8::HandleScope handle_scope;
187 it != GetRegisteredContexts().end(); ++it) { 186 for (ContextList::iterator it = GetContexts().begin();
188 CallFunctionInContext(*it, function_name, argc, argv); 187 it != GetContexts().end(); ++it) {
188 CallFunctionInContext((*it)->context, function_name, argc, argv);
189 } 189 }
190 } 190 }
191
192 // static
193 void EventBindings::HandleResponse(int request_id, bool success,
194 const std::string& response,
195 const std::string& error) {
196 PendingRequest* request = GetPendingRequestMap()[request_id].get();
197 if (!request)
198 return; // The frame went away.
199
200 v8::HandleScope handle_scope;
201 v8::Handle<v8::Value> argv[5];
202 argv[0] = v8::Integer::New(request_id);
203 argv[1] = v8::String::New(request->name.c_str());
204 argv[2] = v8::Boolean::New(success);
205 argv[3] = v8::String::New(response.c_str());
206 argv[4] = v8::String::New(error.c_str());
207 CallFunctionInContext(
208 request->context, "handleResponse", arraysize(argv), argv);
209
210 GetPendingRequestMap().erase(request_id);
211 }
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/event_bindings.h ('k') | chrome/renderer/extensions/extension_api_client_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698