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