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