<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/plugins/plugin_host.h"

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "webkit/default_plugin/default_plugin_shared.h"
#include "webkit/glue/glue_util.h"
#include "webkit/glue/webplugin.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/plugins/plugin_instance.h"
#include "webkit/glue/plugins/plugin_lib.h"
#include "webkit/glue/plugins/plugin_list.h"
#include "webkit/glue/plugins/plugin_stream_url.h"
#include "third_party/npapi/bindings/npruntime.h"

extern "C" {

// FindInstance()
// Finds a PluginInstance from an NPP.
// The caller must take a reference if needed.
NPAPI::PluginInstance* FindInstance(NPP id) {
  if (id == NULL) {
    NOTREACHED();
    return NULL;
  }

  return (NPAPI::PluginInstance *)id->ndata;
}

namespace NPAPI
{
scoped_refptr<PluginHost> PluginHost::singleton_;

static const int kFlashMessageThrottleDelayMs = 10;

PluginHost::PluginHost()
#pragma warning(suppress: 4355)  // can use this
    : throttle_factory_(this) {
  InitializeHostFuncs();
}

PluginHost::~PluginHost() {
}

PluginHost *PluginHost::Singleton() {
  if (singleton_.get() == NULL) {
    singleton_ = new PluginHost();
  }

  DCHECK(singleton_.get() != NULL);
  return singleton_;
}

void PluginHost::InitializeHostFuncs() {
  memset(&host_funcs_, 0, sizeof(host_funcs_));
  host_funcs_.size = sizeof(host_funcs_);
  host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR);

  // The "basic" functions
  host_funcs_.geturl = &NPN_GetURL;
  host_funcs_.posturl = &NPN_PostURL;
  host_funcs_.requestread = &NPN_RequestRead;
  host_funcs_.newstream = &NPN_NewStream;
  host_funcs_.write = &NPN_Write;
  host_funcs_.destroystream = &NPN_DestroyStream;
  host_funcs_.status = &NPN_Status;
  host_funcs_.uagent = &NPN_UserAgent;
  host_funcs_.memalloc = &NPN_MemAlloc;
  host_funcs_.memfree = &NPN_MemFree;
  host_funcs_.memflush = &NPN_MemFlush;
  host_funcs_.reloadplugins = &NPN_ReloadPlugins;

  // We don't implement java yet
  host_funcs_.getJavaEnv = &NPN_GetJavaEnv;
  host_funcs_.getJavaPeer = &NPN_GetJavaPeer;

  // Advanced functions we implement
  host_funcs_.geturlnotify = &NPN_GetURLNotify;
  host_funcs_.posturlnotify = &NPN_PostURLNotify;
  host_funcs_.getvalue = &NPN_GetValue;
  host_funcs_.setvalue = &NPN_SetValue;
  host_funcs_.invalidaterect = &NPN_InvalidateRect;
  host_funcs_.invalidateregion = &NPN_InvalidateRegion;
  host_funcs_.forceredraw = &NPN_ForceRedraw;

  // These come from the Javascript Engine
  host_funcs_.getstringidentifier = NPN_GetStringIdentifier;
  host_funcs_.getstringidentifiers = NPN_GetStringIdentifiers;
  host_funcs_.getintidentifier = NPN_GetIntIdentifier;
  host_funcs_.identifierisstring = NPN_IdentifierIsString;
  host_funcs_.utf8fromidentifier = NPN_UTF8FromIdentifier;
  host_funcs_.intfromidentifier = NPN_IntFromIdentifier;
  host_funcs_.createobject = NPN_CreateObject;
  host_funcs_.retainobject = NPN_RetainObject;
  host_funcs_.releaseobject = NPN_ReleaseObject;
  host_funcs_.invoke = NPN_Invoke;
  host_funcs_.invokeDefault = NPN_InvokeDefault;
  host_funcs_.evaluate = NPN_Evaluate;
  host_funcs_.getproperty = NPN_GetProperty;
  host_funcs_.setproperty = NPN_SetProperty;
  host_funcs_.removeproperty = NPN_RemoveProperty;
  host_funcs_.hasproperty = NPN_HasProperty;
  host_funcs_.hasmethod = NPN_HasMethod;
  host_funcs_.releasevariantvalue = NPN_ReleaseVariantValue;
  host_funcs_.setexception = NPN_SetException;
  host_funcs_.pushpopupsenabledstate = &NPN_PushPopupsEnabledState;
  host_funcs_.poppopupsenabledstate = &NPN_PopPopupsEnabledState;
  host_funcs_.enumerate = &NPN_Enumerate;
  host_funcs_.pluginthreadasynccall = &NPN_PluginThreadAsyncCall;
  host_funcs_.construct = &NPN_Construct;

}

void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
  // When running in the plugin process, we need to patch the NPN functions
  // that the plugin calls to interact with NPObjects that we give.  Otherwise
  // the plugin will call the v8 NPN functions, which won't work since we have
  // an NPObjectProxy and not a real v8 implementation.
  if (overrides->invoke)
    host_funcs_.invoke = overrides->invoke;

  if (overrides->invokeDefault)
    host_funcs_.invokeDefault = overrides->invokeDefault;

  if (overrides->evaluate)
    host_funcs_.evaluate = overrides->evaluate;

  if (overrides->getproperty)
    host_funcs_.getproperty = overrides->getproperty;

  if (overrides->setproperty)
    host_funcs_.setproperty = overrides->setproperty;

  if (overrides->removeproperty)
    host_funcs_.removeproperty = overrides->removeproperty;

  if (overrides->hasproperty)
    host_funcs_.hasproperty = overrides->hasproperty;

  if (overrides->hasmethod)
    host_funcs_.hasmethod = overrides->hasmethod;

  if (overrides->setexception)
    host_funcs_.setexception = overrides->setexception;

  if (overrides->enumerate)
    host_funcs_.enumerate = overrides->enumerate;
}

