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