OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "chrome/renderer/extensions/extension_process_bindings.h" | |
6 | |
7 #include <map> | |
8 #include <set> | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/callback.h" | |
13 #include "base/command_line.h" | |
14 #include "base/json/json_reader.h" | |
15 #include "base/lazy_instance.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/string_number_conversions.h" | |
18 #include "base/string_util.h" | |
19 #include "base/utf_string_conversions.h" | |
20 #include "chrome/common/extensions/extension.h" | |
21 #include "chrome/common/extensions/extension_action.h" | |
22 #include "chrome/common/extensions/extension_constants.h" | |
23 #include "chrome/common/extensions/extension_messages.h" | |
24 #include "chrome/common/extensions/extension_set.h" | |
25 #include "chrome/common/extensions/url_pattern.h" | |
26 #include "chrome/common/render_messages.h" | |
27 #include "chrome/common/url_constants.h" | |
28 #include "chrome/common/chrome_view_types.h" | |
29 #include "content/public/renderer/render_view.h" | |
30 #include "content/public/renderer/render_view_visitor.h" | |
31 #include "chrome/renderer/chrome_render_process_observer.h" | |
32 #include "chrome/renderer/extensions/chrome_v8_context.h" | |
33 #include "chrome/renderer/extensions/chrome_v8_context_set.h" | |
34 #include "chrome/renderer/extensions/chrome_v8_extension.h" | |
35 #include "chrome/renderer/extensions/event_bindings.h" | |
36 #include "chrome/renderer/extensions/extension_dispatcher.h" | |
37 #include "chrome/renderer/extensions/extension_helper.h" | |
38 #include "chrome/renderer/extensions/renderer_extension_bindings.h" | |
39 #include "chrome/renderer/extensions/user_script_slave.h" | |
40 #include "chrome/renderer/static_v8_external_string_resource.h" | |
41 #include "content/public/renderer/render_view.h" | |
42 #include "grit/common_resources.h" | |
43 #include "grit/renderer_resources.h" | |
44 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBlob.h" | |
45 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
46 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
47 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
48 #include "third_party/skia/include/core/SkBitmap.h" | |
49 #include "third_party/skia/include/core/SkColor.h" | |
50 #include "ui/base/resource/resource_bundle.h" | |
51 #include "v8/include/v8.h" | |
52 #include "webkit/glue/webkit_glue.h" | |
53 | |
54 using WebKit::WebFrame; | |
55 using WebKit::WebView; | |
56 | |
57 namespace { | |
58 | |
59 const char* kExtensionDeps[] = { | |
60 "extensions/event.js", | |
61 "extensions/json_schema.js", | |
62 "extensions/renderer_extension_bindings.js", | |
63 "extensions/apitest.js" | |
64 }; | |
65 | |
66 // Contains info relevant to a pending API request. | |
67 struct PendingRequest { | |
68 public : | |
69 PendingRequest(v8::Persistent<v8::Context> context, const std::string& name, | |
70 const std::string& extension_id) | |
71 : context(context), name(name), extension_id(extension_id) { | |
72 } | |
73 v8::Persistent<v8::Context> context; | |
74 std::string name; | |
75 std::string extension_id; | |
76 }; | |
77 typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap; | |
78 | |
79 base::LazyInstance<PendingRequestMap> g_pending_requests( | |
80 base::LINKER_INITIALIZED); | |
81 | |
82 // A RenderViewVisitor class that iterates through the set of available | |
83 // views, looking for a view of the given type, in the given browser window | |
84 // and within the given extension. | |
85 // Used to accumulate the list of views associated with an extension. | |
86 class ExtensionViewAccumulator : public content::RenderViewVisitor { | |
87 public: | |
88 ExtensionViewAccumulator(const std::string& extension_id, | |
89 int browser_window_id, | |
90 content::ViewType view_type) | |
91 : extension_id_(extension_id), | |
92 browser_window_id_(browser_window_id), | |
93 view_type_(view_type), | |
94 views_(v8::Array::New()), | |
95 index_(0) { | |
96 } | |
97 | |
98 v8::Local<v8::Array> views() { return views_; } | |
99 | |
100 virtual bool Visit(content::RenderView* render_view) { | |
101 ExtensionHelper* helper = ExtensionHelper::Get(render_view); | |
102 if (!ViewTypeMatches(helper->view_type(), view_type_)) | |
103 return true; | |
104 | |
105 GURL url = render_view->GetWebView()->mainFrame()->document().url(); | |
106 if (!url.SchemeIs(chrome::kExtensionScheme)) | |
107 return true; | |
108 const std::string& extension_id = url.host(); | |
109 if (extension_id != extension_id_) | |
110 return true; | |
111 | |
112 if (browser_window_id_ != extension_misc::kUnknownWindowId && | |
113 helper->browser_window_id() != browser_window_id_) { | |
114 return true; | |
115 } | |
116 | |
117 v8::Local<v8::Context> context = | |
118 render_view->GetWebView()->mainFrame()->mainWorldScriptContext(); | |
119 if (!context.IsEmpty()) { | |
120 v8::Local<v8::Value> window = context->Global(); | |
121 DCHECK(!window.IsEmpty()); | |
122 | |
123 if (!OnMatchedView(window)) | |
124 return false; | |
125 } | |
126 return true; | |
127 } | |
128 | |
129 private: | |
130 // Called on each view found matching the search criteria. Returns false | |
131 // to terminate the iteration. | |
132 bool OnMatchedView(v8::Local<v8::Value> view_window) { | |
133 views_->Set(v8::Integer::New(index_), view_window); | |
134 index_++; | |
135 | |
136 if (view_type_ == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) | |
137 return false; // There can be only one... | |
138 | |
139 return true; | |
140 } | |
141 | |
142 // Returns true is |type| "isa" |match|. | |
143 static bool ViewTypeMatches(content::ViewType type, content::ViewType match) { | |
144 if (type == match) | |
145 return true; | |
146 | |
147 // INVALID means match all. | |
148 if (match == content::VIEW_TYPE_INVALID) | |
149 return true; | |
150 | |
151 return false; | |
152 } | |
153 | |
154 std::string extension_id_; | |
155 int browser_window_id_; | |
156 content::ViewType view_type_; | |
157 v8::Local<v8::Array> views_; | |
158 int index_; | |
159 }; | |
160 | |
161 class ExtensionImpl : public ChromeV8Extension { | |
162 public: | |
163 explicit ExtensionImpl(ExtensionDispatcher* extension_dispatcher) | |
164 : ChromeV8Extension("extensions/extension_process_bindings.js", | |
165 IDR_EXTENSION_PROCESS_BINDINGS_JS, | |
166 arraysize(kExtensionDeps), | |
167 kExtensionDeps, | |
168 extension_dispatcher) { | |
169 } | |
170 ~ExtensionImpl() {} | |
171 | |
172 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | |
173 v8::Handle<v8::String> name) { | |
174 if (name->Equals(v8::String::New("GetExtensionAPIDefinition"))) { | |
175 return v8::FunctionTemplate::New(GetExtensionAPIDefinition); | |
176 } else if (name->Equals(v8::String::New("GetExtensionViews"))) { | |
177 return v8::FunctionTemplate::New(GetExtensionViews, | |
178 v8::External::New(this)); | |
179 } else if (name->Equals(v8::String::New("GetNextRequestId"))) { | |
180 return v8::FunctionTemplate::New(GetNextRequestId); | |
181 } else if (name->Equals(v8::String::New("OpenChannelToTab"))) { | |
182 return v8::FunctionTemplate::New(OpenChannelToTab); | |
183 } else if (name->Equals(v8::String::New("GetNextContextMenuId"))) { | |
184 return v8::FunctionTemplate::New(GetNextContextMenuId); | |
185 } else if (name->Equals(v8::String::New("GetNextTtsEventId"))) { | |
186 return v8::FunctionTemplate::New(GetNextTtsEventId); | |
187 } else if (name->Equals(v8::String::New("GetCurrentPageActions"))) { | |
188 return v8::FunctionTemplate::New(GetCurrentPageActions, | |
189 v8::External::New(this)); | |
190 } else if (name->Equals(v8::String::New("StartRequest"))) { | |
191 return v8::FunctionTemplate::New(StartRequest, | |
192 v8::External::New(this)); | |
193 } else if (name->Equals(v8::String::New("GetRenderViewId"))) { | |
194 return v8::FunctionTemplate::New(GetRenderViewId); | |
195 } else if (name->Equals(v8::String::New("SetIconCommon"))) { | |
196 return v8::FunctionTemplate::New(SetIconCommon, | |
197 v8::External::New(this)); | |
198 } else if (name->Equals(v8::String::New("GetUniqueSubEventName"))) { | |
199 return v8::FunctionTemplate::New(GetUniqueSubEventName); | |
200 } else if (name->Equals(v8::String::New("GetLocalFileSystem"))) { | |
201 return v8::FunctionTemplate::New(GetLocalFileSystem); | |
202 } else if (name->Equals(v8::String::New("DecodeJPEG"))) { | |
203 return v8::FunctionTemplate::New(DecodeJPEG, v8::External::New(this)); | |
204 } else if (name->Equals(v8::String::New("CreateBlob"))) { | |
205 return v8::FunctionTemplate::New(CreateBlob, v8::External::New(this)); | |
206 } | |
207 | |
208 return ChromeV8Extension::GetNativeFunction(name); | |
209 } | |
210 | |
211 private: | |
212 static v8::Handle<v8::Value> GetExtensionAPIDefinition( | |
213 const v8::Arguments& args) { | |
214 return v8::String::NewExternal( | |
215 new StaticV8ExternalAsciiStringResource( | |
216 ResourceBundle::GetSharedInstance().GetRawDataResource( | |
217 IDR_EXTENSION_API_JSON))); | |
218 } | |
219 | |
220 static v8::Handle<v8::Value> GetExtensionViews(const v8::Arguments& args) { | |
221 if (args.Length() != 2) | |
222 return v8::Undefined(); | |
223 | |
224 if (!args[0]->IsInt32() || !args[1]->IsString()) | |
225 return v8::Undefined(); | |
226 | |
227 // |browser_window_id| == extension_misc::kUnknownWindowId means getting | |
228 // views attached to any browser window. | |
229 int browser_window_id = args[0]->Int32Value(); | |
230 | |
231 std::string view_type_string = *v8::String::Utf8Value(args[1]->ToString()); | |
232 StringToUpperASCII(&view_type_string); | |
233 // |view_type| == content::VIEW_TYPE_INVALID means getting any type of | |
234 // views. | |
235 content::ViewType view_type = content::VIEW_TYPE_INVALID; | |
236 if (view_type_string == chrome::kViewTypeBackgroundPage) { | |
237 view_type = chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE; | |
238 } else if (view_type_string == chrome::kViewTypeInfobar) { | |
239 view_type = chrome::VIEW_TYPE_EXTENSION_INFOBAR; | |
240 } else if (view_type_string == chrome::kViewTypeNotification) { | |
241 view_type = chrome::VIEW_TYPE_NOTIFICATION; | |
242 } else if (view_type_string == chrome::kViewTypeTabContents) { | |
243 view_type = content::VIEW_TYPE_TAB_CONTENTS; | |
244 } else if (view_type_string == chrome::kViewTypePopup) { | |
245 view_type = chrome::VIEW_TYPE_EXTENSION_POPUP; | |
246 } else if (view_type_string == chrome::kViewTypeExtensionDialog) { | |
247 view_type = chrome::VIEW_TYPE_EXTENSION_DIALOG; | |
248 } else if (view_type_string != chrome::kViewTypeAll) { | |
249 return v8::Undefined(); | |
250 } | |
251 | |
252 ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); | |
253 const ::Extension* extension = | |
254 v8_extension->GetExtensionForCurrentRenderView(); | |
255 if (!extension) | |
256 return v8::Undefined(); | |
257 | |
258 ExtensionViewAccumulator accumulator(extension->id(), browser_window_id, | |
259 view_type); | |
260 content::RenderView::ForEach(&accumulator); | |
261 return accumulator.views(); | |
262 } | |
263 | |
264 static v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args) { | |
265 static int next_request_id = 0; | |
266 return v8::Integer::New(next_request_id++); | |
267 } | |
268 | |
269 // Attach an event name to an object. | |
270 static v8::Handle<v8::Value> GetUniqueSubEventName( | |
271 const v8::Arguments& args) { | |
272 static int next_event_id = 0; | |
273 DCHECK(args.Length() == 1); | |
274 DCHECK(args[0]->IsString()); | |
275 std::string event_name(*v8::String::AsciiValue(args[0])); | |
276 std::string unique_event_name = | |
277 event_name + "/" + base::IntToString(++next_event_id); | |
278 return v8::String::New(unique_event_name.c_str()); | |
279 } | |
280 | |
281 static v8::Handle<v8::Value> GetLocalFileSystem( | |
282 const v8::Arguments& args) { | |
283 DCHECK(args.Length() == 2); | |
284 DCHECK(args[0]->IsString()); | |
285 DCHECK(args[1]->IsString()); | |
286 std::string name(*v8::String::Utf8Value(args[0])); | |
287 std::string path(*v8::String::Utf8Value(args[1])); | |
288 | |
289 WebFrame* webframe = WebFrame::frameForCurrentContext(); | |
290 return webframe->createFileSystem(WebKit::WebFileSystem::TypeExternal, | |
291 WebKit::WebString::fromUTF8(name.c_str()), | |
292 WebKit::WebString::fromUTF8(path.c_str())); | |
293 } | |
294 | |
295 // Decodes supplied JPEG byte array to image pixel array. | |
296 static v8::Handle<v8::Value> DecodeJPEG(const v8::Arguments& args) { | |
297 static const char* kAllowedIds[] = { | |
298 "haiffjcadagjlijoggckpgfnoeiflnem", | |
299 "gnedhmakppccajfpfiihfcdlnpgomkcf", | |
300 "fjcibdnjlbfnbfdjneajpipnlcppleek", | |
301 "oflbaaikkabfdfkimeclgkackhdkpnip" // Testing extension. | |
302 }; | |
303 static const std::vector<std::string> allowed_ids( | |
304 kAllowedIds, kAllowedIds + arraysize(kAllowedIds)); | |
305 | |
306 ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); | |
307 const ::Extension* extension = | |
308 v8_extension->GetExtensionForCurrentRenderView(); | |
309 if (!extension) | |
310 return v8::Undefined(); | |
311 if (allowed_ids.end() == std::find( | |
312 allowed_ids.begin(), allowed_ids.end(), extension->id())) { | |
313 return v8::Undefined(); | |
314 } | |
315 | |
316 DCHECK(args.Length() == 1); | |
317 DCHECK(args[0]->IsArray()); | |
318 v8::Local<v8::Object> jpeg_array = args[0]->ToObject(); | |
319 size_t jpeg_length = | |
320 jpeg_array->Get(v8::String::New("length"))->Int32Value(); | |
321 | |
322 // Put input JPEG array into string for DecodeImage(). | |
323 std::string jpeg_array_string; | |
324 jpeg_array_string.reserve(jpeg_length); | |
325 | |
326 // Unfortunately we cannot request continuous backing store of the | |
327 // |jpeg_array| object as it might not have one. So we make | |
328 // element copy here. | |
329 // Note(mnaganov): If it is not fast enough | |
330 // (and main constraints might be in repetition of v8 API calls), | |
331 // change the argument type from Array to String and use | |
332 // String::Write(). | |
333 // Note(vitalyr): Another option is to use Int8Array for inputs and | |
334 // Int32Array for output. | |
335 for (size_t i = 0; i != jpeg_length; ++i) { | |
336 jpeg_array_string.push_back( | |
337 jpeg_array->Get(v8::Integer::New(i))->Int32Value()); | |
338 } | |
339 | |
340 // Decode and verify resulting image metrics. | |
341 SkBitmap bitmap; | |
342 if (!webkit_glue::DecodeImage(jpeg_array_string, &bitmap)) | |
343 return v8::Undefined(); | |
344 if (bitmap.config() != SkBitmap::kARGB_8888_Config) | |
345 return v8::Undefined(); | |
346 const int width = bitmap.width(); | |
347 const int height = bitmap.height(); | |
348 SkAutoLockPixels lockpixels(bitmap); | |
349 const uint32_t* pixels = static_cast<uint32_t*>(bitmap.getPixels()); | |
350 if (!pixels) | |
351 return v8::Undefined(); | |
352 | |
353 // Compose output array. This API call only accepts kARGB_8888_Config images | |
354 // so we rely on each pixel occupying 4 bytes. | |
355 // Note(mnaganov): to speed this up, you may use backing store | |
356 // technique from CreateExternalArray() of v8/src/d8.cc. | |
357 v8::Local<v8::Array> bitmap_array(v8::Array::New(width * height)); | |
358 for (int i = 0; i != width * height; ++i) { | |
359 bitmap_array->Set(v8::Integer::New(i), | |
360 v8::Integer::New(pixels[i] & 0xFFFFFF)); | |
361 } | |
362 return bitmap_array; | |
363 } | |
364 | |
365 // Creates a Blob with the content of the specified file. | |
366 static v8::Handle<v8::Value> CreateBlob(const v8::Arguments& args) { | |
367 CHECK(args.Length() == 2); | |
368 CHECK(args[0]->IsString()); | |
369 CHECK(args[1]->IsInt32()); | |
370 WebKit::WebString path(UTF8ToUTF16(*v8::String::Utf8Value(args[0]))); | |
371 WebKit::WebBlob blob = | |
372 WebKit::WebBlob::createFromFile(path, args[1]->Int32Value()); | |
373 return blob.toV8Value(); | |
374 } | |
375 | |
376 // Creates a new messaging channel to the tab with the given ID. | |
377 static v8::Handle<v8::Value> OpenChannelToTab(const v8::Arguments& args) { | |
378 // Get the current RenderView so that we can send a routed IPC message from | |
379 // the correct source. | |
380 content::RenderView* renderview = GetCurrentRenderView(); | |
381 if (!renderview) | |
382 return v8::Undefined(); | |
383 | |
384 if (args.Length() >= 3 && args[0]->IsInt32() && args[1]->IsString() && | |
385 args[2]->IsString()) { | |
386 int tab_id = args[0]->Int32Value(); | |
387 std::string extension_id = *v8::String::Utf8Value(args[1]->ToString()); | |
388 std::string channel_name = *v8::String::Utf8Value(args[2]->ToString()); | |
389 int port_id = -1; | |
390 renderview->Send(new ExtensionHostMsg_OpenChannelToTab( | |
391 renderview->GetRoutingId(), tab_id, extension_id, channel_name, | |
392 &port_id)); | |
393 return v8::Integer::New(port_id); | |
394 } | |
395 return v8::Undefined(); | |
396 } | |
397 | |
398 static v8::Handle<v8::Value> GetNextContextMenuId(const v8::Arguments& args) { | |
399 // Note: this works because contextMenus.create() only works in the | |
400 // extension process. If that API is opened up to content scripts, this | |
401 // will need to change. See crbug.com/77023 | |
402 static int next_context_menu_id = 1; | |
403 return v8::Integer::New(next_context_menu_id++); | |
404 } | |
405 | |
406 static v8::Handle<v8::Value> GetNextTtsEventId(const v8::Arguments& args) { | |
407 // Note: this works because the TTS API only works in the | |
408 // extension process, not content scripts. | |
409 static int next_tts_event_id = 1; | |
410 return v8::Integer::New(next_tts_event_id++); | |
411 } | |
412 | |
413 static v8::Handle<v8::Value> GetCurrentPageActions( | |
414 const v8::Arguments& args) { | |
415 ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); | |
416 std::string extension_id = *v8::String::Utf8Value(args[0]->ToString()); | |
417 const ::Extension* extension = | |
418 v8_extension->extension_dispatcher_->extensions()->GetByID( | |
419 extension_id); | |
420 CHECK(!extension_id.empty()); | |
421 if (!extension) | |
422 return v8::Undefined(); | |
423 | |
424 v8::Local<v8::Array> page_action_vector = v8::Array::New(); | |
425 if (extension->page_action()) { | |
426 std::string id = extension->page_action()->id(); | |
427 page_action_vector->Set(v8::Integer::New(0), | |
428 v8::String::New(id.c_str(), id.size())); | |
429 } | |
430 | |
431 return page_action_vector; | |
432 } | |
433 | |
434 // Common code for starting an API request to the browser. |value_args| | |
435 // contains the request's arguments. | |
436 // Steals value_args contents for efficiency. | |
437 static v8::Handle<v8::Value> StartRequestCommon( | |
438 const v8::Arguments& args, ListValue* value_args) { | |
439 ExtensionImpl* v8_extension = GetFromArguments<ExtensionImpl>(args); | |
440 | |
441 const ChromeV8ContextSet& contexts = | |
442 v8_extension->extension_dispatcher()->v8_context_set(); | |
443 ChromeV8Context* current_context = contexts.GetCurrent(); | |
444 if (!current_context) | |
445 return v8::Undefined(); | |
446 | |
447 // Get the current RenderView so that we can send a routed IPC message from | |
448 // the correct source. | |
449 content::RenderView* renderview = current_context->GetRenderView(); | |
450 if (!renderview) | |
451 return v8::Undefined(); | |
452 | |
453 std::string name = *v8::String::AsciiValue(args[0]); | |
454 const std::set<std::string>& function_names = | |
455 v8_extension->extension_dispatcher_->function_names(); | |
456 if (function_names.find(name) == function_names.end()) { | |
457 NOTREACHED() << "Unexpected function " << name; | |
458 return v8::Undefined(); | |
459 } | |
460 | |
461 // TODO(aa): add this to ChromeV8Context. | |
462 if (!v8_extension->CheckPermissionForCurrentRenderView(name)) | |
463 return v8::Undefined(); | |
464 | |
465 GURL source_url; | |
466 WebFrame* webframe = current_context->web_frame(); | |
467 if (webframe) | |
468 source_url = webframe->document().url(); | |
469 | |
470 int request_id = args[2]->Int32Value(); | |
471 bool has_callback = args[3]->BooleanValue(); | |
472 bool for_io_thread = args[4]->BooleanValue(); | |
473 | |
474 v8::Persistent<v8::Context> v8_context = | |
475 v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); | |
476 DCHECK(!v8_context.IsEmpty()); | |
477 g_pending_requests.Get()[request_id].reset(new PendingRequest( | |
478 v8_context, name, current_context->extension_id())); | |
479 | |
480 ExtensionHostMsg_Request_Params params; | |
481 params.name = name; | |
482 params.arguments.Swap(value_args); | |
483 params.source_url = source_url; | |
484 params.request_id = request_id; | |
485 params.has_callback = has_callback; | |
486 params.user_gesture = | |
487 webframe ? webframe->isProcessingUserGesture() : false; | |
488 if (for_io_thread) { | |
489 renderview->Send(new ExtensionHostMsg_RequestForIOThread( | |
490 renderview->GetRoutingId(), params)); | |
491 } else { | |
492 renderview->Send(new ExtensionHostMsg_Request( | |
493 renderview->GetRoutingId(), params)); | |
494 } | |
495 | |
496 return v8::Undefined(); | |
497 } | |
498 | |
499 // Starts an API request to the browser, with an optional callback. The | |
500 // callback will be dispatched to EventBindings::HandleResponse. | |
501 static v8::Handle<v8::Value> StartRequest(const v8::Arguments& args) { | |
502 std::string str_args = *v8::String::Utf8Value(args[1]); | |
503 base::JSONReader reader; | |
504 scoped_ptr<Value> value_args; | |
505 value_args.reset(reader.JsonToValue(str_args, false, false)); | |
506 | |
507 // Since we do the serialization in the v8 extension, we should always get | |
508 // valid JSON. | |
509 if (!value_args.get() || !value_args->IsType(Value::TYPE_LIST)) { | |
510 NOTREACHED() << "Invalid JSON passed to StartRequest."; | |
511 return v8::Undefined(); | |
512 } | |
513 | |
514 return StartRequestCommon(args, static_cast<ListValue*>(value_args.get())); | |
515 } | |
516 | |
517 static bool ConvertImageDataToBitmapValue( | |
518 const v8::Arguments& args, Value** bitmap_value) { | |
519 v8::Local<v8::Object> extension_args = args[1]->ToObject(); | |
520 v8::Local<v8::Object> details = | |
521 extension_args->Get(v8::String::New("0"))->ToObject(); | |
522 v8::Local<v8::Object> image_data = | |
523 details->Get(v8::String::New("imageData"))->ToObject(); | |
524 v8::Local<v8::Object> data = | |
525 image_data->Get(v8::String::New("data"))->ToObject(); | |
526 int width = image_data->Get(v8::String::New("width"))->Int32Value(); | |
527 int height = image_data->Get(v8::String::New("height"))->Int32Value(); | |
528 | |
529 int data_length = data->Get(v8::String::New("length"))->Int32Value(); | |
530 if (data_length != 4 * width * height) { | |
531 NOTREACHED() << "Invalid argument to setIcon. Expecting ImageData."; | |
532 return false; | |
533 } | |
534 | |
535 SkBitmap bitmap; | |
536 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
537 bitmap.allocPixels(); | |
538 bitmap.eraseARGB(0, 0, 0, 0); | |
539 | |
540 uint32_t* pixels = bitmap.getAddr32(0, 0); | |
541 for (int t = 0; t < width*height; t++) { | |
542 // |data| is RGBA, pixels is ARGB. | |
543 pixels[t] = SkPreMultiplyColor( | |
544 ((data->Get(v8::Integer::New(4*t + 3))->Int32Value() & 0xFF) << 24) | | |
545 ((data->Get(v8::Integer::New(4*t + 0))->Int32Value() & 0xFF) << 16) | | |
546 ((data->Get(v8::Integer::New(4*t + 1))->Int32Value() & 0xFF) << 8) | | |
547 ((data->Get(v8::Integer::New(4*t + 2))->Int32Value() & 0xFF) << 0)); | |
548 } | |
549 | |
550 // Construct the Value object. | |
551 IPC::Message bitmap_pickle; | |
552 IPC::WriteParam(&bitmap_pickle, bitmap); | |
553 *bitmap_value = base::BinaryValue::CreateWithCopiedBuffer( | |
554 static_cast<const char*>(bitmap_pickle.data()), bitmap_pickle.size()); | |
555 | |
556 return true; | |
557 } | |
558 | |
559 // A special request for setting the extension action icon and the sidebar | |
560 // mini tab icon. This function accepts a canvas ImageData object, so it needs | |
561 // to do extra processing before sending the request to the browser. | |
562 static v8::Handle<v8::Value> SetIconCommon( | |
563 const v8::Arguments& args) { | |
564 Value* bitmap_value = NULL; | |
565 if (!ConvertImageDataToBitmapValue(args, &bitmap_value)) | |
566 return v8::Undefined(); | |
567 | |
568 v8::Local<v8::Object> extension_args = args[1]->ToObject(); | |
569 v8::Local<v8::Object> details = | |
570 extension_args->Get(v8::String::New("0"))->ToObject(); | |
571 | |
572 DictionaryValue* dict = new DictionaryValue(); | |
573 dict->Set("imageData", bitmap_value); | |
574 | |
575 if (details->Has(v8::String::New("tabId"))) { | |
576 dict->SetInteger("tabId", | |
577 details->Get(v8::String::New("tabId"))->Int32Value()); | |
578 } | |
579 | |
580 ListValue list_value; | |
581 list_value.Append(dict); | |
582 | |
583 return StartRequestCommon(args, &list_value); | |
584 } | |
585 | |
586 static v8::Handle<v8::Value> GetRenderViewId(const v8::Arguments& args) { | |
587 content::RenderView* renderview = GetCurrentRenderView(); | |
588 if (!renderview) | |
589 return v8::Undefined(); | |
590 return v8::Integer::New(renderview->GetRoutingId()); | |
591 } | |
592 }; | |
593 | |
594 } // namespace | |
595 | |
596 v8::Extension* ExtensionProcessBindings::Get( | |
597 ExtensionDispatcher* extension_dispatcher) { | |
598 static v8::Extension* extension = new ExtensionImpl(extension_dispatcher); | |
599 CHECK_EQ(extension_dispatcher, | |
600 static_cast<ExtensionImpl*>(extension)->extension_dispatcher()); | |
601 return extension; | |
602 } | |
603 | |
604 // static | |
605 void ExtensionProcessBindings::HandleResponse( | |
606 const ChromeV8ContextSet& contexts, | |
607 int request_id, | |
608 bool success, | |
609 const std::string& response, | |
610 const std::string& error, | |
611 std::string* extension_id) { | |
612 PendingRequestMap::iterator request = | |
613 g_pending_requests.Get().find(request_id); | |
614 if (request == g_pending_requests.Get().end()) { | |
615 // This should not be able to happen since we only remove requests when they | |
616 // are handled. | |
617 LOG(ERROR) << "Could not find specified request id: " << request_id; | |
618 return; | |
619 } | |
620 | |
621 ChromeV8Context* v8_context = | |
622 contexts.GetByV8Context(request->second->context); | |
623 if (!v8_context) | |
624 return; // The frame went away. | |
625 | |
626 v8::HandleScope handle_scope; | |
627 v8::Handle<v8::Value> argv[5]; | |
628 argv[0] = v8::Integer::New(request_id); | |
629 argv[1] = v8::String::New(request->second->name.c_str()); | |
630 argv[2] = v8::Boolean::New(success); | |
631 argv[3] = v8::String::New(response.c_str()); | |
632 argv[4] = v8::String::New(error.c_str()); | |
633 | |
634 v8::Handle<v8::Value> retval; | |
635 CHECK(v8_context->CallChromeHiddenMethod("handleResponse", | |
636 arraysize(argv), | |
637 argv, | |
638 &retval)); | |
639 // In debug, the js will validate the callback parameters and return a | |
640 // string if a validation error has occured. | |
641 #ifndef NDEBUG | |
642 if (!retval.IsEmpty() && !retval->IsUndefined()) { | |
643 std::string error = *v8::String::AsciiValue(retval); | |
644 DCHECK(false) << error; | |
645 } | |
646 #endif | |
647 | |
648 // Save the extension id before erasing the request. | |
649 *extension_id = request->second->extension_id; | |
650 | |
651 request->second->context.Dispose(); | |
652 request->second->context.Clear(); | |
653 g_pending_requests.Get().erase(request); | |
654 } | |
655 | |
656 // static | |
657 bool ExtensionProcessBindings::HasPendingRequests( | |
658 const std::string& extension_id) { | |
659 for (PendingRequestMap::const_iterator it = g_pending_requests.Get().begin(); | |
660 it != g_pending_requests.Get().end(); ++it) { | |
661 if (it->second->extension_id == extension_id) | |
662 return true; | |
663 } | |
664 return false; | |
665 } | |
OLD | NEW |