void PluginHost::InvalidateRect(NPP id, NPRect* invalidRect) {
  if (!invalidRect) {
    NOTREACHED();
    return;
  }

  // Invalidates specified drawing area prior to repainting or refreshing a
  // windowless plugin

  // Before a windowless plugin can refresh part of its drawing area, it must
  // first invalidate it.  This function causes the NPP_HandleEvent method to
  // pass an update event or a paint message to the plug-in.  After calling
  // this method, the plug-in recieves a paint message asynchronously.

  // The browser redraws invalid areas of the document and any windowless 
  // plug-ins at regularly timed intervals. To force a paint message, the 
  // plug-in can call NPN_ForceRedraw after calling this method.

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  DCHECK(plugin.get() != NULL);

  if (plugin.get() && plugin->webplugin()) {
    if (plugin->throttle_invalidate()) {
      // We need to track plugin invalidates on a per instance basis.
      ThrottledInvalidates plugin_instance_invalidates;
      InstanceThrottledInvalidatesMap::iterator invalidate_index = 
          instance_throttled_invalidates_.find(id);
      if (invalidate_index != instance_throttled_invalidates_.end()) {
        plugin_instance_invalidates = (*invalidate_index).second;
      }

      bool throttle_active = 
          (plugin_instance_invalidates.throttled_invalidates.size() > 0);

      gfx::Rect rect(invalidRect->left,
                     invalidRect->top,
                     invalidRect->right - invalidRect->left,
                     invalidRect->bottom - invalidRect->top);

      plugin_instance_invalidates.throttled_invalidates.push_back(rect);

      if (!throttle_active) {
        // We hold a reference to the plugin instance to avoid race conditions
        // due to the instance being released before the OnInvalidateRect
        // function is invoked.
        plugin->AddRef();
        MessageLoop::current()->PostDelayedTask(FROM_HERE,
            throttle_factory_.NewRunnableMethod(&PluginHost::OnInvalidateRect, 
                                                id, plugin.get()),
            kFlashMessageThrottleDelayMs);
      } 
      instance_throttled_invalidates_[id] = plugin_instance_invalidates;
    } else {
      gfx::Rect rect(invalidRect->left,
                     invalidRect->top,
                     invalidRect->right - invalidRect->left,
                     invalidRect->bottom - invalidRect->top);
        plugin->webplugin()->InvalidateRect(rect);
    }
  }
}

bool PluginHost::SetPostData(const char *buf,
                             uint32 length,
                             std::vector<std::string>* names,
                             std::vector<std::string>* values,
                             std::vector<char>* body) {
  // Use a state table to do the parsing.  Whitespace must be 
  // trimmed after the fact if desired.  In our case, we actually
  // don't care about the whitespace, because we're just going to
  // pass this back into another POST.  This function strips out the
  // "Content-length" header and does not append it to the request.

  // 
  // This parser takes action only on state changes.
  //
  // Transition table:
  //                  :       \n  NULL    Other
  // 0 GetHeader      1       2   4       0
  // 1 GetValue       1       0   3       1    
  // 2 GetData        2       2   3       2
  // 3 DONE           
  // 4 ERR
  //
  enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER };
  enum { GETNAME, GETVALUE, GETDATA, DONE, ERR };
  int statemachine[3][4] = { { GETVALUE, GETDATA, ERR,  GETNAME },
                             { GETVALUE, GETNAME, DONE, GETVALUE },
                             { GETDATA,  GETDATA, DONE, GETDATA } };
  std::string name, value;
  char *ptr = (char*)buf;
  char *start = ptr;
  int state = GETNAME;  // initial state
  bool done = false;
  bool err = false;
  do {
    int input;

    // Translate the current character into an input
    // for the state table.
    switch (*ptr) {
      case ':' : 
        input = INPUT_COLON;   
        break;
      case '\n': 
        input = INPUT_NEWLINE; 
        break;
      case 0   : 
        input = INPUT_NULL;    
        break;
      default  : 
        input = INPUT_OTHER;   
        break;
    }

    int newstate = statemachine[state][input];

    // Take action based on the new state.
    if (state != newstate) {
      switch (newstate) {
      case GETNAME:
        // Got a value.
        value = std::string(start, ptr - start);
        TrimWhitespace(value, TRIM_ALL, &value);
        // If the name field is empty, we'll skip this header
        // but we won't error out.  
        if (!name.empty() && name != "content-length") {
          names->push_back(name);
          values->push_back(value);
        }
        start = ptr + 1;
        break;
      case GETVALUE:
        // Got a header.
        name = StringToLowerASCII(std::string(start, ptr - start));
        TrimWhitespace(name, TRIM_ALL, &name);
        start = ptr + 1;
        break; 
      case GETDATA:
      {
        // Finished headers, now get body.
        start = ptr + 1;
        size_t previous_size = body->size();
        size_t new_body_size = length - static_cast<int>(start - buf);
        body->resize(previous_size + new_body_size);
        if (!body->empty())
          memcpy(&body->front() + previous_size, start, new_body_size);
        done = true;
        break;
      }
      case ERR:
        // error
        err = true;
        done = true;
        break;
      }
    } 
    state = newstate;
    ptr++;
  } while (!done);

  return !err;
}

void PluginHost::OnInvalidateRect(NPP id, PluginInstance* instance) {
  if (!instance) {
    NOTREACHED();
    return;
  }

  InstanceThrottledInvalidatesMap::iterator invalidate_index = 
      instance_throttled_invalidates_.find(id);
  if (invalidate_index == instance_throttled_invalidates_.end()) {
    NOTREACHED();
    instance->Release();
    return;
  }

  ThrottledInvalidates plugin_instance_invalidates = 
      (*invalidate_index).second;

  if (instance->webplugin()) {
    for (unsigned int throttle_index = 0; 
         throttle_index < 
            plugin_instance_invalidates.throttled_invalidates.size();
         throttle_index++) {
      instance->webplugin()->InvalidateRect(
          plugin_instance_invalidates.throttled_invalidates[throttle_index]);
    }
  }

  instance->Release();
  instance_throttled_invalidates_.erase(invalidate_index);
}

} // namespace NPAPI

// Allocates memory from the host's memory space.
void* NPN_MemAlloc(uint32 size) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    // Note: We must use the same allocator/deallocator
    // that is used by the javascript library, as some of the
    // JS APIs will pass memory to the plugin which the plugin
    // will attempt to free.
    return malloc(size);
  }
  return NULL;
}

// Deallocates memory from the host's memory space
void NPN_MemFree(void* ptr) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    if (ptr != NULL && ptr != (void*)-1) {
      free(ptr);
    }
  }
}

// Requests that the host free a specified amount of memory.
uint32 NPN_MemFlush(uint32 size) {
  // This is not relevant on Windows; MAC specific
  return size;
}

// This is for dynamic discovery of new plugins.
// Should force a re-scan of the plugins directory to load new ones.
void  NPN_ReloadPlugins(NPBool reloadPages) {
  // TODO: implement me
}

