OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "webkit/glue/plugins/plugin_host.h" | |
6 | |
7 #include "base/file_util.h" | |
8 #include "base/logging.h" | |
9 #include "base/scoped_ptr.h" | |
10 #include "base/string_piece.h" | |
11 #include "base/string_util.h" | |
12 #if defined(OS_MACOSX) | |
13 #include "base/sys_info.h" | |
14 #endif | |
15 #include "base/sys_string_conversions.h" | |
16 #include "base/utf_string_conversions.h" | |
17 #include "net/base/net_util.h" | |
18 #include "third_party/npapi/bindings/npapi_extensions.h" | |
19 #include "third_party/npapi/bindings/npruntime.h" | |
20 #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" | |
21 #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" | |
22 #include "webkit/glue/webkit_glue.h" | |
23 #include "webkit/glue/plugins/default_plugin_shared.h" | |
24 #include "webkit/glue/plugins/npapi_extension_thunk.h" | |
25 #include "webkit/glue/plugins/plugin_instance.h" | |
26 #include "webkit/glue/plugins/plugin_lib.h" | |
27 #include "webkit/glue/plugins/plugin_list.h" | |
28 #include "webkit/glue/plugins/plugin_stream_url.h" | |
29 #include "webkit/glue/plugins/webplugin_delegate.h" | |
30 #include "webkit/glue/plugins/webplugininfo.h" | |
31 | |
32 using WebKit::WebBindings; | |
33 | |
34 // Finds a PluginInstance from an NPP. | |
35 // The caller must take a reference if needed. | |
36 static NPAPI::PluginInstance* FindInstance(NPP id) { | |
37 if (id == NULL) { | |
38 return NULL; | |
39 } | |
40 return reinterpret_cast<NPAPI::PluginInstance*>(id->ndata); | |
41 } | |
42 | |
43 #if defined(OS_MACOSX) | |
44 // Returns true if the OS supports shared accelerated surfaces via IOSurface. | |
45 // This is true on Snow Leopard and higher. | |
46 static bool SupportsSharingAcceleratedSurfaces() { | |
47 int32 major, minor, bugfix; | |
48 base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); | |
49 return major > 10 || (major == 10 && minor > 5); | |
50 } | |
51 #endif | |
52 | |
53 namespace NPAPI { | |
54 | |
55 scoped_refptr<PluginHost> PluginHost::singleton_; | |
56 | |
57 PluginHost::PluginHost() { | |
58 InitializeHostFuncs(); | |
59 } | |
60 | |
61 PluginHost::~PluginHost() { | |
62 } | |
63 | |
64 PluginHost *PluginHost::Singleton() { | |
65 if (singleton_.get() == NULL) { | |
66 singleton_ = new PluginHost(); | |
67 } | |
68 | |
69 DCHECK(singleton_.get() != NULL); | |
70 return singleton_; | |
71 } | |
72 | |
73 void PluginHost::InitializeHostFuncs() { | |
74 memset(&host_funcs_, 0, sizeof(host_funcs_)); | |
75 host_funcs_.size = sizeof(host_funcs_); | |
76 host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR); | |
77 | |
78 // The "basic" functions | |
79 host_funcs_.geturl = &NPN_GetURL; | |
80 host_funcs_.posturl = &NPN_PostURL; | |
81 host_funcs_.requestread = &NPN_RequestRead; | |
82 host_funcs_.newstream = &NPN_NewStream; | |
83 host_funcs_.write = &NPN_Write; | |
84 host_funcs_.destroystream = &NPN_DestroyStream; | |
85 host_funcs_.status = &NPN_Status; | |
86 host_funcs_.uagent = &NPN_UserAgent; | |
87 host_funcs_.memalloc = &NPN_MemAlloc; | |
88 host_funcs_.memfree = &NPN_MemFree; | |
89 host_funcs_.memflush = &NPN_MemFlush; | |
90 host_funcs_.reloadplugins = &NPN_ReloadPlugins; | |
91 | |
92 // We don't implement java yet | |
93 host_funcs_.getJavaEnv = &NPN_GetJavaEnv; | |
94 host_funcs_.getJavaPeer = &NPN_GetJavaPeer; | |
95 | |
96 // Advanced functions we implement | |
97 host_funcs_.geturlnotify = &NPN_GetURLNotify; | |
98 host_funcs_.posturlnotify = &NPN_PostURLNotify; | |
99 host_funcs_.getvalue = &NPN_GetValue; | |
100 host_funcs_.setvalue = &NPN_SetValue; | |
101 host_funcs_.invalidaterect = &NPN_InvalidateRect; | |
102 host_funcs_.invalidateregion = &NPN_InvalidateRegion; | |
103 host_funcs_.forceredraw = &NPN_ForceRedraw; | |
104 | |
105 // These come from the Javascript Engine | |
106 host_funcs_.getstringidentifier = WebBindings::getStringIdentifier; | |
107 host_funcs_.getstringidentifiers = WebBindings::getStringIdentifiers; | |
108 host_funcs_.getintidentifier = WebBindings::getIntIdentifier; | |
109 host_funcs_.identifierisstring = WebBindings::identifierIsString; | |
110 host_funcs_.utf8fromidentifier = WebBindings::utf8FromIdentifier; | |
111 host_funcs_.intfromidentifier = WebBindings::intFromIdentifier; | |
112 host_funcs_.createobject = WebBindings::createObject; | |
113 host_funcs_.retainobject = WebBindings::retainObject; | |
114 host_funcs_.releaseobject = WebBindings::releaseObject; | |
115 host_funcs_.invoke = WebBindings::invoke; | |
116 host_funcs_.invokeDefault = WebBindings::invokeDefault; | |
117 host_funcs_.evaluate = WebBindings::evaluate; | |
118 host_funcs_.getproperty = WebBindings::getProperty; | |
119 host_funcs_.setproperty = WebBindings::setProperty; | |
120 host_funcs_.removeproperty = WebBindings::removeProperty; | |
121 host_funcs_.hasproperty = WebBindings::hasProperty; | |
122 host_funcs_.hasmethod = WebBindings::hasMethod; | |
123 host_funcs_.releasevariantvalue = WebBindings::releaseVariantValue; | |
124 host_funcs_.setexception = WebBindings::setException; | |
125 host_funcs_.pushpopupsenabledstate = NPN_PushPopupsEnabledState; | |
126 host_funcs_.poppopupsenabledstate = NPN_PopPopupsEnabledState; | |
127 host_funcs_.enumerate = WebBindings::enumerate; | |
128 host_funcs_.pluginthreadasynccall = NPN_PluginThreadAsyncCall; | |
129 host_funcs_.construct = WebBindings::construct; | |
130 host_funcs_.getvalueforurl = NPN_GetValueForURL; | |
131 host_funcs_.setvalueforurl = NPN_SetValueForURL; | |
132 host_funcs_.getauthenticationinfo = NPN_GetAuthenticationInfo; | |
133 host_funcs_.scheduletimer = NPN_ScheduleTimer; | |
134 host_funcs_.unscheduletimer = NPN_UnscheduleTimer; | |
135 host_funcs_.popupcontextmenu = NPN_PopUpContextMenu; | |
136 host_funcs_.convertpoint = NPN_ConvertPoint; | |
137 host_funcs_.handleevent = NPN_HandleEvent; | |
138 host_funcs_.unfocusinstance = NPN_UnfocusInstance; | |
139 host_funcs_.urlredirectresponse = NPN_URLRedirectResponse; | |
140 } | |
141 | |
142 void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) { | |
143 // When running in the plugin process, we need to patch the NPN functions | |
144 // that the plugin calls to interact with NPObjects that we give. Otherwise | |
145 // the plugin will call the v8 NPN functions, which won't work since we have | |
146 // an NPObjectProxy and not a real v8 implementation. | |
147 if (overrides->invoke) | |
148 host_funcs_.invoke = overrides->invoke; | |
149 | |
150 if (overrides->invokeDefault) | |
151 host_funcs_.invokeDefault = overrides->invokeDefault; | |
152 | |
153 if (overrides->evaluate) | |
154 host_funcs_.evaluate = overrides->evaluate; | |
155 | |
156 if (overrides->getproperty) | |
157 host_funcs_.getproperty = overrides->getproperty; | |
158 | |
159 if (overrides->setproperty) | |
160 host_funcs_.setproperty = overrides->setproperty; | |
161 | |
162 if (overrides->removeproperty) | |
163 host_funcs_.removeproperty = overrides->removeproperty; | |
164 | |
165 if (overrides->hasproperty) | |
166 host_funcs_.hasproperty = overrides->hasproperty; | |
167 | |
168 if (overrides->hasmethod) | |
169 host_funcs_.hasmethod = overrides->hasmethod; | |
170 | |
171 if (overrides->setexception) | |
172 host_funcs_.setexception = overrides->setexception; | |
173 | |
174 if (overrides->enumerate) | |
175 host_funcs_.enumerate = overrides->enumerate; | |
176 } | |
177 | |
178 bool PluginHost::SetPostData(const char* buf, | |
179 uint32 length, | |
180 std::vector<std::string>* names, | |
181 std::vector<std::string>* values, | |
182 std::vector<char>* body) { | |
183 // Use a state table to do the parsing. Whitespace must be | |
184 // trimmed after the fact if desired. In our case, we actually | |
185 // don't care about the whitespace, because we're just going to | |
186 // pass this back into another POST. This function strips out the | |
187 // "Content-length" header and does not append it to the request. | |
188 | |
189 // | |
190 // This parser takes action only on state changes. | |
191 // | |
192 // Transition table: | |
193 // : \n NULL Other | |
194 // 0 GetHeader 1 2 4 0 | |
195 // 1 GetValue 1 0 3 1 | |
196 // 2 GetData 2 2 3 2 | |
197 // 3 DONE | |
198 // 4 ERR | |
199 // | |
200 enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER }; | |
201 enum { GETNAME, GETVALUE, GETDATA, DONE, ERR }; | |
202 int statemachine[3][4] = { { GETVALUE, GETDATA, GETDATA, GETNAME }, | |
203 { GETVALUE, GETNAME, DONE, GETVALUE }, | |
204 { GETDATA, GETDATA, DONE, GETDATA } }; | |
205 std::string name, value; | |
206 const char* ptr = static_cast<const char*>(buf); | |
207 const char* start = ptr; | |
208 int state = GETNAME; // initial state | |
209 bool done = false; | |
210 bool err = false; | |
211 do { | |
212 int input; | |
213 | |
214 // Translate the current character into an input | |
215 // for the state table. | |
216 switch (*ptr) { | |
217 case ':' : | |
218 input = INPUT_COLON; | |
219 break; | |
220 case '\n': | |
221 input = INPUT_NEWLINE; | |
222 break; | |
223 case 0 : | |
224 input = INPUT_NULL; | |
225 break; | |
226 default : | |
227 input = INPUT_OTHER; | |
228 break; | |
229 } | |
230 | |
231 int newstate = statemachine[state][input]; | |
232 | |
233 // Take action based on the new state. | |
234 if (state != newstate) { | |
235 switch (newstate) { | |
236 case GETNAME: | |
237 // Got a value. | |
238 value = std::string(start, ptr - start); | |
239 TrimWhitespace(value, TRIM_ALL, &value); | |
240 // If the name field is empty, we'll skip this header | |
241 // but we won't error out. | |
242 if (!name.empty() && name != "content-length") { | |
243 names->push_back(name); | |
244 values->push_back(value); | |
245 } | |
246 start = ptr + 1; | |
247 break; | |
248 case GETVALUE: | |
249 // Got a header. | |
250 name = StringToLowerASCII(std::string(start, ptr - start)); | |
251 TrimWhitespace(name, TRIM_ALL, &name); | |
252 start = ptr + 1; | |
253 break; | |
254 case GETDATA: { | |
255 // Finished headers, now get body | |
256 if (*ptr) | |
257 start = ptr + 1; | |
258 size_t previous_size = body->size(); | |
259 size_t new_body_size = length - static_cast<int>(start - buf); | |
260 body->resize(previous_size + new_body_size); | |
261 if (!body->empty()) | |
262 memcpy(&body->front() + previous_size, start, new_body_size); | |
263 done = true; | |
264 break; | |
265 } | |
266 case ERR: | |
267 // error | |
268 err = true; | |
269 done = true; | |
270 break; | |
271 } | |
272 } | |
273 state = newstate; | |
274 ptr++; | |
275 } while (!done); | |
276 | |
277 return !err; | |
278 } | |
279 | |
280 } // namespace NPAPI | |
281 | |
282 extern "C" { | |
283 | |
284 // Allocates memory from the host's memory space. | |
285 void* NPN_MemAlloc(uint32_t size) { | |
286 scoped_refptr<NPAPI::PluginHost> host(NPAPI::PluginHost::Singleton()); | |
287 if (host != NULL) { | |
288 // Note: We must use the same allocator/deallocator | |
289 // that is used by the javascript library, as some of the | |
290 // JS APIs will pass memory to the plugin which the plugin | |
291 // will attempt to free. | |
292 return malloc(size); | |
293 } | |
294 return NULL; | |
295 } | |
296 | |
297 // Deallocates memory from the host's memory space | |
298 void NPN_MemFree(void* ptr) { | |
299 scoped_refptr<NPAPI::PluginHost> host(NPAPI::PluginHost::Singleton()); | |
300 if (host != NULL) { | |
301 if (ptr != NULL && ptr != reinterpret_cast<void*>(-1)) | |
302 free(ptr); | |
303 } | |
304 } | |
305 | |
306 // Requests that the host free a specified amount of memory. | |
307 uint32_t NPN_MemFlush(uint32_t size) { | |
308 // This is not relevant on Windows; MAC specific | |
309 return size; | |
310 } | |
311 | |
312 // This is for dynamic discovery of new plugins. | |
313 // Should force a re-scan of the plugins directory to load new ones. | |
314 void NPN_ReloadPlugins(NPBool reload_pages) { | |
315 WebKit::resetPluginCache(reload_pages ? true : false); | |
316 } | |
317 | |
318 // Requests a range of bytes for a seekable stream. | |
319 NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) { | |
320 if (!stream || !range_list) | |
321 return NPERR_GENERIC_ERROR; | |
322 | |
323 scoped_refptr<NPAPI::PluginInstance> plugin( | |
324 reinterpret_cast<NPAPI::PluginInstance*>(stream->ndata)); | |
325 if (!plugin.get()) | |
326 return NPERR_GENERIC_ERROR; | |
327 | |
328 plugin->RequestRead(stream, range_list); | |
329 return NPERR_NO_ERROR; | |
330 } | |
331 | |
332 // Generic form of GetURL for common code between GetURL and GetURLNotify. | |
333 static NPError GetURLNotify(NPP id, | |
334 const char* url, | |
335 const char* target, | |
336 bool notify, | |
337 void* notify_data) { | |
338 if (!url) | |
339 return NPERR_INVALID_URL; | |
340 | |
341 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
342 if (!plugin.get()) { | |
343 return NPERR_GENERIC_ERROR; | |
344 } | |
345 | |
346 plugin->RequestURL(url, "GET", target, NULL, 0, notify, notify_data); | |
347 return NPERR_NO_ERROR; | |
348 } | |
349 | |
350 // Requests creation of a new stream with the contents of the | |
351 // specified URL; gets notification of the result. | |
352 NPError NPN_GetURLNotify(NPP id, | |
353 const char* url, | |
354 const char* target, | |
355 void* notify_data) { | |
356 // This is identical to NPN_GetURL, but after finishing, the | |
357 // browser will call NPP_URLNotify to inform the plugin that | |
358 // it has completed. | |
359 | |
360 // According to the NPAPI documentation, if target == _self | |
361 // or a parent to _self, the browser should return NPERR_INVALID_PARAM, | |
362 // because it can't notify the plugin once deleted. This is | |
363 // absolutely false; firefox doesn't do this, and Flash relies on | |
364 // being able to use this. | |
365 | |
366 // Also according to the NPAPI documentation, we should return | |
367 // NPERR_INVALID_URL if the url requested is not valid. However, | |
368 // this would require that we synchronously start fetching the | |
369 // URL. That just isn't practical. As such, there really is | |
370 // no way to return this error. From looking at the Firefox | |
371 // implementation, it doesn't look like Firefox does this either. | |
372 | |
373 return GetURLNotify(id, url, target, true, notify_data); | |
374 } | |
375 | |
376 NPError NPN_GetURL(NPP id, const char* url, const char* target) { | |
377 // Notes: | |
378 // Request from the Plugin to fetch content either for the plugin | |
379 // or to be placed into a browser window. | |
380 // | |
381 // If target == null, the browser fetches content and streams to plugin. | |
382 // otherwise, the browser loads content into an existing browser frame. | |
383 // If the target is the window/frame containing the plugin, the plugin | |
384 // may be destroyed. | |
385 // If the target is _blank, a mailto: or news: url open content in a new | |
386 // browser window | |
387 // If the target is _self, no other instance of the plugin is created. The | |
388 // plugin continues to operate in its own window | |
389 | |
390 return GetURLNotify(id, url, target, false, 0); | |
391 } | |
392 | |
393 // Generic form of PostURL for common code between PostURL and PostURLNotify. | |
394 static NPError PostURLNotify(NPP id, | |
395 const char* url, | |
396 const char* target, | |
397 uint32_t len, | |
398 const char* buf, | |
399 NPBool file, | |
400 bool notify, | |
401 void* notify_data) { | |
402 if (!url) | |
403 return NPERR_INVALID_URL; | |
404 | |
405 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
406 if (!plugin.get()) { | |
407 NOTREACHED(); | |
408 return NPERR_GENERIC_ERROR; | |
409 } | |
410 | |
411 std::string post_file_contents; | |
412 | |
413 if (file) { | |
414 // Post data to be uploaded from a file. This can be handled in two | |
415 // ways. | |
416 // 1. Read entire file and send the contents as if it was a post data | |
417 // specified in the argument | |
418 // 2. Send just the file details and read them in the browser at the | |
419 // time of sending the request. | |
420 // Approach 2 is more efficient but complicated. Approach 1 has a major | |
421 // drawback of sending potentially large data over two IPC hops. In a way | |
422 // 'large data over IPC' problem exists as it is in case of plugin giving | |
423 // the data directly instead of in a file. | |
424 // Currently we are going with the approach 1 to get the feature working. | |
425 // We can optimize this later with approach 2. | |
426 | |
427 // TODO(joshia): Design a scheme to send a file descriptor instead of | |
428 // entire file contents across. | |
429 | |
430 // Security alert: | |
431 // --------------- | |
432 // Here we are blindly uploading whatever file requested by a plugin. | |
433 // This is risky as someone could exploit a plugin to send private | |
434 // data in arbitrary locations. | |
435 // A malicious (non-sandboxed) plugin has unfeterred access to OS | |
436 // resources and can do this anyway without using browser's HTTP stack. | |
437 // FWIW, Firefox and Safari don't perform any security checks. | |
438 | |
439 if (!buf) | |
440 return NPERR_FILE_NOT_FOUND; | |
441 | |
442 std::string file_path_ascii(buf); | |
443 FilePath file_path; | |
444 static const char kFileUrlPrefix[] = "file:"; | |
445 if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) { | |
446 GURL file_url(file_path_ascii); | |
447 DCHECK(file_url.SchemeIsFile()); | |
448 net::FileURLToFilePath(file_url, &file_path); | |
449 } else { | |
450 file_path = FilePath::FromWStringHack( | |
451 base::SysNativeMBToWide(file_path_ascii)); | |
452 } | |
453 | |
454 base::PlatformFileInfo post_file_info = {0}; | |
455 if (!file_util::GetFileInfo(file_path, &post_file_info) || | |
456 post_file_info.is_directory) | |
457 return NPERR_FILE_NOT_FOUND; | |
458 | |
459 if (!file_util::ReadFileToString(file_path, &post_file_contents)) | |
460 return NPERR_FILE_NOT_FOUND; | |
461 | |
462 buf = post_file_contents.c_str(); | |
463 len = post_file_contents.size(); | |
464 } | |
465 | |
466 // The post data sent by a plugin contains both headers | |
467 // and post data. Example: | |
468 // Content-type: text/html | |
469 // Content-length: 200 | |
470 // | |
471 // <200 bytes of content here> | |
472 // | |
473 // Unfortunately, our stream needs these broken apart, | |
474 // so we need to parse the data and set headers and data | |
475 // separately. | |
476 plugin->RequestURL(url, "POST", target, buf, len, notify, notify_data); | |
477 return NPERR_NO_ERROR; | |
478 } | |
479 | |
480 NPError NPN_PostURLNotify(NPP id, | |
481 const char* url, | |
482 const char* target, | |
483 uint32_t len, | |
484 const char* buf, | |
485 NPBool file, | |
486 void* notify_data) { | |
487 return PostURLNotify(id, url, target, len, buf, file, true, notify_data); | |
488 } | |
489 | |
490 NPError NPN_PostURL(NPP id, | |
491 const char* url, | |
492 const char* target, | |
493 uint32_t len, | |
494 const char* buf, | |
495 NPBool file) { | |
496 // POSTs data to an URL, either from a temp file or a buffer. | |
497 // If file is true, buf contains a temp file (which host will delete after | |
498 // completing), and len contains the length of the filename. | |
499 // If file is false, buf contains the data to send, and len contains the | |
500 // length of the buffer | |
501 // | |
502 // If target is null, | |
503 // server response is returned to the plugin | |
504 // If target is _current, _self, or _top, | |
505 // server response is written to the plugin window and plugin is unloaded. | |
506 // If target is _new or _blank, | |
507 // server response is written to a new browser window | |
508 // If target is an existing frame, | |
509 // server response goes to that frame. | |
510 // | |
511 // For protocols other than FTP | |
512 // file uploads must be line-end converted from \r\n to \n | |
513 // | |
514 // Note: you cannot specify headers (even a blank line) in a memory buffer, | |
515 // use NPN_PostURLNotify | |
516 | |
517 return PostURLNotify(id, url, target, len, buf, file, false, 0); | |
518 } | |
519 | |
520 NPError NPN_NewStream(NPP id, | |
521 NPMIMEType type, | |
522 const char* target, | |
523 NPStream** stream) { | |
524 // Requests creation of a new data stream produced by the plugin, | |
525 // consumed by the browser. | |
526 // | |
527 // Browser should put this stream into a window target. | |
528 // | |
529 // TODO: implement me | |
530 DVLOG(1) << "NPN_NewStream is not implemented yet."; | |
531 return NPERR_GENERIC_ERROR; | |
532 } | |
533 | |
534 int32_t NPN_Write(NPP id, NPStream* stream, int32_t len, void* buffer) { | |
535 // Writes data to an existing Plugin-created stream. | |
536 | |
537 // TODO: implement me | |
538 DVLOG(1) << "NPN_Write is not implemented yet."; | |
539 return NPERR_GENERIC_ERROR; | |
540 } | |
541 | |
542 NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) { | |
543 // Destroys a stream (could be created by plugin or browser). | |
544 // | |
545 // Reasons: | |
546 // NPRES_DONE - normal completion | |
547 // NPRES_USER_BREAK - user terminated | |
548 // NPRES_NETWORK_ERROR - network error (all errors fit here?) | |
549 // | |
550 // | |
551 | |
552 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
553 if (plugin.get() == NULL) { | |
554 NOTREACHED(); | |
555 return NPERR_GENERIC_ERROR; | |
556 } | |
557 | |
558 return plugin->NPP_DestroyStream(stream, reason); | |
559 } | |
560 | |
561 const char* NPN_UserAgent(NPP id) { | |
562 #if defined(OS_WIN) | |
563 // Flash passes in a null id during the NP_initialize call. We need to | |
564 // default to the Mozilla user agent if we don't have an NPP instance or | |
565 // else Flash won't request windowless mode. | |
566 bool use_mozilla_user_agent = true; | |
567 if (id) { | |
568 scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); | |
569 if (plugin.get() && !plugin->use_mozilla_user_agent()) | |
570 use_mozilla_user_agent = false; | |
571 } | |
572 | |
573 if (use_mozilla_user_agent) | |
574 return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) " | |
575 "Gecko/20061103 Firefox/2.0a1"; | |
576 #elif defined(OS_MACOSX) | |
577 // Silverlight 4 doesn't handle events correctly unless we claim to be Safari. | |
578 scoped_refptr<NPAPI::PluginInstance> plugin; | |
579 if (id) | |
580 plugin = FindInstance(id); | |
581 if (plugin.get()) { | |
582 WebPluginInfo plugin_info = plugin->plugin_lib()->plugin_info(); | |
583 if (plugin_info.name == ASCIIToUTF16("Silverlight Plug-In") && | |
584 StartsWith(plugin_info.version, ASCIIToUTF16("4."), false)) { | |
585 return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) " | |
586 "AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 Safari/533.16"; | |
587 } | |
588 } | |
589 #endif | |
590 | |
591 return webkit_glue::GetUserAgent(GURL()).c_str(); | |
592 } | |
593 | |
594 void NPN_Status(NPP id, const char* message) { | |
595 // Displays a message on the status line of the browser window. | |
596 | |
597 // TODO: implement me | |
598 DVLOG(1) << "NPN_Status is not implemented yet."; | |
599 } | |
600 | |
601 void NPN_InvalidateRect(NPP id, NPRect *invalidRect) { | |
602 // Invalidates specified drawing area prior to repainting or refreshing a | |
603 // windowless plugin | |
604 | |
605 // Before a windowless plugin can refresh part of its drawing area, it must | |
606 // first invalidate it. This function causes the NPP_HandleEvent method to | |
607 // pass an update event or a paint message to the plug-in. After calling | |
608 // this method, the plug-in recieves a paint message asynchronously. | |
609 | |
610 // The browser redraws invalid areas of the document and any windowless | |
611 // plug-ins at regularly timed intervals. To force a paint message, the | |
612 // plug-in can call NPN_ForceRedraw after calling this method. | |
613 | |
614 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
615 if (plugin.get() && plugin->webplugin()) { | |
616 if (invalidRect) { | |
617 #if defined(OS_WIN) | |
618 if (!plugin->windowless()) { | |
619 RECT rect = {0}; | |
620 rect.left = invalidRect->left; | |
621 rect.right = invalidRect->right; | |
622 rect.top = invalidRect->top; | |
623 rect.bottom = invalidRect->bottom; | |
624 ::InvalidateRect(plugin->window_handle(), &rect, false); | |
625 return; | |
626 } | |
627 #endif | |
628 gfx::Rect rect(invalidRect->left, | |
629 invalidRect->top, | |
630 invalidRect->right - invalidRect->left, | |
631 invalidRect->bottom - invalidRect->top); | |
632 plugin->webplugin()->InvalidateRect(rect); | |
633 } else { | |
634 plugin->webplugin()->Invalidate(); | |
635 } | |
636 } | |
637 } | |
638 | |
639 void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) { | |
640 // Invalidates a specified drawing region prior to repainting | |
641 // or refreshing a window-less plugin. | |
642 // | |
643 // Similar to NPN_InvalidateRect. | |
644 | |
645 // TODO: this is overkill--add platform-specific region handling (at the | |
646 // very least, fetch the region's bounding box and pass it to InvalidateRect). | |
647 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
648 DCHECK(plugin.get() != NULL); | |
649 if (plugin.get() && plugin->webplugin()) | |
650 plugin->webplugin()->Invalidate(); | |
651 } | |
652 | |
653 void NPN_ForceRedraw(NPP id) { | |
654 // Forces repaint for a windowless plug-in. | |
655 // | |
656 // We deliberately do not implement this; we don't want plugins forcing | |
657 // synchronous paints. | |
658 } | |
659 | |
660 NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) { | |
661 // Allows the plugin to query the browser for information | |
662 // | |
663 // Variables: | |
664 // NPNVxDisplay (unix only) | |
665 // NPNVxtAppContext (unix only) | |
666 // NPNVnetscapeWindow (win only) - Gets the native window on which the | |
667 // plug-in drawing occurs, returns HWND | |
668 // NPNVjavascriptEnabledBool: tells whether Javascript is enabled | |
669 // NPNVasdEnabledBool: tells whether SmartUpdate is enabled | |
670 // NPNVOfflineBool: tells whether offline-mode is enabled | |
671 | |
672 NPError rv = NPERR_GENERIC_ERROR; | |
673 | |
674 switch (static_cast<int>(variable)) { | |
675 case NPNVWindowNPObject: { | |
676 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
677 NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject(); | |
678 // Return value is expected to be retained, as | |
679 // described here: | |
680 // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> | |
681 if (np_object) { | |
682 WebBindings::retainObject(np_object); | |
683 void **v = (void **)value; | |
684 *v = np_object; | |
685 rv = NPERR_NO_ERROR; | |
686 } else { | |
687 NOTREACHED(); | |
688 } | |
689 break; | |
690 } | |
691 case NPNVPluginElementNPObject: { | |
692 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
693 NPObject *np_object = plugin->webplugin()->GetPluginElement(); | |
694 // Return value is expected to be retained, as | |
695 // described here: | |
696 // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> | |
697 if (np_object) { | |
698 WebBindings::retainObject(np_object); | |
699 void** v = static_cast<void**>(value); | |
700 *v = np_object; | |
701 rv = NPERR_NO_ERROR; | |
702 } else { | |
703 NOTREACHED(); | |
704 } | |
705 break; | |
706 } | |
707 #if !defined(OS_MACOSX) // OS X doesn't have windowed plugins. | |
708 case NPNVnetscapeWindow: { | |
709 scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); | |
710 if (!plugin.get()) { | |
711 NOTREACHED(); | |
712 return NPERR_GENERIC_ERROR; | |
713 } | |
714 gfx::PluginWindowHandle handle = plugin->window_handle(); | |
715 *((void**)value) = (void*)handle; | |
716 rv = NPERR_NO_ERROR; | |
717 break; | |
718 } | |
719 #endif | |
720 case NPNVjavascriptEnabledBool: { | |
721 // yes, JS is enabled. | |
722 *((void**)value) = (void*)1; | |
723 rv = NPERR_NO_ERROR; | |
724 break; | |
725 } | |
726 #if defined(TOOLKIT_USES_GTK) | |
727 case NPNVToolkit: | |
728 // Tell them we are GTK2. (The alternative is GTK 1.2.) | |
729 *reinterpret_cast<int*>(value) = NPNVGtk2; | |
730 rv = NPERR_NO_ERROR; | |
731 break; | |
732 | |
733 case NPNVSupportsXEmbedBool: | |
734 *reinterpret_cast<NPBool*>(value) = true; | |
735 rv = NPERR_NO_ERROR; | |
736 break; | |
737 #endif | |
738 case NPNVSupportsWindowless: { | |
739 NPBool* supports_windowless = reinterpret_cast<NPBool*>(value); | |
740 *supports_windowless = true; | |
741 rv = NPERR_NO_ERROR; | |
742 break; | |
743 } | |
744 case NPNVprivateModeBool: { | |
745 NPBool* private_mode = reinterpret_cast<NPBool*>(value); | |
746 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
747 *private_mode = plugin->webplugin()->IsOffTheRecord(); | |
748 rv = NPERR_NO_ERROR; | |
749 break; | |
750 } | |
751 case default_plugin::kMissingPluginStatusStart + | |
752 default_plugin::MISSING_PLUGIN_AVAILABLE: | |
753 // fall through | |
754 case default_plugin::kMissingPluginStatusStart + | |
755 default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: { | |
756 // This is a hack for the default plugin to send notification to | |
757 // renderer. Even though we check if the plugin is the default plugin, | |
758 // we still need to worry about future standard change that may conflict | |
759 // with the variable definition, in order to avoid duplicate case clauses | |
760 // in this big switch statement. | |
761 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
762 if (plugin->plugin_lib()->plugin_info().path.value() == | |
763 kDefaultPluginLibraryName) { | |
764 plugin->webplugin()->OnMissingPluginStatus( | |
765 variable - default_plugin::kMissingPluginStatusStart); | |
766 } | |
767 break; | |
768 } | |
769 #if defined(OS_MACOSX) | |
770 case NPNVpluginDrawingModel: { | |
771 // return the drawing model that was negotiated when we initialized. | |
772 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
773 *reinterpret_cast<int*>(value) = plugin->drawing_model(); | |
774 rv = NPERR_NO_ERROR; | |
775 break; | |
776 } | |
777 #ifndef NP_NO_QUICKDRAW | |
778 case NPNVsupportsQuickDrawBool: { | |
779 // We do not admit to supporting the QuickDraw drawing model. The logic | |
780 // here is that our QuickDraw plugin support is so rudimentary that we | |
781 // only want to use it as a fallback to keep plugins from crashing: if a | |
782 // plugin knows enough to ask, we want them to use CoreGraphics. | |
783 NPBool* supports_qd = reinterpret_cast<NPBool*>(value); | |
784 *supports_qd = false; | |
785 rv = NPERR_NO_ERROR; | |
786 break; | |
787 } | |
788 #endif | |
789 case NPNVsupportsCoreGraphicsBool: | |
790 #ifndef NP_NO_CARBON | |
791 case NPNVsupportsCarbonBool: | |
792 #endif | |
793 case NPNVsupportsCocoaBool: { | |
794 // we do support these drawing and event models. | |
795 NPBool* supports_model = reinterpret_cast<NPBool*>(value); | |
796 *supports_model = true; | |
797 rv = NPERR_NO_ERROR; | |
798 break; | |
799 } | |
800 case NPNVsupportsCoreAnimationBool: { | |
801 // We only support the Core Animation model on 10.6 and higher | |
802 // TODO(stuartmorgan): Once existing CA plugins have implemented the | |
803 // invalidating version, remove support for this one. | |
804 NPBool* supports_model = reinterpret_cast<NPBool*>(value); | |
805 *supports_model = SupportsSharingAcceleratedSurfaces() ? true : false; | |
806 rv = NPERR_NO_ERROR; | |
807 break; | |
808 } | |
809 case NPNVsupportsInvalidatingCoreAnimationBool: { | |
810 NPBool* supports_model = reinterpret_cast<NPBool*>(value); | |
811 *supports_model = true; | |
812 rv = NPERR_NO_ERROR; | |
813 break; | |
814 } | |
815 case NPNVsupportsOpenGLBool: { | |
816 // This drawing model was never widely supported, and we don't plan to | |
817 // support it. | |
818 NPBool* supports_model = reinterpret_cast<NPBool*>(value); | |
819 *supports_model = false; | |
820 rv = NPERR_NO_ERROR; | |
821 break; | |
822 } | |
823 #endif // OS_MACOSX | |
824 case NPNVPepperExtensions: | |
825 // Available for any plugin that attempts to get it. | |
826 // If the plugin is not started in a Pepper implementation, it | |
827 // will likely fail when it tries to use any of the functions | |
828 // attached to the extension vector. | |
829 rv = NPAPI::GetPepperExtensionsFunctions(value); | |
830 break; | |
831 default: | |
832 DVLOG(1) << "NPN_GetValue(" << variable << ") is not implemented yet."; | |
833 break; | |
834 } | |
835 return rv; | |
836 } | |
837 | |
838 NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) { | |
839 // Allows the plugin to set various modes | |
840 | |
841 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
842 switch(variable) { | |
843 case NPPVpluginWindowBool: { | |
844 // Sets windowless mode for display of the plugin | |
845 // Note: the documentation at | |
846 // http://developer.mozilla.org/en/docs/NPN_SetValue is wrong. When | |
847 // value is NULL, the mode is set to true. This is the same way Mozilla | |
848 // works. | |
849 plugin->set_windowless(value == 0); | |
850 return NPERR_NO_ERROR; | |
851 } | |
852 case NPPVpluginTransparentBool: { | |
853 // Sets transparent mode for display of the plugin | |
854 // | |
855 // Transparent plugins require the browser to paint the background | |
856 // before having the plugin paint. By default, windowless plugins | |
857 // are transparent. Making a windowless plugin opaque means that | |
858 // the plugin does not require the browser to paint the background. | |
859 bool mode = (value != 0); | |
860 plugin->set_transparent(mode); | |
861 return NPERR_NO_ERROR; | |
862 } | |
863 case NPPVjavascriptPushCallerBool: | |
864 // Specifies whether you are pushing or popping the JSContext off. | |
865 // the stack | |
866 // TODO: implement me | |
867 DVLOG(1) << "NPN_SetValue(NPPVJavascriptPushCallerBool) is not " | |
868 "implemented."; | |
869 return NPERR_GENERIC_ERROR; | |
870 case NPPVpluginKeepLibraryInMemory: | |
871 // Tells browser that plugin library should live longer than usual. | |
872 // TODO: implement me | |
873 DVLOG(1) << "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not " | |
874 "implemented."; | |
875 return NPERR_GENERIC_ERROR; | |
876 #if defined(OS_MACOSX) | |
877 case NPPVpluginDrawingModel: { | |
878 int model = reinterpret_cast<int>(value); | |
879 if (model == NPDrawingModelCoreGraphics || | |
880 model == NPDrawingModelInvalidatingCoreAnimation || | |
881 (model == NPDrawingModelCoreAnimation && | |
882 SupportsSharingAcceleratedSurfaces())) { | |
883 plugin->set_drawing_model(static_cast<NPDrawingModel>(model)); | |
884 return NPERR_NO_ERROR; | |
885 } | |
886 return NPERR_GENERIC_ERROR; | |
887 } | |
888 case NPPVpluginEventModel: { | |
889 // we support Carbon and Cocoa event models | |
890 int model = reinterpret_cast<int>(value); | |
891 switch (model) { | |
892 #ifndef NP_NO_CARBON | |
893 case NPEventModelCarbon: | |
894 #endif | |
895 case NPEventModelCocoa: | |
896 plugin->set_event_model(static_cast<NPEventModel>(model)); | |
897 return NPERR_NO_ERROR; | |
898 break; | |
899 } | |
900 return NPERR_GENERIC_ERROR; | |
901 } | |
902 #endif | |
903 default: | |
904 // TODO: implement me | |
905 DVLOG(1) << "NPN_SetValue(" << variable << ") is not implemented."; | |
906 break; | |
907 } | |
908 | |
909 NOTREACHED(); | |
910 return NPERR_GENERIC_ERROR; | |
911 } | |
912 | |
913 void* NPN_GetJavaEnv() { | |
914 // TODO: implement me | |
915 DVLOG(1) << "NPN_GetJavaEnv is not implemented."; | |
916 return NULL; | |
917 } | |
918 | |
919 void* NPN_GetJavaPeer(NPP) { | |
920 // TODO: implement me | |
921 DVLOG(1) << "NPN_GetJavaPeer is not implemented."; | |
922 return NULL; | |
923 } | |
924 | |
925 void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) { | |
926 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
927 if (plugin) | |
928 plugin->PushPopupsEnabledState(enabled ? true : false); | |
929 } | |
930 | |
931 void NPN_PopPopupsEnabledState(NPP id) { | |
932 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
933 if (plugin) | |
934 plugin->PopPopupsEnabledState(); | |
935 } | |
936 | |
937 void NPN_PluginThreadAsyncCall(NPP id, | |
938 void (*func)(void*), | |
939 void* user_data) { | |
940 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
941 if (plugin) | |
942 plugin->PluginThreadAsyncCall(func, user_data); | |
943 } | |
944 | |
945 NPError NPN_GetValueForURL(NPP id, | |
946 NPNURLVariable variable, | |
947 const char* url, | |
948 char** value, | |
949 uint32_t* len) { | |
950 if (!id) | |
951 return NPERR_INVALID_PARAM; | |
952 | |
953 if (!url || !*url || !len) | |
954 return NPERR_INVALID_URL; | |
955 | |
956 *len = 0; | |
957 std::string result; | |
958 | |
959 switch (variable) { | |
960 case NPNURLVProxy: { | |
961 result = "DIRECT"; | |
962 if (!webkit_glue::FindProxyForUrl(GURL((std::string(url))), &result)) | |
963 return NPERR_GENERIC_ERROR; | |
964 | |
965 break; | |
966 } | |
967 case NPNURLVCookie: { | |
968 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
969 if (!plugin) | |
970 return NPERR_GENERIC_ERROR; | |
971 | |
972 webkit_glue::WebPlugin* webplugin = plugin->webplugin(); | |
973 if (!webplugin) | |
974 return NPERR_GENERIC_ERROR; | |
975 | |
976 // Bypass third-party cookie blocking by using the url as the | |
977 // first_party_for_cookies. | |
978 GURL cookies_url((std::string(url))); | |
979 result = webplugin->GetCookies(cookies_url, cookies_url); | |
980 break; | |
981 } | |
982 default: | |
983 return NPERR_GENERIC_ERROR; | |
984 } | |
985 | |
986 // Allocate this using the NPAPI allocator. The plugin will call | |
987 // NPN_Free to free this. | |
988 *value = static_cast<char*>(NPN_MemAlloc(result.length() + 1)); | |
989 base::strlcpy(*value, result.c_str(), result.length() + 1); | |
990 *len = result.length(); | |
991 | |
992 return NPERR_NO_ERROR; | |
993 } | |
994 | |
995 NPError NPN_SetValueForURL(NPP id, | |
996 NPNURLVariable variable, | |
997 const char* url, | |
998 const char* value, | |
999 uint32_t len) { | |
1000 if (!id) | |
1001 return NPERR_INVALID_PARAM; | |
1002 | |
1003 if (!url || !*url) | |
1004 return NPERR_INVALID_URL; | |
1005 | |
1006 switch (variable) { | |
1007 case NPNURLVCookie: { | |
1008 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
1009 if (!plugin) | |
1010 return NPERR_GENERIC_ERROR; | |
1011 | |
1012 webkit_glue::WebPlugin* webplugin = plugin->webplugin(); | |
1013 if (!webplugin) | |
1014 return NPERR_GENERIC_ERROR; | |
1015 | |
1016 std::string cookie(value, len); | |
1017 GURL cookies_url((std::string(url))); | |
1018 webplugin->SetCookie(cookies_url, cookies_url, cookie); | |
1019 return NPERR_NO_ERROR; | |
1020 } | |
1021 case NPNURLVProxy: | |
1022 // We don't support setting proxy values, fall through... | |
1023 break; | |
1024 default: | |
1025 // Fall through and return an error... | |
1026 break; | |
1027 } | |
1028 | |
1029 return NPERR_GENERIC_ERROR; | |
1030 } | |
1031 | |
1032 NPError NPN_GetAuthenticationInfo(NPP id, | |
1033 const char* protocol, | |
1034 const char* host, | |
1035 int32_t port, | |
1036 const char* scheme, | |
1037 const char* realm, | |
1038 char** username, | |
1039 uint32_t* ulen, | |
1040 char** password, | |
1041 uint32_t* plen) { | |
1042 if (!id || !protocol || !host || !scheme || !realm || !username || | |
1043 !ulen || !password || !plen) | |
1044 return NPERR_INVALID_PARAM; | |
1045 | |
1046 // TODO: implement me (bug 23928) | |
1047 return NPERR_GENERIC_ERROR; | |
1048 } | |
1049 | |
1050 uint32_t NPN_ScheduleTimer(NPP id, | |
1051 uint32_t interval, | |
1052 NPBool repeat, | |
1053 void (*func)(NPP id, uint32_t timer_id)) { | |
1054 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
1055 if (!plugin) | |
1056 return 0; | |
1057 | |
1058 return plugin->ScheduleTimer(interval, repeat, func); | |
1059 } | |
1060 | |
1061 void NPN_UnscheduleTimer(NPP id, uint32_t timer_id) { | |
1062 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
1063 if (plugin) | |
1064 plugin->UnscheduleTimer(timer_id); | |
1065 } | |
1066 | |
1067 NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) { | |
1068 if (!menu) | |
1069 return NPERR_INVALID_PARAM; | |
1070 | |
1071 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
1072 if (plugin.get()) { | |
1073 return plugin->PopUpContextMenu(menu); | |
1074 } | |
1075 NOTREACHED(); | |
1076 return NPERR_GENERIC_ERROR; | |
1077 } | |
1078 | |
1079 NPBool NPN_ConvertPoint(NPP id, double sourceX, double sourceY, | |
1080 NPCoordinateSpace sourceSpace, | |
1081 double *destX, double *destY, | |
1082 NPCoordinateSpace destSpace) { | |
1083 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id)); | |
1084 if (plugin.get()) { | |
1085 return plugin->ConvertPoint(sourceX, sourceY, sourceSpace, | |
1086 destX, destY, destSpace); | |
1087 } | |
1088 NOTREACHED(); | |
1089 return false; | |
1090 } | |
1091 | |
1092 NPBool NPN_HandleEvent(NPP id, void *event, NPBool handled) { | |
1093 // TODO: Implement advanced key handling: http://crbug.com/46578 | |
1094 NOTIMPLEMENTED(); | |
1095 return false; | |
1096 } | |
1097 | |
1098 NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) { | |
1099 // TODO: Implement advanced key handling: http://crbug.com/46578 | |
1100 NOTIMPLEMENTED(); | |
1101 return false; | |
1102 } | |
1103 | |
1104 void NPN_URLRedirectResponse(NPP instance, void* notify_data, NPBool allow) { | |
1105 scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(instance)); | |
1106 if (plugin.get()) { | |
1107 plugin->URLRedirectResponse(!!allow, notify_data); | |
1108 } | |
1109 } | |
1110 | |
1111 } // extern "C" | |
OLD | NEW |