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
36using blink::WebBindings;
37
38// Declarations for stub implementations of deprecated functions, which are no
39// longer listed in npapi.h.
40extern "C" {
41void* NPN_GetJavaEnv();
42void* NPN_GetJavaPeer(NPP);
43}
44
45namespace content {
46
47// Finds a PluginInstance from an NPP.
48// The caller must take a reference if needed.
49static 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.
60static 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
84PluginHost::PluginHost() {
85  InitializeHostFuncs();
86}
87
88PluginHost::~PluginHost() {
89}
90
91PluginHost *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
101void 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
170void 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
206bool 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
310extern "C" {
311
312using content::FindInstance;
313using content::PluginHost;
314using content::PluginInstance;
315using content::WebPlugin;
316
317// Allocates memory from the host's memory space.
318void* 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
327void 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.
333uint32_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.
340void NPN_ReloadPlugins(NPBool reload_pages) {
341  blink::resetPluginCache(reload_pages ? true : false);
342}
343
344// Requests a range of bytes for a seekable stream.
345NPError 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.
359static 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.
378NPError 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
402NPError 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.
420static 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
505NPError 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
515NPError 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
545NPError 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
559int32_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
567NPError 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
586const 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
612void 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
619void 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
657void 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
671void 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
678NPError 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
831NPError 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
905void* NPN_GetJavaEnv() {
906  // TODO: implement me
907  DVLOG(1) << "NPN_GetJavaEnv is not implemented.";
908  return NULL;
909}
910
911void* NPN_GetJavaPeer(NPP) {
912  // TODO: implement me
913  DVLOG(1) << "NPN_GetJavaPeer is not implemented.";
914  return NULL;
915}
916
917void 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
923void NPN_PopPopupsEnabledState(NPP id) {
924  scoped_refptr<PluginInstance> plugin(FindInstance(id));
925  if (plugin.get())
926    plugin->PopPopupsEnabledState();
927}
928
929void 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
937NPError 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
994NPError 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
1031NPError 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
1049uint32_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
1060void 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
1066NPError 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
1078NPBool 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
1091NPBool 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
1097NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) {
1098  // TODO: Implement advanced key handling: http://crbug.com/46578
1099  NOTIMPLEMENTED();
1100  return false;
1101}
1102
1103void 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"
1111