// Requests a range of bytes for a seekable stream.
NPError  NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) {
  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

static bool IsJavaScriptUrl(const std::string& url) {
  return StartsWithASCII(url, "javascript:", false);
}

// Generic form of GetURL for common code between
// GetURL() and GetURLNotify().
static NPError GetURLNotify(NPP id,
                            const char* url, 
                            const char* target,
                            bool notify, 
                            void* notify_data) {
  if (!url)
    return NPERR_INVALID_URL;

  bool is_javascript_url = IsJavaScriptUrl(url);

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get()) {
    plugin->webplugin()->HandleURLRequest("GET", 
                                          is_javascript_url, 
                                          target, 
                                          0, 
                                          0, 
                                          false, 
                                          notify, 
                                          url, 
                                          notify_data,
                                          plugin->popups_allowed());
  } else {
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }
  return NPERR_NO_ERROR;
}

// Requests creation of a new stream with the contents of the 
// specified URL; gets notification of the result.
NPError NPN_GetURLNotify(NPP id,
                         const char* url, 
                         const char* target,
                         void* notify_data) {
  // This is identical to NPN_GetURL, but after finishing, the 
  // browser will call NPP_URLNotify to inform the plugin that 
  // it has completed.

  // According to the NPAPI documentation, if target == _self 
  // or a parent to _self, the browser should return NPERR_INVALID_PARAM, 
  // because it can't notify the plugin once deleted.  This is
  // absolutely false; firefox doesn't do this, and Flash relies on
  // being able to use this.

  // Also according to the NPAPI documentation, we should return
  // NPERR_INVALID_URL if the url requested is not valid.  However,
  // this would require that we synchronously start fetching the 
  // URL.  That just isn't practical.  As such, there really is
  // no way to return this error.  From looking at the Firefox 
  // implementation, it doesn't look like Firefox does this either.

  return GetURLNotify(id, url, target, true, notify_data);
}

NPError  NPN_GetURL(NPP id, const char* url, const char* target) {
  // Notes:
  //    Request from the Plugin to fetch content either for the plugin
  //    or to be placed into a browser window.
  //
  // If target == null, the browser fetches content and streams to plugin.
  //    otherwise, the browser loads content into an existing browser frame.
  // If the target is the window/frame containing the plugin, the plugin 
  //    may be destroyed.
  // If the target is _blank, a mailto: or news: url open content in a new 
  //    browser window
  // If the target is _self, no other instance of the plugin is created.  The
  //    plugin continues to operate in its own window

  return GetURLNotify(id, url, target, false, 0);
}

// Generic form of PostURL for common code between
// PostURL() and PostURLNotify().
static NPError PostURLNotify(NPP id,
                             const char* url,
                             const char* target,
                             uint32 len,
                             const char* buf,
                             NPBool file,
                             bool notify,
                             void* notify_data) {
  if (!url)
    return NPERR_INVALID_URL;

  if (file) {
    // Unfortunately, our WebKit requests can support files which
    // contain *only* data.  But the files from NPAPI contain
    // headers + data!  So, we need to read the file, extract 
    // the headers, write the data back to a new file, and then
    // finally we can post the file.  TODO: Implement me!
    // TODO: implement me
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }

  bool is_javascript_url = IsJavaScriptUrl(url);

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get()) {
    // The post data sent by a plugin contains both headers
    // and post data.  Example:
    //      Content-type: text/html
    //      Content-length: 200
    //
    //      <200 bytes of content here>
    //
    // Unfortunately, our stream needs these broken apart, 
    // so we need to parse the data and set headers and data
    // separately.
    plugin->webplugin()->HandleURLRequest("POST", 
                                          is_javascript_url, 
                                          target, 
                                          len, 
                                          buf, 
                                          file ? true : false, 
                                          notify, 
                                          url, 
                                          notify_data,
                                          plugin->popups_allowed());
    return NPERR_NO_ERROR;
  } else {
    NOTREACHED();
  }
  return NPERR_GENERIC_ERROR;
}

NPError  NPN_PostURLNotify(NPP id,
                           const char* url,
                           const char* target,
                           uint32 len,
                           const char* buf,
                           NPBool file,
                           void* notify_data) {
  return PostURLNotify(id, url, target, len, buf, file, true, notify_data);
}

NPError  NPN_PostURL(NPP id,
                     const char* url,
                     const char* target,
                     uint32 len,
                     const char* buf,
                     NPBool file) {
  // POSTs data to an URL, either from a temp file or a buffer.
  // If file is true, buf contains a temp file (which host will delete after
  //   completing), and len contains the length of the filename.
  // If file is false, buf contains the data to send, and len contains the 
  //   length of the buffer
  //
  // If target is null, 
  //   server response is returned to the plugin
  // If target is _current, _self, or _top,
  //   server response is written to the plugin window and plugin is unloaded.
  // If target is _new or _blank,
  //   server response is written to a new browser window
  // If target is an existing frame, 
  //   server response goes to that frame.
  //
  // For protocols other than FTP
  //   file uploads must be line-end converted from \r\n to \n
  // 
  // Note:  you cannot specify headers (even a blank line) in a memory buffer,
  //        use NPN_PostURLNotify

  return PostURLNotify(id, url, target, len, buf, file, false, 0);
}

NPError NPN_NewStream(NPP id,
                      NPMIMEType type,
                      const char* target,
                      NPStream** stream) {
  // Requests creation of a new data stream produced by the plugin, 
  // consumed by the browser.
  // 
  // Browser should put this stream into a window target.
  //

  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

int32 NPN_Write(NPP id, NPStream* stream, int32 len, void* buffer) {
  // Writes data to an existing Plugin-created stream.

  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) {
  // Destroys a stream (could be created by plugin or browser).
  //
  // Reasons:
  //    NPRES_DONE          - normal completion
  //    NPRES_USER_BREAK    - user terminated
  //    NPRES_NETWORK_ERROR - network error (all errors fit here?)
  //
  //

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get() == NULL) {
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }

  return plugin->NPP_DestroyStream(stream, reason);
}

const char* NPN_UserAgent(NPP id) {
  // Flash passes in a null id during the NP_initialize call.  We need to
  // default to the Mozilla user agent if we don't have an NPP instance or
  // else Flash won't request windowless mode.
  if (id) {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    if (plugin.get() && !plugin->use_mozilla_user_agent())
      return webkit_glue::GetDefaultUserAgent().c_str();
  }

  static const char *UA = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20061103 Firefox/2.0a1";
  return UA;
}

void NPN_Status(NPP id, const char* message) {
  // Displays a message on the status line of the browser window.

  // TODO: implement me
}

void NPN_InvalidateRect(NPP id, NPRect *invalidRect) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    host->InvalidateRect(id, invalidRect);
  }
}

