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