plugin_host.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/filename_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
35using blink::WebBindings;
36
37// Declarations for stub implementations of deprecated functions, which are no
38// longer listed in npapi.h.
39extern "C" {
40void* NPN_GetJavaEnv();
41void* NPN_GetJavaPeer(NPP);
42}
43
44namespace content {
45
46// Finds a PluginInstance from an NPP.
47// The caller must take a reference if needed.
48static 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.
59static 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
83PluginHost::PluginHost() {
84  InitializeHostFuncs();
85}
86
87PluginHost::~PluginHost() {
88}
89
90PluginHost *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
100void 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
169void 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
205bool 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
309extern "C" {
310
311using content::FindInstance;
312using content::PluginHost;
313using content::PluginInstance;
314using content::WebPlugin;
315
316// Allocates memory from the host's memory space.
317void* 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
326void 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.
332uint32_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.
339void NPN_ReloadPlugins(NPBool reload_pages) {
340  blink::resetPluginCache(reload_pages ? true : false);
341}
342
343// Requests a range of bytes for a seekable stream.
344NPError 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.
358static 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.
377NPError 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
401NPError 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.
419static 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
504NPError 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
514NPError 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
544NPError 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
558int32_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
566NPError 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
585const 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
605void 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
612void 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
650void 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
664void 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
671NPError 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
836NPError 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
910void* NPN_GetJavaEnv() {
911  // TODO: implement me
912  DVLOG(1) << "NPN_GetJavaEnv is not implemented.";
913  return NULL;
914}
915
916void* NPN_GetJavaPeer(NPP) {
917  // TODO: implement me
918  DVLOG(1) << "NPN_GetJavaPeer is not implemented.";
919  return NULL;
920}
921
922void 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
928void NPN_PopPopupsEnabledState(NPP id) {
929  scoped_refptr<PluginInstance> plugin(FindInstance(id));
930  if (plugin.get())
931    plugin->PopPopupsEnabledState();
932}
933
934void 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
942NPError 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
999NPError 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
1036NPError 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
1054uint32_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
1065void 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
1071NPError 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
1083NPBool 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
1096NPBool 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
1102NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) {
1103  // TODO: Implement advanced key handling: http://crbug.com/46578
1104  NOTIMPLEMENTED();
1105  return false;
1106}
1107
1108void 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"
1116