void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) {
  // Invalidates a specified drawing region prior to repainting
  // or refreshing a window-less plugin.
  //
  // Similar to NPN_InvalidateRect.

  // TODO: implement me
}

void NPN_ForceRedraw(NPP id) {
  // Forces repaint for a windowless plug-in.
  //
  // Once a value has been invalidated with NPN_InvalidateRect/
  // NPN_InvalidateRegion, ForceRedraw can be used to force a paint message.
  //
  // The plugin will receive a WM_PAINT message, the lParam of the WM_PAINT
  // message holds a pointer to an NPRect that is the bounding box of the
  // update area.
  // Since the plugin and browser share the same HDC, before drawing, the
  // plugin is responsible fro saving the current HDC settings, setting up
  // its own environment, drawing, and restoring the HDC to the previous
  // settings.  The HDC settings must be restored whenever control returns
  // back to the browser, either before returning from NPP_HandleEvent or
  // before calling a drawing-related netscape method.
  //

  // TODO: implement me
}

NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) {
  // Allows the plugin to query the browser for information
  //
  // Variables:
  //    NPNVxDisplay (unix only)
  //    NPNVxtAppContext (unix only)
  //    NPNVnetscapeWindow (win only) - Gets the native window on which the
  //              plug-in drawing occurs, returns HWND
  //    NPNVjavascriptEnabledBool:  tells whether Javascript is enabled
  //    NPNVasdEnabledBool:  tells whether SmartUpdate is enabled
  //    NPNVOfflineBool: tells whether offline-mode is enabled

  NPError rv = NPERR_GENERIC_ERROR;

  switch (variable) {
  case NPNVWindowNPObject:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject();
    // Return value is expected to be retained, as
    // described here:
    // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
    if (np_object) {
      NPN_RetainObject(np_object);
      void **v = (void **)value;
      *v = np_object;
      rv = NPERR_NO_ERROR;
    } else {
      NOTREACHED();
    }
    break;
  }
  case NPNVPluginElementNPObject:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    NPObject *np_object = plugin->webplugin()->GetPluginElement();
    // Return value is expected to be retained, as
    // described here:
    // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
    if (np_object) {
      NPN_RetainObject(np_object);
      void **v = (void **)value;
      *v = np_object;
      rv = NPERR_NO_ERROR;
    } else {
      NOTREACHED();
    }
    break;
  }
  case NPNVnetscapeWindow:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    HWND handle = plugin->window_handle();
    *((void**)value) = (void*)handle;
    rv = NPERR_NO_ERROR;
    break;
  }
  case NPNVjavascriptEnabledBool:
  {
    // yes, JS is enabled.
    *((void**)value) = (void*)1;
    rv = NPERR_NO_ERROR;
    break;
  }
  case NPNVserviceManager:
  {
    NPAPI::PluginInstance* instance =
        NPAPI::PluginInstance::GetInitializingInstance();
    if (instance) {
      instance->GetServiceManager(reinterpret_cast<void**>(value));
    } else {
      NOTREACHED();
    }

    rv = NPERR_NO_ERROR;
    break;
  }
  case default_plugin::kMissingPluginStatusStart +
       default_plugin::MISSING_PLUGIN_AVAILABLE:
  // fall through
  case default_plugin::kMissingPluginStatusStart +
       default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD:
  {
    // This is a hack for the default plugin to send notification to renderer.
    // Because we will check if the plugin is default plugin, we don't need
    // to worry about future standard change that may conflict with the
    // variable definition.
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    if (plugin->plugin_lib()->plugin_info().file == kDefaultPluginDllName)
      plugin->webplugin()->OnMissingPluginStatus(
          variable - default_plugin::kMissingPluginStatusStart);
    break;
  }
  default:
  {
    // TODO: implement me
    break;
  }
  }
  return rv;
}

NPError  NPN_SetValue(NPP id, NPPVariable variable, void *value) {
  // Allows the plugin to set various modes

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  switch(variable)
  {
  case NPPVpluginWindowBool:
  {
    // Sets windowless mode for display of the plugin
    // Note: the documentation at http://developer.mozilla.org/en/docs/NPN_SetValue
    // is wrong.  When value is NULL, the mode is set to true.  This is the same
    // way Mozilla works.
    bool mode = (value == 0);
    plugin->set_windowless(mode);
    return NPERR_NO_ERROR;
  }
  case NPPVpluginTransparentBool:
  {
    // Sets transparent mode for display of the plugin
    //
    // Transparent plugins require the browser to paint the background
    // before having the plugin paint.  By default, windowless plugins
    // are transparent.  Making a windowless plugin opaque means that
    // the plugin does not require the browser to paint the background.
    //
    bool mode = (value != 0);
    plugin->set_transparent(mode);
    return NPERR_NO_ERROR;
  }
  case NPPVjavascriptPushCallerBool:
    // Specifies whether you are pushing or popping the JSContext off
    // the stack
    // TODO: implement me
    return NPERR_GENERIC_ERROR;
  case NPPVpluginKeepLibraryInMemory:
    // Tells browser that plugin dll should live longer than usual.
    // TODO: implement me
    return NPERR_GENERIC_ERROR;
  default:
    // TODO: implement me
    break;
  }

  NOTREACHED();
  return NPERR_GENERIC_ERROR;
}

void *NPN_GetJavaEnv() {
  // TODO: implement me
  return NULL;
}

void *NPN_GetJavaPeer(NPP) {
  // TODO: implement me
  return NULL;
}

void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PushPopupsEnabledState(enabled);
  }
}

void NPN_PopPopupsEnabledState(NPP id) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PopPopupsEnabledState();
  }
}

void NPN_PluginThreadAsyncCall(NPP id,
                         void (*func)(void *),
                         void *userData) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PluginThreadAsyncCall(func, userData);
  }
}

bool NPN_Construct(NPP npp,
                   NPObject* obj,
                   const NPVariant *args,
                   uint32_t argCount,
                   NPVariant *result) {
  NOTREACHED();
  return false;
}

} // extern "C"

<commit_msg>Fixes the following bugs reported with the Silverlight plugin:-<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/plugins/plugin_host.h"

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "webkit/default_plugin/default_plugin_shared.h"
#include "webkit/glue/glue_util.h"
#include "webkit/glue/webplugin.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/plugins/plugin_instance.h"
#include "webkit/glue/plugins/plugin_lib.h"
#include "webkit/glue/plugins/plugin_list.h"
#include "webkit/glue/plugins/plugin_stream_url.h"
#include "third_party/npapi/bindings/npruntime.h"

