Home | History | Annotate | Download | only in npapi
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/child/npapi/plugin_host.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/file_util.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/strings/string_piece.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/sys_string_conversions.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "build/build_config.h"
     16 #include "content/child/npapi/plugin_instance.h"
     17 #include "content/child/npapi/plugin_lib.h"
     18 #include "content/child/npapi/plugin_stream_url.h"
     19 #include "content/child/npapi/webplugin_delegate.h"
     20 #include "content/public/common/content_switches.h"
     21 #include "content/public/common/webplugininfo.h"
     22 #include "net/base/net_util.h"
     23 #include "third_party/WebKit/public/web/WebBindings.h"
     24 #include "third_party/WebKit/public/web/WebKit.h"
     25 #include "third_party/npapi/bindings/npruntime.h"
     26 #include "ui/gl/gl_implementation.h"
     27 #include "ui/gl/gl_surface.h"
     28 #include "webkit/common/user_agent/user_agent.h"
     29 
     30 #if defined(OS_MACOSX)
     31 #include "base/mac/mac_util.h"
     32 #endif
     33 
     34 using blink::WebBindings;
     35 
     36 // Declarations for stub implementations of deprecated functions, which are no
     37 // longer listed in npapi.h.
     38 extern "C" {
     39 void* NPN_GetJavaEnv();
     40 void* NPN_GetJavaPeer(NPP);
     41 }
     42 
     43 namespace content {
     44 
     45 // Finds a PluginInstance from an NPP.
     46 // The caller must take a reference if needed.
     47 static PluginInstance* FindInstance(NPP id) {
     48   if (id == NULL) {
     49     return NULL;
     50   }
     51   return reinterpret_cast<PluginInstance*>(id->ndata);
     52 }
     53 
     54 #if defined(OS_MACOSX)
     55 // Returns true if Core Animation plugins are supported. This requires that the
     56 // OS supports shared accelerated surfaces via IOSurface. This is true on Snow
     57 // Leopard and higher.
     58 static bool SupportsCoreAnimationPlugins() {
     59   if (CommandLine::ForCurrentProcess()->HasSwitch(
     60       switches::kDisableCoreAnimationPlugins))
     61     return false;
     62   // We also need to be running with desktop GL and not the software
     63   // OSMesa renderer in order to share accelerated surfaces between
     64   // processes.
     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 
     77 PluginHost::PluginHost() {
     78   InitializeHostFuncs();
     79 }
     80 
     81 PluginHost::~PluginHost() {
     82 }
     83 
     84 PluginHost *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 
     94 void 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 
    163 void 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 
    199 bool 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 
    303 extern "C" {
    304 
    305 using content::FindInstance;
    306 using content::PluginHost;
    307 using content::PluginInstance;
    308 using content::WebPlugin;
    309 
    310 // Allocates memory from the host's memory space.
    311 void* 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
    320 void 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.
    326 uint32_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.
    333 void NPN_ReloadPlugins(NPBool reload_pages) {
    334   blink::resetPluginCache(reload_pages ? true : false);
    335 }
    336 
    337 // Requests a range of bytes for a seekable stream.
    338 NPError 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.
    352 static 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.
    371 NPError 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 
    395 NPError 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.
    413 static 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 
    498 NPError 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 
    508 NPError 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 
    538 NPError 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 
    552 int32_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 
    560 NPError 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 
    579 const 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 
    599 void 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 
    606 void 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 
    644 void 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 
    658 void 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 
    665 NPError 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 
    830 NPError 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 
    904 void* NPN_GetJavaEnv() {
    905   // TODO: implement me
    906   DVLOG(1) << "NPN_GetJavaEnv is not implemented.";
    907   return NULL;
    908 }
    909 
    910 void* NPN_GetJavaPeer(NPP) {
    911   // TODO: implement me
    912   DVLOG(1) << "NPN_GetJavaPeer is not implemented.";
    913   return NULL;
    914 }
    915 
    916 void 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 
    922 void NPN_PopPopupsEnabledState(NPP id) {
    923   scoped_refptr<PluginInstance> plugin(FindInstance(id));
    924   if (plugin.get())
    925     plugin->PopPopupsEnabledState();
    926 }
    927 
    928 void 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 
    936 NPError 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 
    993 NPError 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 
   1030 NPError 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 
   1048 uint32_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 
   1059 void 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 
   1065 NPError 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 
   1077 NPBool 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 
   1090 NPBool 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 
   1096 NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) {
   1097   // TODO: Implement advanced key handling: http://crbug.com/46578
   1098   NOTIMPLEMENTED();
   1099   return false;
   1100 }
   1101 
   1102 void 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