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