extern "C" {

// FindInstance()
// Finds a PluginInstance from an NPP.
// The caller must take a reference if needed.
NPAPI::PluginInstance* FindInstance(NPP id) {
  if (id == NULL) {
    NOTREACHED();
    return NULL;
  }

  return (NPAPI::PluginInstance *)id->ndata;
}

namespace NPAPI
{
scoped_refptr<PluginHost> PluginHost::singleton_;

static const int kFlashMessageThrottleDelayMs = 10;

PluginHost::PluginHost()
#pragma warning(suppress: 4355)  // can use this
    : throttle_factory_(this) {
  InitializeHostFuncs();
}

PluginHost::~PluginHost() {
}

PluginHost *PluginHost::Singleton() {
  if (singleton_.get() == NULL) {
    singleton_ = new PluginHost();
  }

  DCHECK(singleton_.get() != NULL);
  return singleton_;
}

void PluginHost::InitializeHostFuncs() {
  memset(&host_funcs_, 0, sizeof(host_funcs_));
  host_funcs_.size = sizeof(host_funcs_);
  host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR);

  // The "basic" functions
  host_funcs_.geturl = &NPN_GetURL;
  host_funcs_.posturl = &NPN_PostURL;
  host_funcs_.requestread = &NPN_RequestRead;
  host_funcs_.newstream = &NPN_NewStream;
  host_funcs_.write = &NPN_Write;
  host_funcs_.destroystream = &NPN_DestroyStream;
  host_funcs_.status = &NPN_Status;
  host_funcs_.uagent = &NPN_UserAgent;
  host_funcs_.memalloc = &NPN_MemAlloc;
  host_funcs_.memfree = &NPN_MemFree;
  host_funcs_.memflush = &NPN_MemFlush;
  host_funcs_.reloadplugins = &NPN_ReloadPlugins;

  // We don't implement java yet
  host_funcs_.getJavaEnv = &NPN_GetJavaEnv;
  host_funcs_.getJavaPeer = &NPN_GetJavaPeer;

  // Advanced functions we implement
  host_funcs_.geturlnotify = &NPN_GetURLNotify;
  host_funcs_.posturlnotify = &NPN_PostURLNotify;
  host_funcs_.getvalue = &NPN_GetValue;
  host_funcs_.setvalue = &NPN_SetValue;
  host_funcs_.invalidaterect = &NPN_InvalidateRect;
  host_funcs_.invalidateregion = &NPN_InvalidateRegion;
  host_funcs_.forceredraw = &NPN_ForceRedraw;

  // These come from the Javascript Engine
  host_funcs_.getstringidentifier = NPN_GetStringIdentifier;
  host_funcs_.getstringidentifiers = NPN_GetStringIdentifiers;
  host_funcs_.getintidentifier = NPN_GetIntIdentifier;
  host_funcs_.identifierisstring = NPN_IdentifierIsString;
  host_funcs_.utf8fromidentifier = NPN_UTF8FromIdentifier;
  host_funcs_.intfromidentifier = NPN_IntFromIdentifier;
  host_funcs_.createobject = NPN_CreateObject;
  host_funcs_.retainobject = NPN_RetainObject;
  host_funcs_.releaseobject = NPN_ReleaseObject;
  host_funcs_.invoke = NPN_Invoke;
  host_funcs_.invokeDefault = NPN_InvokeDefault;
  host_funcs_.evaluate = NPN_Evaluate;
  host_funcs_.getproperty = NPN_GetProperty;
  host_funcs_.setproperty = NPN_SetProperty;
  host_funcs_.removeproperty = NPN_RemoveProperty;
  host_funcs_.hasproperty = NPN_HasProperty;
  host_funcs_.hasmethod = NPN_HasMethod;
  host_funcs_.releasevariantvalue = NPN_ReleaseVariantValue;
  host_funcs_.setexception = NPN_SetException;
  host_funcs_.pushpopupsenabledstate = &NPN_PushPopupsEnabledState;
  host_funcs_.poppopupsenabledstate = &NPN_PopPopupsEnabledState;
  host_funcs_.enumerate = &NPN_Enumerate;
  host_funcs_.pluginthreadasynccall = &NPN_PluginThreadAsyncCall;
  host_funcs_.construct = &NPN_Construct;

}

void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
  // When running in the plugin process, we need to patch the NPN functions
  // that the plugin calls to interact with NPObjects that we give.  Otherwise
  // the plugin will call the v8 NPN functions, which won't work since we have
  // an NPObjectProxy and not a real v8 implementation.
  if (overrides->invoke)
    host_funcs_.invoke = overrides->invoke;

  if (overrides->invokeDefault)
    host_funcs_.invokeDefault = overrides->invokeDefault;

  if (overrides->evaluate)
    host_funcs_.evaluate = overrides->evaluate;

  if (overrides->getproperty)
    host_funcs_.getproperty = overrides->getproperty;

  if (overrides->setproperty)
    host_funcs_.setproperty = overrides->setproperty;

  if (overrides->removeproperty)
    host_funcs_.removeproperty = overrides->removeproperty;

  if (overrides->hasproperty)
    host_funcs_.hasproperty = overrides->hasproperty;

  if (overrides->hasmethod)
    host_funcs_.hasmethod = overrides->hasmethod;

  if (overrides->setexception)
    host_funcs_.setexception = overrides->setexception;

  if (overrides->enumerate)
    host_funcs_.enumerate = overrides->enumerate;
}

