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