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