void PluginHost::InvalidateRect(NPP id, NPRect* invalidRect) {
  if (!invalidRect) {
    NOTREACHED();
    return;
  }

  // Invalidates specified drawing area prior to repainting or refreshing a
  // windowless plugin

  // Before a windowless plugin can refresh part of its drawing area, it must
  // first invalidate it.  This function causes the NPP_HandleEvent method to
  // pass an update event or a paint message to the plug-in.  After calling
  // this method, the plug-in recieves a paint message asynchronously.

  // The browser redraws invalid areas of the document and any windowless 
  // plug-ins at regularly timed intervals. To force a paint message, the 
  // plug-in can call NPN_ForceRedraw after calling this method.

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  DCHECK(plugin.get() != NULL);

  if (plugin.get() && plugin->webplugin()) {
    if (!plugin->windowless()) {
      RECT rect = {0};
      rect.left = invalidRect->left;
      rect.right = invalidRect->right;
      rect.top = invalidRect->top;
      rect.bottom = invalidRect->bottom;
      ::InvalidateRect(plugin->window_handle(), &rect, FALSE);
      return;
    }

    if (plugin->throttle_invalidate()) {
      // We need to track plugin invalidates on a per instance basis.
      ThrottledInvalidates plugin_instance_invalidates;
      InstanceThrottledInvalidatesMap::iterator invalidate_index = 
          instance_throttled_invalidates_.find(id);
      if (invalidate_index != instance_throttled_invalidates_.end()) {
        plugin_instance_invalidates = (*invalidate_index).second;
      }

      bool throttle_active = 
          (plugin_instance_invalidates.throttled_invalidates.size() > 0);

      gfx::Rect rect(invalidRect->left,
                     invalidRect->top,
                     invalidRect->right - invalidRect->left,
                     invalidRect->bottom - invalidRect->top);

      plugin_instance_invalidates.throttled_invalidates.push_back(rect);

      if (!throttle_active) {
        // We hold a reference to the plugin instance to avoid race conditions
        // due to the instance being released before the OnInvalidateRect
        // function is invoked.
        plugin->AddRef();
        MessageLoop::current()->PostDelayedTask(FROM_HERE,
            throttle_factory_.NewRunnableMethod(&PluginHost::OnInvalidateRect, 
                                                id, plugin.get()),
            kFlashMessageThrottleDelayMs);
      } 
      instance_throttled_invalidates_[id] = plugin_instance_invalidates;
    } else {
      gfx::Rect rect(invalidRect->left,
                     invalidRect->top,
                     invalidRect->right - invalidRect->left,
                     invalidRect->bottom - invalidRect->top);
        plugin->webplugin()->InvalidateRect(rect);
    }
  }
}

bool PluginHost::SetPostData(const char *buf,
                             uint32 length,
                             std::vector<std::string>* names,
                             std::vector<std::string>* values,
                             std::vector<char>* body) {
  // Use a state table to do the parsing.  Whitespace must be 
  // trimmed after the fact if desired.  In our case, we actually
  // don't care about the whitespace, because we're just going to
  // pass this back into another POST.  This function strips out the
  // "Content-length" header and does not append it to the request.

  // 
  // This parser takes action only on state changes.
  //
  // Transition table:
  //                  :       \n  NULL    Other
  // 0 GetHeader      1       2   4       0
  // 1 GetValue       1       0   3       1    
  // 2 GetData        2       2   3       2
  // 3 DONE           
  // 4 ERR
  //
  enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER };
  enum { GETNAME, GETVALUE, GETDATA, DONE, ERR };
  int statemachine[3][4] = { { GETVALUE, GETDATA, ERR,  GETNAME },
                             { GETVALUE, GETNAME, DONE, GETVALUE },
                             { GETDATA,  GETDATA, DONE, GETDATA } };
  std::string name, value;
  char *ptr = (char*)buf;
  char *start = ptr;
  int state = GETNAME;  // initial state
  bool done = false;
  bool err = false;
  do {
    int input;

    // Translate the current character into an input
    // for the state table.
    switch (*ptr) {
      case ':' : 
        input = INPUT_COLON;   
        break;
      case '\n': 
        input = INPUT_NEWLINE; 
        break;
      case 0   : 
        input = INPUT_NULL;    
        break;
      default  : 
        input = INPUT_OTHER;   
        break;
    }

    int newstate = statemachine[state][input];

    // Take action based on the new state.
    if (state != newstate) {
      switch (newstate) {
      case GETNAME:
        // Got a value.
        value = std::string(start, ptr - start);
        TrimWhitespace(value, TRIM_ALL, &value);
        // If the name field is empty, we'll skip this header
        // but we won't error out.  
        if (!name.empty() && name != "content-length") {
          names->push_back(name);
          values->push_back(value);
        }
        start = ptr + 1;
        break;
      case GETVALUE:
        // Got a header.
        name = StringToLowerASCII(std::string(start, ptr - start));
        TrimWhitespace(name, TRIM_ALL, &name);
        start = ptr + 1;
        break; 
      case GETDATA:
      {
        // Finished headers, now get body.
        start = ptr + 1;
        size_t previous_size = body->size();
        size_t new_body_size = length - static_cast<int>(start - buf);
        body->resize(previous_size + new_body_size);
        if (!body->empty())
          memcpy(&body->front() + previous_size, start, new_body_size);
        done = true;
        break;
      }
      case ERR:
        // error
        err = true;
        done = true;
        break;
      }
    } 
    state = newstate;
    ptr++;
  } while (!done);

  return !err;
}

void PluginHost::OnInvalidateRect(NPP id, PluginInstance* instance) {
  if (!instance) {
    NOTREACHED();
    return;
  }

  InstanceThrottledInvalidatesMap::iterator invalidate_index = 
      instance_throttled_invalidates_.find(id);
  if (invalidate_index == instance_throttled_invalidates_.end()) {
    NOTREACHED();
    instance->Release();
    return;
  }

  ThrottledInvalidates plugin_instance_invalidates = 
      (*invalidate_index).second;

  if (instance->webplugin()) {
    for (unsigned int throttle_index = 0; 
         throttle_index < 
            plugin_instance_invalidates.throttled_invalidates.size();
         throttle_index++) {
      instance->webplugin()->InvalidateRect(
          plugin_instance_invalidates.throttled_invalidates[throttle_index]);
    }
  }

  instance->Release();
  instance_throttled_invalidates_.erase(invalidate_index);
}

} // namespace NPAPI

// Allocates memory from the host's memory space.
void* NPN_MemAlloc(uint32 size) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    // Note: We must use the same allocator/deallocator
    // that is used by the javascript library, as some of the
    // JS APIs will pass memory to the plugin which the plugin
    // will attempt to free.
    return malloc(size);
  }
  return NULL;
}

// Deallocates memory from the host's memory space
void NPN_MemFree(void* ptr) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    if (ptr != NULL && ptr != (void*)-1) {
      free(ptr);
    }
  }
}

// Requests that the host free a specified amount of memory.
uint32 NPN_MemFlush(uint32 size) {
  // This is not relevant on Windows; MAC specific
  return size;
}

// This is for dynamic discovery of new plugins.
// Should force a re-scan of the plugins directory to load new ones.
void  NPN_ReloadPlugins(NPBool reloadPages) {
  // TODO: implement me
}

