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