// Requests a range of bytes for a seekable stream.
NPError  NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) {
  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

static bool IsJavaScriptUrl(const std::string& url) {
  return StartsWithASCII(url, "javascript:", false);
}

// Generic form of GetURL for common code between
// GetURL() and GetURLNotify().
static NPError GetURLNotify(NPP id,
                            const char* url, 
                            const char* target,
                            bool notify, 
                            void* notify_data) {
  if (!url)
    return NPERR_INVALID_URL;

  bool is_javascript_url = IsJavaScriptUrl(url);

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get()) {
    plugin->webplugin()->HandleURLRequest("GET", 
                                          is_javascript_url, 
                                          target, 
                                          0, 
                                          0, 
                                          false, 
                                          notify, 
                                          url, 
                                          notify_data,
                                          plugin->popups_allowed());
  } else {
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }
  return NPERR_NO_ERROR;
}

// Requests creation of a new stream with the contents of the 
// specified URL; gets notification of the result.
NPError NPN_GetURLNotify(NPP id,
                         const char* url, 
                         const char* target,
                         void* notify_data) {
  // This is identical to NPN_GetURL, but after finishing, the 
  // browser will call NPP_URLNotify to inform the plugin that 
  // it has completed.

  // According to the NPAPI documentation, if target == _self 
  // or a parent to _self, the browser should return NPERR_INVALID_PARAM, 
  // because it can't notify the plugin once deleted.  This is
  // absolutely false; firefox doesn't do this, and Flash relies on
  // being able to use this.

  // Also according to the NPAPI documentation, we should return
  // NPERR_INVALID_URL if the url requested is not valid.  However,
  // this would require that we synchronously start fetching the 
  // URL.  That just isn't practical.  As such, there really is
  // no way to return this error.  From looking at the Firefox 
  // implementation, it doesn't look like Firefox does this either.

  return GetURLNotify(id, url, target, true, notify_data);
}

NPError  NPN_GetURL(NPP id, const char* url, const char* target) {
  // Notes:
  //    Request from the Plugin to fetch content either for the plugin
  //    or to be placed into a browser window.
  //
  // If target == null, the browser fetches content and streams to plugin.
  //    otherwise, the browser loads content into an existing browser frame.
  // If the target is the window/frame containing the plugin, the plugin 
  //    may be destroyed.
  // If the target is _blank, a mailto: or news: url open content in a new 
  //    browser window
  // If the target is _self, no other instance of the plugin is created.  The
  //    plugin continues to operate in its own window

  return GetURLNotify(id, url, target, false, 0);
}

// Generic form of PostURL for common code between
// PostURL() and PostURLNotify().
static NPError PostURLNotify(NPP id,
                             const char* url,
                             const char* target,
                             uint32 len,
                             const char* buf,
                             NPBool file,
                             bool notify,
                             void* notify_data) {
  if (!url)
    return NPERR_INVALID_URL;

  if (file) {
    // Unfortunately, our WebKit requests can support files which
    // contain *only* data.  But the files from NPAPI contain
    // headers + data!  So, we need to read the file, extract 
    // the headers, write the data back to a new file, and then
    // finally we can post the file.  TODO: Implement me!
    // TODO: implement me
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }

  bool is_javascript_url = IsJavaScriptUrl(url);

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get()) {
    // The post data sent by a plugin contains both headers
    // and post data.  Example:
    //      Content-type: text/html
    //      Content-length: 200
    //
    //      <200 bytes of content here>
    //
    // Unfortunately, our stream needs these broken apart, 
    // so we need to parse the data and set headers and data
    // separately.
    plugin->webplugin()->HandleURLRequest("POST", 
                                          is_javascript_url, 
                                          target, 
                                          len, 
                                          buf, 
                                          file ? true : false, 
                                          notify, 
                                          url, 
                                          notify_data,
                                          plugin->popups_allowed());
    return NPERR_NO_ERROR;
  } else {
    NOTREACHED();
  }
  return NPERR_GENERIC_ERROR;
}

NPError  NPN_PostURLNotify(NPP id,
                           const char* url,
                           const char* target,
                           uint32 len,
                           const char* buf,
                           NPBool file,
                           void* notify_data) {
  return PostURLNotify(id, url, target, len, buf, file, true, notify_data);
}

NPError  NPN_PostURL(NPP id,
                     const char* url,
                     const char* target,
                     uint32 len,
                     const char* buf,
                     NPBool file) {
  // POSTs data to an URL, either from a temp file or a buffer.
  // If file is true, buf contains a temp file (which host will delete after
  //   completing), and len contains the length of the filename.
  // If file is false, buf contains the data to send, and len contains the 
  //   length of the buffer
  //
  // If target is null, 
  //   server response is returned to the plugin
  // If target is _current, _self, or _top,
  //   server response is written to the plugin window and plugin is unloaded.
  // If target is _new or _blank,
  //   server response is written to a new browser window
  // If target is an existing frame, 
  //   server response goes to that frame.
  //
  // For protocols other than FTP
  //   file uploads must be line-end converted from \r\n to \n
  // 
  // Note:  you cannot specify headers (even a blank line) in a memory buffer,
  //        use NPN_PostURLNotify

  return PostURLNotify(id, url, target, len, buf, file, false, 0);
}

NPError NPN_NewStream(NPP id,
                      NPMIMEType type,
                      const char* target,
                      NPStream** stream) {
  // Requests creation of a new data stream produced by the plugin, 
  // consumed by the browser.
  // 
  // Browser should put this stream into a window target.
  //

  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

int32 NPN_Write(NPP id, NPStream* stream, int32 len, void* buffer) {
  // Writes data to an existing Plugin-created stream.

  // TODO: implement me
  return NPERR_GENERIC_ERROR;
}

NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) {
  // Destroys a stream (could be created by plugin or browser).
  //
  // Reasons:
  //    NPRES_DONE          - normal completion
  //    NPRES_USER_BREAK    - user terminated
  //    NPRES_NETWORK_ERROR - network error (all errors fit here?)
  //
  //

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin.get() == NULL) {
    NOTREACHED();
    return NPERR_GENERIC_ERROR;
  }

  return plugin->NPP_DestroyStream(stream, reason);
}

const char* NPN_UserAgent(NPP id) {
  // Flash passes in a null id during the NP_initialize call.  We need to
  // default to the Mozilla user agent if we don't have an NPP instance or
  // else Flash won't request windowless mode.
  if (id) {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    if (plugin.get() && !plugin->use_mozilla_user_agent())
      return webkit_glue::GetDefaultUserAgent().c_str();
  }

  static const char *UA = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20061103 Firefox/2.0a1";
  return UA;
}

void NPN_Status(NPP id, const char* message) {
  // Displays a message on the status line of the browser window.

  // TODO: implement me
}

void NPN_InvalidateRect(NPP id, NPRect *invalidRect) {
  scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton();
  if (host != NULL) {
    host->InvalidateRect(id, invalidRect);
  }
}

void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) {
  // Invalidates a specified drawing region prior to repainting
  // or refreshing a window-less plugin.
  //
  // Similar to NPN_InvalidateRect.

  // TODO: implement me
}

void NPN_ForceRedraw(NPP id) {
  // Forces repaint for a windowless plug-in.
  //
  // Once a value has been invalidated with NPN_InvalidateRect/
  // NPN_InvalidateRegion, ForceRedraw can be used to force a paint message.
  //
  // The plugin will receive a WM_PAINT message, the lParam of the WM_PAINT
  // message holds a pointer to an NPRect that is the bounding box of the
  // update area.
  // Since the plugin and browser share the same HDC, before drawing, the
  // plugin is responsible fro saving the current HDC settings, setting up
  // its own environment, drawing, and restoring the HDC to the previous
  // settings.  The HDC settings must be restored whenever control returns
  // back to the browser, either before returning from NPP_HandleEvent or
  // before calling a drawing-related netscape method.
  //

  // TODO: implement me
}

NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) {
  // Allows the plugin to query the browser for information
  //
  // Variables:
  //    NPNVxDisplay (unix only)
  //    NPNVxtAppContext (unix only)
  //    NPNVnetscapeWindow (win only) - Gets the native window on which the
  //              plug-in drawing occurs, returns HWND
  //    NPNVjavascriptEnabledBool:  tells whether Javascript is enabled
  //    NPNVasdEnabledBool:  tells whether SmartUpdate is enabled
  //    NPNVOfflineBool: tells whether offline-mode is enabled

  NPError rv = NPERR_GENERIC_ERROR;

  switch (variable) {
  case NPNVWindowNPObject:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject();
    // Return value is expected to be retained, as
    // described here:
    // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
    if (np_object) {
      NPN_RetainObject(np_object);
      void **v = (void **)value;
      *v = np_object;
      rv = NPERR_NO_ERROR;
    } else {
      NOTREACHED();
    }
    break;
  }
  case NPNVPluginElementNPObject:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    NPObject *np_object = plugin->webplugin()->GetPluginElement();
    // Return value is expected to be retained, as
    // described here:
    // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
    if (np_object) {
      NPN_RetainObject(np_object);
      void **v = (void **)value;
      *v = np_object;
      rv = NPERR_NO_ERROR;
    } else {
      NOTREACHED();
    }
    break;
  }
  case NPNVnetscapeWindow:
  {
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    HWND handle = plugin->window_handle();
    *((void**)value) = (void*)handle;
    rv = NPERR_NO_ERROR;
    break;
  }
  case NPNVjavascriptEnabledBool:
  {
    // yes, JS is enabled.
    *((void**)value) = (void*)1;
    rv = NPERR_NO_ERROR;
    break;
  }
  case NPNVserviceManager:
  {
    NPAPI::PluginInstance* instance =
        NPAPI::PluginInstance::GetInitializingInstance();
    if (instance) {
      instance->GetServiceManager(reinterpret_cast<void**>(value));
    } else {
      NOTREACHED();
    }

    rv = NPERR_NO_ERROR;
    break;
  }
  case default_plugin::kMissingPluginStatusStart +
       default_plugin::MISSING_PLUGIN_AVAILABLE:
  // fall through
  case default_plugin::kMissingPluginStatusStart +
       default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD:
  {
    // This is a hack for the default plugin to send notification to renderer.
    // Because we will check if the plugin is default plugin, we don't need
    // to worry about future standard change that may conflict with the
    // variable definition.
    scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
    if (plugin->plugin_lib()->plugin_info().file == kDefaultPluginDllName)
      plugin->webplugin()->OnMissingPluginStatus(
          variable - default_plugin::kMissingPluginStatusStart);
    break;
  }
  default:
  {
    // TODO: implement me
    break;
  }
  }
  return rv;
}

NPError  NPN_SetValue(NPP id, NPPVariable variable, void *value) {
  // Allows the plugin to set various modes

  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  switch(variable)
  {
  case NPPVpluginWindowBool:
  {
    // Sets windowless mode for display of the plugin
    // Note: the documentation at http://developer.mozilla.org/en/docs/NPN_SetValue
    // is wrong.  When value is NULL, the mode is set to true.  This is the same
    // way Mozilla works.
    bool mode = (value == 0);
    plugin->set_windowless(mode);
    return NPERR_NO_ERROR;
  }
  case NPPVpluginTransparentBool:
  {
    // Sets transparent mode for display of the plugin
    //
    // Transparent plugins require the browser to paint the background
    // before having the plugin paint.  By default, windowless plugins
    // are transparent.  Making a windowless plugin opaque means that
    // the plugin does not require the browser to paint the background.
    //
    bool mode = (value != 0);
    plugin->set_transparent(mode);
    return NPERR_NO_ERROR;
  }
  case NPPVjavascriptPushCallerBool:
    // Specifies whether you are pushing or popping the JSContext off
    // the stack
    // TODO: implement me
    return NPERR_GENERIC_ERROR;
  case NPPVpluginKeepLibraryInMemory:
    // Tells browser that plugin dll should live longer than usual.
    // TODO: implement me
    return NPERR_GENERIC_ERROR;
  default:
    // TODO: implement me
    break;
  }

  NOTREACHED();
  return NPERR_GENERIC_ERROR;
}

void *NPN_GetJavaEnv() {
  // TODO: implement me
  return NULL;
}

void *NPN_GetJavaPeer(NPP) {
  // TODO: implement me
  return NULL;
}

void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PushPopupsEnabledState(enabled);
  }
}

void NPN_PopPopupsEnabledState(NPP id) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PopPopupsEnabledState();
  }
}

void NPN_PluginThreadAsyncCall(NPP id,
                         void (*func)(void *),
                         void *userData) {
  scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
  if (plugin) {
    plugin->PluginThreadAsyncCall(func, userData);
  }
}

bool NPN_Construct(NPP npp,
                   NPObject* obj,
                   const NPVariant *args,
                   uint32_t argCount,
                   NPVariant *result) {
  NOTREACHED();
  return false;
}

} // extern "C"


{'fraction_non_alphanumeric': 0.09261007939006051, 'fraction_numerical': 0.00344489910117861, 'mean_word_length': 2.4787395826996774, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 209, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 78, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5610205', 'n_tokens_mistral': 17449, 'n_tokens_neox': 16815, 'n_words': 4927}