<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.

#import <Cocoa/Cocoa.h>

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

#include <string>
#include <unistd.h>
#include <set>

#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/stats_counters.h"
#include "base/string_util.h"
#include "base/timer.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "webkit/default_plugin/plugin_impl.h"
#include "webkit/glue/webplugin.h"
#include "webkit/glue/plugins/coregraphics_private_symbols_mac.h"
#include "webkit/glue/plugins/fake_plugin_window_tracker_mac.h"
#include "webkit/glue/plugins/plugin_constants_win.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 "webkit/glue/webkit_glue.h"

// If we're compiling support for the QuickDraw drawing model, turn off GCC
// warnings about deprecated functions (since QuickDraw is a deprecated API).
// According to the GCC documentation, this can only be done per file, not
// pushed and popped like some options can be.
#ifndef NP_NO_QUICKDRAW
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

using webkit_glue::WebPlugin;
using webkit_glue::WebPluginDelegate;
using webkit_glue::WebPluginResourceClient;
using WebKit::WebCursorInfo;
using WebKit::WebKeyboardEvent;
using WebKit::WebInputEvent;
using WebKit::WebMouseEvent;
using WebKit::WebMouseWheelEvent;

// Important implementation notes: The Mac definition of NPAPI, particularly
// the distinction between windowed and windowless modes, differs from the
// Windows and Linux definitions.  Most of those differences are
// accomodated by the WebPluginDelegate class.

namespace {

base::LazyInstance<std::set<WebPluginDelegateImpl*> > g_active_delegates(
    base::LINKER_INITIALIZED);

WebPluginDelegateImpl* g_active_delegate;

// Helper to simplify correct usage of g_active_delegate.  Instantiating will
// set the active delegate to |delegate| for the lifetime of the object, then
// NULL when it goes out of scope.
class ScopedActiveDelegate {
public:
  explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
    g_active_delegate = delegate;
  }
  ~ScopedActiveDelegate() {
    g_active_delegate = NULL;
  }
private:
  DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
};

#ifndef NP_NO_CARBON
// Timer periods for sending idle events to Carbon plugins. The visible value
// (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
// Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
// going lower than that causes issues.
const int kVisibleIdlePeriodMs = 20;     // (50Hz)
const int kHiddenIdlePeriodMs = 125;  // (8Hz)

class CarbonIdleEventSource {
 public:
  // Returns the shared Carbon idle event source.
  static CarbonIdleEventSource* SharedInstance() {
    DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
    static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
    return event_source;
  }

  // Registers the plugin delegate as interested in receiving idle events at
  // a rate appropriate for the given visibility. A delegate can safely be
  // re-registered any number of times, with the latest registration winning.
  void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
    if (visible) {
      visible_delegates_->RegisterDelegate(delegate);
      hidden_delegates_->UnregisterDelegate(delegate);
    } else {
      hidden_delegates_->RegisterDelegate(delegate);
      visible_delegates_->UnregisterDelegate(delegate);
    }
  }

  // Removes the plugin delegate from the list of plugins receiving idle events.
  void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
    visible_delegates_->UnregisterDelegate(delegate);
    hidden_delegates_->UnregisterDelegate(delegate);
  }

 private:
  class VisibilityGroup {
   public:
    explicit VisibilityGroup(int timer_period)
        : timer_period_(timer_period), iterator_(delegates_.end()) {}

    // Adds |delegate| to this visibility group.
    void RegisterDelegate(WebPluginDelegateImpl* delegate) {
      if (delegates_.empty()) {
        timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
                     this, &VisibilityGroup::SendIdleEvents);
      }
      delegates_.insert(delegate);
    }

    // Removes |delegate| from this visibility group.
    void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
      // If a plugin changes visibility during idle event handling, it
      // may be removed from this set while SendIdleEvents is still iterating;
      // if that happens and it's next on the list, increment the iterator
      // before erasing so that the iteration won't be corrupted.
      if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
        ++iterator_;
      size_t removed = delegates_.erase(delegate);
      if (removed > 0 && delegates_.empty())
        timer_.Stop();
    }

   private:
    // Fires off idle events for each delegate in the group.
    void SendIdleEvents() {
      for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
        // Pre-increment so that the skip logic in UnregisterDelegates works.
        WebPluginDelegateImpl* delegate = *(iterator_++);
        delegate->FireIdleEvent();
      }
    }

    int timer_period_;
    base::RepeatingTimer<VisibilityGroup> timer_;
    std::set<WebPluginDelegateImpl*> delegates_;
    std::set<WebPluginDelegateImpl*>::iterator iterator_;
  };

  CarbonIdleEventSource()
      : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
        hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}

  scoped_ptr<VisibilityGroup> visible_delegates_;
  scoped_ptr<VisibilityGroup> hidden_delegates_;

  DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
};
#endif  // !NP_NO_CARBON

}  // namespace

WebPluginDelegateImpl::WebPluginDelegateImpl(
    gfx::PluginWindowHandle containing_view,
    NPAPI::PluginInstance *instance)
    : windowless_needs_set_window_(true),
      // all Mac plugins are "windowless" in the Windows/X11 sense
      windowless_(true),
      plugin_(NULL),
      instance_(instance),
      parent_(containing_view),
      quirks_(0),
      have_focus_(false),
      focus_notifier_(NULL),
      containing_window_has_focus_(false),
      initial_window_focus_(false),
      container_is_visible_(false),
      have_called_set_window_(false),
      handle_event_depth_(0) {
  memset(&window_, 0, sizeof(window_));
#ifndef NP_NO_CARBON
  memset(&cg_context_, 0, sizeof(cg_context_));
#endif
#ifndef NP_NO_QUICKDRAW
  memset(&qd_port_, 0, sizeof(qd_port_));
#endif
  instance->set_windowless(true);

  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  delegates->insert(this);
}

WebPluginDelegateImpl::~WebPluginDelegateImpl() {
  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  delegates->erase(this);

  DestroyInstance();

#ifndef NP_NO_CARBON
  if (cg_context_.window) {
    FakePluginWindowTracker::SharedInstance()->RemoveFakeWindowForDelegate(
        this, reinterpret_cast<WindowRef>(cg_context_.window));
  }
#endif
}

void WebPluginDelegateImpl::PlatformInitialize() {
  // Don't set a NULL window handle on destroy for Mac plugins.  This matches
  // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
  // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
  // destroyPlugin in WebNetscapePluginView.mm, for examples).
  quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;

  // Some plugins don't always unload cleanly, so don't unload them at shutdown.
  if (instance()->mime_type().find("x-silverlight") != std::string::npos ||
      instance()->mime_type().find("audio/x-pn-realaudio") != std::string::npos)
    instance()->plugin_lib()->PreventLibraryUnload();

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    // Create a stand-in for the browser window so that the plugin will have
    // a non-NULL WindowRef to which it can refer.
    FakePluginWindowTracker* window_tracker =
        FakePluginWindowTracker::SharedInstance();
    cg_context_.window = window_tracker->GenerateFakeWindowForDelegate(this);
    cg_context_.context = NULL;
    Rect window_bounds = { 0, 0, window_rect_.height(), window_rect_.width() };
    SetWindowBounds(reinterpret_cast<WindowRef>(cg_context_.window),
                    kWindowContentRgn, &window_bounds);
#ifndef NP_NO_QUICKDRAW
    qd_port_.port =
        GetWindowPort(reinterpret_cast<WindowRef>(cg_context_.window));
#endif
  }
#endif

  switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
    case NPDrawingModelQuickDraw:
      window_.window = &qd_port_;
      window_.type = NPWindowTypeDrawable;
      break;
#endif
    case NPDrawingModelCoreGraphics:
#ifndef NP_NO_CARBON
      if (instance()->event_model() == NPEventModelCarbon)
        window_.window = &cg_context_;
#endif
      window_.type = NPWindowTypeDrawable;
      break;
    default:
      NOTREACHED();
      break;
  }

  // TODO(stuartmorgan): We need real plugin container visibility information
  // when the plugin is initialized; for now, assume it's visible.
  // None of the calls SetContainerVisibility would make are useful at this
  // point, so we just set the initial state directly.
  container_is_visible_ = true;

#ifndef NP_NO_CARBON
  // If the plugin wants Carbon events, hook up to the source of idle events.
  if (instance()->event_model() == NPEventModelCarbon)
    UpdateIdleEventRate();
#endif
  plugin_->SetWindow(NULL);

  // QuickTime can crash if it gets other calls (e.g., NPP_Write) before it
  // gets a SetWindow call, so call SetWindow (with a 0x0 rect) immediately.
  const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
  if (plugin_info.name.find(L"QuickTime") != std::wstring::npos)
    WindowlessSetWindow(true);
}

void WebPluginDelegateImpl::PlatformDestroyInstance() {
#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon)
    CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
#endif
}

void WebPluginDelegateImpl::UpdateContext(CGContextRef context) {
#ifndef NP_NO_CARBON
  // Flash on the Mac apparently caches the context from the struct it receives
  // in NPP_SetWindow, and continues to use it even when the contents of the
  // struct have changed, so we need to call NPP_SetWindow again if the context
  // changes.
  if (instance()->event_model() == NPEventModelCarbon &&
      context != cg_context_.context) {
    cg_context_.context = context;
    WindowlessSetWindow(true);
  }
#endif
}

void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
  DCHECK(windowless_);
  WindowlessPaint(context, rect);
}

void WebPluginDelegateImpl::Print(CGContextRef context) {
  // Disabling the call to NPP_Print as it causes a crash in
  // flash in some cases. In any case this does not work as expected
  // as the EMF meta file dc passed in needs to be created with the
  // the plugin window dc as its sibling dc and the window rect
  // in .01 mm units.
}

void WebPluginDelegateImpl::InstallMissingPlugin() {
  NOTIMPLEMENTED();
}

bool WebPluginDelegateImpl::WindowedCreatePlugin() {
  NOTREACHED();
  return false;
}

void WebPluginDelegateImpl::WindowedDestroyWindow() {
  NOTREACHED();
}

bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
                                               const gfx::Rect& clip_rect) {
  NOTREACHED();
  return false;
}

void WebPluginDelegateImpl::WindowedSetWindow() {
  NOTREACHED();
}

void WebPluginDelegateImpl::WindowlessUpdateGeometry(
    const gfx::Rect& window_rect,
    const gfx::Rect& clip_rect) {
  bool old_clip_was_empty = clip_rect_.IsEmpty();
  cached_clip_rect_ = clip_rect;
  if (container_is_visible_)  // Remove check when cached_clip_rect_ is removed.
    clip_rect_ = clip_rect;
  bool new_clip_is_empty = clip_rect_.IsEmpty();

  // Only resend to the instance if the geometry has changed (see note in
  // WindowlessSetWindow for why we only care about the clip rect switching
  // empty state).
  if (window_rect == window_rect_ && old_clip_was_empty == new_clip_is_empty)
    return;

#ifndef NP_NO_CARBON
  // If visibility has changed, switch our idle event rate.
  if (instance()->event_model() == NPEventModelCarbon &&
      old_clip_was_empty != new_clip_is_empty) {
    UpdateIdleEventRate();
  }
#endif

  SetPluginRect(window_rect);
  WindowlessSetWindow(true);
}

void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
                                            const gfx::Rect& damage_rect) {
#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    // If we somehow get a paint before we've set up the plugin window, bail.
    if (!cg_context_.context)
      return;
    DCHECK(cg_context_.context == context);
  }
#endif

  static StatsRate plugin_paint("Plugin.Paint");
  StatsScope<StatsRate> scope(plugin_paint);

  // Plugin invalidates trigger asynchronous paints with the original
  // invalidation rect; the plugin may be resized before the paint is handled,
  // so we need to ensure that the damage rect is still sane.
  const gfx::Rect paint_rect(damage_rect.Intersect(
      gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));

  ScopedActiveDelegate active_delegate(this);

  switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
    case NPDrawingModelQuickDraw: {
      // Plugins using the QuickDraw drawing model do not restrict their
      // drawing to update events the way that CoreGraphics-based plugins
      // do.  When we are asked to paint, we therefore just copy from the
      // plugin's hidden window into our shared memory bitmap context.
      CGRect window_bounds = CGRectMake(0, 0,
                                        window_rect_.width(),
                                        window_rect_.height());
      CGWindowID window_id = HIWindowGetCGWindowID(
          reinterpret_cast<WindowRef>(cg_context_.window));
      CGContextSaveGState(context);
      CGContextTranslateCTM(context, 0, window_rect_.height());
      CGContextScaleCTM(context, 1.0, -1.0);
      CGContextCopyWindowCaptureContentsToRect(context, window_bounds,
                                               _CGSDefaultConnection(),
                                               window_id, 0);
      CGContextRestoreGState(context);
    }
#endif
    case NPDrawingModelCoreGraphics: {
      CGContextSaveGState(context);
      switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
        case NPEventModelCarbon: {
          NPEvent paint_event = { 0 };
          paint_event.what = updateEvt;
          paint_event.message = reinterpret_cast<uint32>(cg_context_.window);
          paint_event.when = TickCount();
          instance()->NPP_HandleEvent(&paint_event);
          break;
        }
#endif
        case NPEventModelCocoa: {
          NPCocoaEvent paint_event;
          memset(&paint_event, 0, sizeof(NPCocoaEvent));
          paint_event.type = NPCocoaEventDrawRect;
          paint_event.data.draw.context = context;
          paint_event.data.draw.x = paint_rect.x();
          paint_event.data.draw.y = paint_rect.y();
          paint_event.data.draw.width = paint_rect.width();
          paint_event.data.draw.height = paint_rect.height();
          instance()->NPP_HandleEvent(&paint_event);
          break;
        }
      }
      CGContextRestoreGState(context);
    }
  }
}

void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) {
  if (!instance())
    return;

  window_.x = 0;
  window_.y = 0;
  window_.height = window_rect_.height();
  window_.width = window_rect_.width();
  window_.clipRect.left = window_.x;
  window_.clipRect.top = window_.y;
  window_.clipRect.right = window_.clipRect.left;
  window_.clipRect.bottom = window_.clipRect.top;
  if (container_is_visible_ && !clip_rect_.IsEmpty()) {
    // We never tell plugins that they are only partially visible; because the
    // drawing target doesn't change size, the positioning of what plugins drew
    // would be wrong, as would any transforms they did on the context.
    window_.clipRect.right += window_.width;
    window_.clipRect.bottom += window_.height;
  }

  NPError err = instance()->NPP_SetWindow(&window_);

  // Send an appropriate window focus event after the first SetWindow.
  if (!have_called_set_window_) {
    have_called_set_window_ = true;
    SetWindowHasFocus(initial_window_focus_);
  }

  DCHECK(err == NPERR_NO_ERROR);
}

WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
  return g_active_delegate;
}

std::set<WebPluginDelegateImpl*> WebPluginDelegateImpl::GetActiveDelegates() {
  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  return *delegates;
}

void WebPluginDelegateImpl::FocusChanged(bool has_focus) {
  if (has_focus == have_focus_)
    return;
  have_focus_ = has_focus;

  ScopedActiveDelegate active_delegate(this);

  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent focus_event = { 0 };
      if (have_focus_)
        focus_event.what = NPEventType_GetFocusEvent;
      else
        focus_event.what = NPEventType_LoseFocusEvent;
      focus_event.when = TickCount();
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent focus_event;
      memset(&focus_event, 0, sizeof(focus_event));
      focus_event.type = NPCocoaEventFocusChanged;
      focus_event.data.focus.hasFocus = have_focus_;
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
  }
}

void WebPluginDelegateImpl::SetFocus() {
  if (focus_notifier_)
    focus_notifier_(this);
  else
    FocusChanged(true);
}

void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
  // If we get a window focus event before calling SetWindow, just remember the
  // states (WindowlessSetWindow will then send it on the first call).
  if (!have_called_set_window_) {
    initial_window_focus_ = has_focus;
    return;
  }

  if (has_focus == containing_window_has_focus_)
    return;
  containing_window_has_focus_ = has_focus;

  ScopedActiveDelegate active_delegate(this);
  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent focus_event = { 0 };
      focus_event.what = activateEvt;
      if (has_focus)
        focus_event.modifiers |= activeFlag;
      focus_event.message = reinterpret_cast<unsigned long>(cg_context_.window);
      focus_event.when = TickCount();
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent focus_event;
      memset(&focus_event, 0, sizeof(focus_event));
      focus_event.type = NPCocoaEventWindowFocusChanged;
      focus_event.data.focus.hasFocus = has_focus;
      instance()->NPP_HandleEvent(&focus_event);
    }
  }
}

void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
  if (is_visible == container_is_visible_)
    return;
  container_is_visible_ = is_visible;

  // TODO(stuartmorgan): This is a temporary workarond for
  // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
  // should all be removed.
  if (is_visible) {
    clip_rect_ = cached_clip_rect_;
  } else {
    clip_rect_.set_width(0);
    clip_rect_.set_height(0);
  }

  // TODO(stuartmorgan): We may need to remember whether we had focus, and
  // restore it ourselves when we become visible again. Revisit once SetFocus
  // is actually being called in all the cases it should be, at which point
  // we'll know whether or not that's handled for us by WebKit.
  if (!is_visible)
    FocusChanged(false);

  // If the plugin is changing visibility, let the plugin know. If it's scrolled
  // off screen (i.e., cached_clip_rect_ is empty), then container visibility
  // doesn't change anything.
  if (!cached_clip_rect_.IsEmpty()) {
#ifndef NP_NO_CARBON
    if (instance() && instance()->event_model() == NPEventModelCarbon)
      UpdateIdleEventRate();
#endif
    WindowlessSetWindow(true);
  }
}

void WebPluginDelegateImpl::WindowFrameChanged(gfx::Rect window_frame,
                                               gfx::Rect view_frame) {
  instance()->set_window_frame(window_frame);
  SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
}

void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
  current_windowless_cursor_.InitFromThemeCursor(cursor);
}

void WebPluginDelegateImpl::SetCursor(const Cursor* cursor) {
  current_windowless_cursor_.InitFromCursor(cursor);
}

void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
  current_windowless_cursor_.InitFromNSCursor(cursor);
}

void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
  window_rect_ = rect;
  PluginScreenLocationChanged();
}

void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
  content_area_origin_ = origin;
  PluginScreenLocationChanged();
}

void WebPluginDelegateImpl::PluginScreenLocationChanged() {
  gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
                           content_area_origin_.y() + window_rect_.y());
  instance()->set_plugin_origin(plugin_origin);

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    UpdateDummyWindowBounds(plugin_origin);
  }
#endif
}

#ifndef NP_NO_CARBON
void WebPluginDelegateImpl::UpdateDummyWindowBounds(
    const gfx::Point& plugin_origin) {
  WindowRef window = reinterpret_cast<WindowRef>(cg_context_.window);
  Rect current_bounds;
  GetWindowBounds(window, kWindowContentRgn, &current_bounds);

  Rect new_bounds;
  // We never want to resize the window to 0x0, so if the plugin is 0x0 just
  // move the window without resizing it.
  if (window_rect_.width() > 0 && window_rect_.height() > 0) {
    SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
    OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
  } else {
    new_bounds = current_bounds;
    OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
               plugin_origin.y() - current_bounds.top);
  }

  if (new_bounds.left != current_bounds.left ||
      new_bounds.top != current_bounds.top ||
      new_bounds.right != current_bounds.right ||
      new_bounds.bottom != current_bounds.bottom)
    SetWindowBounds(window, kWindowContentRgn, &new_bounds);
}

void WebPluginDelegateImpl::UpdateIdleEventRate() {
  bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
  CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
                                                            plugin_visible);
}
#endif  // !NP_NO_CARBON

static bool WebInputEventIsWebMouseEvent(const WebInputEvent& event) {
  switch (event.type) {
    case WebInputEvent::MouseMove:
    case WebInputEvent::MouseLeave:
    case WebInputEvent::MouseEnter:
    case WebInputEvent::MouseDown:
    case WebInputEvent::MouseUp:
      if (event.size < sizeof(WebMouseEvent)) {
        NOTREACHED();
        return false;
      }
      return true;
    default:
      return false;
  }
}

static bool WebInputEventIsWebKeyboardEvent(const WebInputEvent& event) {
  switch (event.type) {
    case WebInputEvent::KeyDown:
    case WebInputEvent::KeyUp:
      if (event.size < sizeof(WebKeyboardEvent)) {
        NOTREACHED();
        return false;
      }
      return true;
    default:
      return false;
  }
}

#ifndef NP_NO_CARBON
static NSInteger CarbonModifiersFromWebEvent(const WebInputEvent& event) {
  NSInteger modifiers = 0;
  if (event.modifiers & WebInputEvent::ControlKey)
    modifiers |= controlKey;
  if (event.modifiers & WebInputEvent::ShiftKey)
    modifiers |= shiftKey;
  if (event.modifiers & WebInputEvent::AltKey)
    modifiers |= optionKey;
  if (event.modifiers & WebInputEvent::MetaKey)
    modifiers |= cmdKey;
  return modifiers;
}

static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
                                     NPEvent *np_event) {
  np_event->where.h = event.globalX;
  np_event->where.v = event.globalY;

  np_event->modifiers |= CarbonModifiersFromWebEvent(event);

  // default to "button up"; override this for mouse down events below.
  np_event->modifiers |= btnState;

  switch (event.button) {
    case WebMouseEvent::ButtonLeft:
      break;
    case WebMouseEvent::ButtonMiddle:
      np_event->modifiers |= cmdKey;
      break;
    case WebMouseEvent::ButtonRight:
      np_event->modifiers |= controlKey;
      break;
    default:
      NOTIMPLEMENTED();
  }
  switch (event.type) {
    case WebInputEvent::MouseMove:
      np_event->what = nullEvent;
      return true;
    case WebInputEvent::MouseLeave:
    case WebInputEvent::MouseEnter:
      np_event->what = NPEventType_AdjustCursorEvent;
      return true;
    case WebInputEvent::MouseDown:
      np_event->modifiers &= ~btnState;
      np_event->what = mouseDown;
      return true;
    case WebInputEvent::MouseUp:
      np_event->what = mouseUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
                                        NPEvent *np_event) {
  // TODO: figure out how to handle Unicode input to plugins, if that's
  // even possible in the NPAPI Carbon event model.
  np_event->message = (event.nativeKeyCode << 8) & keyCodeMask;
  np_event->message |= event.text[0] & charCodeMask;
  np_event->modifiers |= btnState;
  np_event->modifiers |= CarbonModifiersFromWebEvent(event);

  switch (event.type) {
    case WebInputEvent::KeyDown:
      if (event.modifiers & WebInputEvent::IsAutoRepeat)
        np_event->what = autoKey;
      else
        np_event->what = keyDown;
      return true;
    case WebInputEvent::KeyUp:
      np_event->what = keyUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPEventFromWebInputEvent(const WebInputEvent& event,
                                     NPEvent* np_event) {
  np_event->when = TickCount();
  if (WebInputEventIsWebMouseEvent(event)) {
    return NPEventFromWebMouseEvent(*static_cast<const WebMouseEvent*>(&event),
                                    np_event);
  } else if (WebInputEventIsWebKeyboardEvent(event)) {
    return NPEventFromWebKeyboardEvent(
        *static_cast<const WebKeyboardEvent*>(&event), np_event);
  }
  DLOG(WARNING) << "unknown event type" << event.type;
  return false;
}
#endif  // !NP_NO_CARBON

static NSInteger CocoaModifiersFromWebEvent(const WebInputEvent& event) {
  NSInteger modifiers = 0;
  if (event.modifiers & WebInputEvent::ControlKey)
    modifiers |= NSControlKeyMask;
  if (event.modifiers & WebInputEvent::ShiftKey)
    modifiers |= NSShiftKeyMask;
  if (event.modifiers & WebInputEvent::AltKey)
    modifiers |= NSAlternateKeyMask;
  if (event.modifiers & WebInputEvent::MetaKey)
    modifiers |= NSCommandKeyMask;
  return modifiers;
}

static bool KeyIsModifier(int native_key_code) {
  switch (native_key_code) {
    case 55:  // Left command
    case 54:  // Right command
    case 58:  // Left option
    case 61:  // Right option
    case 59:  // Left control
    case 62:  // Right control
    case 56:  // Left shift
    case 60:  // Right shift
    case 57:  // Caps lock
      return true;
    default:
      return false;
  }
}

static bool NPCocoaEventFromWebMouseEvent(const WebMouseEvent& event,
                                          NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->data.mouse.pluginX = event.x;
  np_cocoa_event->data.mouse.pluginY = event.y;
  np_cocoa_event->data.mouse.modifierFlags |= CocoaModifiersFromWebEvent(event);
  np_cocoa_event->data.mouse.clickCount = event.clickCount;
  switch (event.button) {
    case WebMouseEvent::ButtonLeft:
      np_cocoa_event->data.mouse.buttonNumber = 0;
      break;
    case WebMouseEvent::ButtonMiddle:
      np_cocoa_event->data.mouse.buttonNumber = 2;
      break;
    case WebMouseEvent::ButtonRight:
      np_cocoa_event->data.mouse.buttonNumber = 1;
      break;
    default:
      np_cocoa_event->data.mouse.buttonNumber = event.button;
      break;
  }
  switch (event.type) {
    case WebInputEvent::MouseDown:
      np_cocoa_event->type = NPCocoaEventMouseDown;
      return true;
    case WebInputEvent::MouseUp:
      np_cocoa_event->type = NPCocoaEventMouseUp;
      return true;
    case WebInputEvent::MouseMove: {
      bool mouse_is_down = (event.modifiers & WebInputEvent::LeftButtonDown) ||
                           (event.modifiers & WebInputEvent::RightButtonDown) ||
                           (event.modifiers & WebInputEvent::MiddleButtonDown);
      np_cocoa_event->type = mouse_is_down ? NPCocoaEventMouseDragged
                                           : NPCocoaEventMouseMoved;
      return true;
    }
    case WebInputEvent::MouseEnter:
      np_cocoa_event->type = NPCocoaEventMouseEntered;
      return true;
    case WebInputEvent::MouseLeave:
      np_cocoa_event->type = NPCocoaEventMouseExited;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPCocoaEventFromWebMouseWheelEvent(const WebMouseWheelEvent& event,
                                               NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->type = NPCocoaEventScrollWheel;
  np_cocoa_event->data.mouse.pluginX = event.x;
  np_cocoa_event->data.mouse.pluginY = event.y;
  np_cocoa_event->data.mouse.modifierFlags |= CocoaModifiersFromWebEvent(event);
  np_cocoa_event->data.mouse.deltaX = event.deltaX;
  np_cocoa_event->data.mouse.deltaY = event.deltaY;
  return true;
}

static bool NPCocoaEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
                                             NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->data.key.keyCode = event.nativeKeyCode;

  np_cocoa_event->data.key.modifierFlags |= CocoaModifiersFromWebEvent(event);

  // Modifier keys have their own event type, and don't get character or
  // repeat data.
  if (KeyIsModifier(event.nativeKeyCode)) {
    np_cocoa_event->type = NPCocoaEventFlagsChanged;
    return true;
  }

  np_cocoa_event->data.key.characters = reinterpret_cast<NPNSString*>(
      [NSString stringWithFormat:@"%S", event.text]);
  np_cocoa_event->data.key.charactersIgnoringModifiers =
      reinterpret_cast<NPNSString*>(
          [NSString stringWithFormat:@"%S", event.unmodifiedText]);

  if (event.modifiers & WebInputEvent::IsAutoRepeat)
    np_cocoa_event->data.key.isARepeat = true;

  switch (event.type) {
    case WebInputEvent::KeyDown:
      np_cocoa_event->type = NPCocoaEventKeyDown;
      return true;
    case WebInputEvent::KeyUp:
      np_cocoa_event->type = NPCocoaEventKeyUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPCocoaEventFromWebInputEvent(const WebInputEvent& event,
                                          NPCocoaEvent *np_cocoa_event) {
  memset(np_cocoa_event, 0, sizeof(NPCocoaEvent));
  if (event.type == WebInputEvent::MouseWheel) {
    return NPCocoaEventFromWebMouseWheelEvent(
        *static_cast<const WebMouseWheelEvent*>(&event), np_cocoa_event);
  } else if (WebInputEventIsWebMouseEvent(event)) {
    return NPCocoaEventFromWebMouseEvent(
        *static_cast<const WebMouseEvent*>(&event), np_cocoa_event);
  } else if (WebInputEventIsWebKeyboardEvent(event)) {
    return NPCocoaEventFromWebKeyboardEvent(
        *static_cast<const WebKeyboardEvent*>(&event), np_cocoa_event);
  }
  DLOG(WARNING) << "unknown event type " << event.type;
  return false;
}

bool WebPluginDelegateImpl::PlatformHandleInputEvent(
    const WebInputEvent& event, WebCursorInfo* cursor_info) {
  DCHECK(cursor_info != NULL);

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon &&
      !cg_context_.context) {
    // If we somehow get an event before we've set up the plugin window, bail.
    return false;
  }
#endif

  if (WebInputEventIsWebMouseEvent(event)) {
    // Check our plugin location before we send the event to the plugin, just
    // in case we somehow missed a plugin frame change.
    const WebMouseEvent* mouse_event =
        static_cast<const WebMouseEvent*>(&event);
    gfx::Point content_origin(
        mouse_event->globalX - mouse_event->x - window_rect_.x(),
        mouse_event->globalY - mouse_event->y - window_rect_.y());
    if (content_origin.x() != content_area_origin_.x() ||
        content_origin.y() != content_area_origin_.y()) {
      DLOG(WARNING) << "Stale plugin location: " << content_area_origin_
                    << " instead of " << content_origin;
      SetContentAreaOrigin(content_origin);
    }

    current_windowless_cursor_.GetCursorInfo(cursor_info);
  }

  // if we do not currently have focus and this is a mouseDown, trigger a
  // notification that we are taking the keyboard focus.  We can't just key
  // off of incoming calls to SetFocus, since WebKit may already think we
  // have it if we were the most recently focused element on our parent tab.
  if (event.type == WebInputEvent::MouseDown && !have_focus_) {
    SetFocus();
    // Make sure that the plugin is still there after handling the focus event.
    if (!instance())
      return false;
  }

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    if (event.type == WebInputEvent::MouseMove) {
      return true;  // The recurring OnNull will send null events.
    }

    switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
      case NPDrawingModelQuickDraw:
        SetPort(qd_port_.port);
        break;
#endif
      case NPDrawingModelCoreGraphics:
        CGContextSaveGState(cg_context_.context);
        break;
    }
  }
#endif

  ScopedActiveDelegate active_delegate(this);

#ifndef NP_NO_CARBON
  // cgcontext_.context can change during event handling (because of a geometry
  // change triggered by the event); we need to know if that happens so we
  // don't keep trying to use the context. It is not an owning ref, so shouldn't
  // be used for anything but pointer comparison.
  CGContextRef old_context_weak = cg_context_.context;
#endif

  // Create the plugin event structure, and send it to the plugin.
  bool ret = false;
  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent np_event = {0};
      if (!NPEventFromWebInputEvent(event, &np_event)) {
        LOG(WARNING) << "NPEventFromWebInputEvent failed";
        return false;
      }
      ret = instance()->NPP_HandleEvent(&np_event) != 0;
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent np_cocoa_event;
      if (!NPCocoaEventFromWebInputEvent(event, &np_cocoa_event)) {
        LOG(WARNING) << "NPCocoaEventFromWebInputEvent failed";
        return false;
      }
      NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &np_cocoa_event);
      ret = instance()->NPP_HandleEvent(&np_cocoa_event) != 0;
      break;
    }
  }

  if (WebInputEventIsWebMouseEvent(event)) {
    // Plugins are not good about giving accurate information about whether or
    // not they handled events, and other browsers on the Mac generally ignore
    // the return value. We may need to expand this to other input types, but
    // we'll need to be careful about things like Command-keys.
    ret = true;
  }

#ifndef NP_NO_CARBON
  if (instance() && instance()->event_model() == NPEventModelCarbon &&
      instance()->drawing_model() == NPDrawingModelCoreGraphics &&
      cg_context_.context == old_context_weak)
    CGContextRestoreGState(cg_context_.context);
#endif

  return ret;
}

#ifndef NP_NO_CARBON
void WebPluginDelegateImpl::FireIdleEvent() {
  // Avoid a race condition between IO and UI threads during plugin shutdown
  if (!instance())
    return;

  ScopedActiveDelegate active_delegate(this);

  if (!webkit_glue::IsPluginRunningInRendererProcess()) {
    switch (instance()->event_model()) {
      case NPEventModelCarbon:
        // If the plugin is running in a subprocess, drain any pending system
        // events so that the plugin's event handlers will get called on any
        // windows it has created.  Filter out activate/deactivate events on
        // the fake browser window, but pass everything else through.
        EventRecord event;
        while (GetNextEvent(everyEvent, &event)) {
          if (event.what == activateEvt && cg_context_.window &&
              reinterpret_cast<void *>(event.message) != cg_context_.window)
            continue;
          instance()->NPP_HandleEvent(&event);
          // If the plugin went away during event handling, we're done.
          if (!instance())
            return;
        }
        break;
    }
  }

  if (instance()->event_model() == NPEventModelCarbon) {
    // Send an idle event so that the plugin can do background work
    NPEvent np_event = {0};
    np_event.what = nullEvent;
    np_event.when = TickCount();
    np_event.modifiers = GetCurrentKeyModifiers();
    if (!Button())
      np_event.modifiers |= btnState;
    HIPoint mouse_location;
    HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
    np_event.where.h = mouse_location.x;
    np_event.where.v = mouse_location.y;
    instance()->NPP_HandleEvent(&np_event);
  }

#ifndef NP_NO_QUICKDRAW
  // Quickdraw-based plugins can draw at any time, so tell the renderer to
  // repaint.
  // TODO: only do this if the contents of the offscreen window has changed,
  // so as not to spam the renderer with an unchanging image.
  if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
    instance()->webplugin()->Invalidate();
#endif
}
#endif  // !NP_NO_CARBON
<commit_msg>Check for Carbon Mac plugin destruction after each GetNextEvent cycle<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.

#import <Cocoa/Cocoa.h>

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

#include <string>
#include <unistd.h>
#include <set>

#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/stats_counters.h"
#include "base/string_util.h"
#include "base/timer.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "webkit/default_plugin/plugin_impl.h"
#include "webkit/glue/webplugin.h"
#include "webkit/glue/plugins/coregraphics_private_symbols_mac.h"
#include "webkit/glue/plugins/fake_plugin_window_tracker_mac.h"
#include "webkit/glue/plugins/plugin_constants_win.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 "webkit/glue/webkit_glue.h"

// If we're compiling support for the QuickDraw drawing model, turn off GCC
// warnings about deprecated functions (since QuickDraw is a deprecated API).
// According to the GCC documentation, this can only be done per file, not
// pushed and popped like some options can be.
#ifndef NP_NO_QUICKDRAW
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

using webkit_glue::WebPlugin;
using webkit_glue::WebPluginDelegate;
using webkit_glue::WebPluginResourceClient;
using WebKit::WebCursorInfo;
using WebKit::WebKeyboardEvent;
using WebKit::WebInputEvent;
using WebKit::WebMouseEvent;
using WebKit::WebMouseWheelEvent;

// Important implementation notes: The Mac definition of NPAPI, particularly
// the distinction between windowed and windowless modes, differs from the
// Windows and Linux definitions.  Most of those differences are
// accomodated by the WebPluginDelegate class.

namespace {

base::LazyInstance<std::set<WebPluginDelegateImpl*> > g_active_delegates(
    base::LINKER_INITIALIZED);

WebPluginDelegateImpl* g_active_delegate;

// Helper to simplify correct usage of g_active_delegate.  Instantiating will
// set the active delegate to |delegate| for the lifetime of the object, then
// NULL when it goes out of scope.
class ScopedActiveDelegate {
public:
  explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
    g_active_delegate = delegate;
  }
  ~ScopedActiveDelegate() {
    g_active_delegate = NULL;
  }
private:
  DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
};

#ifndef NP_NO_CARBON
// Timer periods for sending idle events to Carbon plugins. The visible value
// (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
// Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
// going lower than that causes issues.
const int kVisibleIdlePeriodMs = 20;     // (50Hz)
const int kHiddenIdlePeriodMs = 125;  // (8Hz)

class CarbonIdleEventSource {
 public:
  // Returns the shared Carbon idle event source.
  static CarbonIdleEventSource* SharedInstance() {
    DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
    static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
    return event_source;
  }

  // Registers the plugin delegate as interested in receiving idle events at
  // a rate appropriate for the given visibility. A delegate can safely be
  // re-registered any number of times, with the latest registration winning.
  void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
    if (visible) {
      visible_delegates_->RegisterDelegate(delegate);
      hidden_delegates_->UnregisterDelegate(delegate);
    } else {
      hidden_delegates_->RegisterDelegate(delegate);
      visible_delegates_->UnregisterDelegate(delegate);
    }
  }

  // Removes the plugin delegate from the list of plugins receiving idle events.
  void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
    visible_delegates_->UnregisterDelegate(delegate);
    hidden_delegates_->UnregisterDelegate(delegate);
  }

 private:
  class VisibilityGroup {
   public:
    explicit VisibilityGroup(int timer_period)
        : timer_period_(timer_period), iterator_(delegates_.end()) {}

    // Adds |delegate| to this visibility group.
    void RegisterDelegate(WebPluginDelegateImpl* delegate) {
      if (delegates_.empty()) {
        timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
                     this, &VisibilityGroup::SendIdleEvents);
      }
      delegates_.insert(delegate);
    }

    // Removes |delegate| from this visibility group.
    void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
      // If a plugin changes visibility during idle event handling, it
      // may be removed from this set while SendIdleEvents is still iterating;
      // if that happens and it's next on the list, increment the iterator
      // before erasing so that the iteration won't be corrupted.
      if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
        ++iterator_;
      size_t removed = delegates_.erase(delegate);
      if (removed > 0 && delegates_.empty())
        timer_.Stop();
    }

   private:
    // Fires off idle events for each delegate in the group.
    void SendIdleEvents() {
      for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
        // Pre-increment so that the skip logic in UnregisterDelegates works.
        WebPluginDelegateImpl* delegate = *(iterator_++);
        delegate->FireIdleEvent();
      }
    }

    int timer_period_;
    base::RepeatingTimer<VisibilityGroup> timer_;
    std::set<WebPluginDelegateImpl*> delegates_;
    std::set<WebPluginDelegateImpl*>::iterator iterator_;
  };

  CarbonIdleEventSource()
      : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
        hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}

  scoped_ptr<VisibilityGroup> visible_delegates_;
  scoped_ptr<VisibilityGroup> hidden_delegates_;

  DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
};
#endif  // !NP_NO_CARBON

}  // namespace

WebPluginDelegateImpl::WebPluginDelegateImpl(
    gfx::PluginWindowHandle containing_view,
    NPAPI::PluginInstance *instance)
    : windowless_needs_set_window_(true),
      // all Mac plugins are "windowless" in the Windows/X11 sense
      windowless_(true),
      plugin_(NULL),
      instance_(instance),
      parent_(containing_view),
      quirks_(0),
      have_focus_(false),
      focus_notifier_(NULL),
      containing_window_has_focus_(false),
      initial_window_focus_(false),
      container_is_visible_(false),
      have_called_set_window_(false),
      handle_event_depth_(0) {
  memset(&window_, 0, sizeof(window_));
#ifndef NP_NO_CARBON
  memset(&cg_context_, 0, sizeof(cg_context_));
#endif
#ifndef NP_NO_QUICKDRAW
  memset(&qd_port_, 0, sizeof(qd_port_));
#endif
  instance->set_windowless(true);

  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  delegates->insert(this);
}

WebPluginDelegateImpl::~WebPluginDelegateImpl() {
  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  delegates->erase(this);

  DestroyInstance();

#ifndef NP_NO_CARBON
  if (cg_context_.window) {
    FakePluginWindowTracker::SharedInstance()->RemoveFakeWindowForDelegate(
        this, reinterpret_cast<WindowRef>(cg_context_.window));
  }
#endif
}

void WebPluginDelegateImpl::PlatformInitialize() {
  // Don't set a NULL window handle on destroy for Mac plugins.  This matches
  // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
  // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
  // destroyPlugin in WebNetscapePluginView.mm, for examples).
  quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;

  // Some plugins don't always unload cleanly, so don't unload them at shutdown.
  if (instance()->mime_type().find("x-silverlight") != std::string::npos ||
      instance()->mime_type().find("audio/x-pn-realaudio") != std::string::npos)
    instance()->plugin_lib()->PreventLibraryUnload();

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    // Create a stand-in for the browser window so that the plugin will have
    // a non-NULL WindowRef to which it can refer.
    FakePluginWindowTracker* window_tracker =
        FakePluginWindowTracker::SharedInstance();
    cg_context_.window = window_tracker->GenerateFakeWindowForDelegate(this);
    cg_context_.context = NULL;
    Rect window_bounds = { 0, 0, window_rect_.height(), window_rect_.width() };
    SetWindowBounds(reinterpret_cast<WindowRef>(cg_context_.window),
                    kWindowContentRgn, &window_bounds);
#ifndef NP_NO_QUICKDRAW
    qd_port_.port =
        GetWindowPort(reinterpret_cast<WindowRef>(cg_context_.window));
#endif
  }
#endif

  switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
    case NPDrawingModelQuickDraw:
      window_.window = &qd_port_;
      window_.type = NPWindowTypeDrawable;
      break;
#endif
    case NPDrawingModelCoreGraphics:
#ifndef NP_NO_CARBON
      if (instance()->event_model() == NPEventModelCarbon)
        window_.window = &cg_context_;
#endif
      window_.type = NPWindowTypeDrawable;
      break;
    default:
      NOTREACHED();
      break;
  }

  // TODO(stuartmorgan): We need real plugin container visibility information
  // when the plugin is initialized; for now, assume it's visible.
  // None of the calls SetContainerVisibility would make are useful at this
  // point, so we just set the initial state directly.
  container_is_visible_ = true;

#ifndef NP_NO_CARBON
  // If the plugin wants Carbon events, hook up to the source of idle events.
  if (instance()->event_model() == NPEventModelCarbon)
    UpdateIdleEventRate();
#endif
  plugin_->SetWindow(NULL);

  // QuickTime can crash if it gets other calls (e.g., NPP_Write) before it
  // gets a SetWindow call, so call SetWindow (with a 0x0 rect) immediately.
  const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
  if (plugin_info.name.find(L"QuickTime") != std::wstring::npos)
    WindowlessSetWindow(true);
}

void WebPluginDelegateImpl::PlatformDestroyInstance() {
#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon)
    CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
#endif
}

void WebPluginDelegateImpl::UpdateContext(CGContextRef context) {
#ifndef NP_NO_CARBON
  // Flash on the Mac apparently caches the context from the struct it receives
  // in NPP_SetWindow, and continues to use it even when the contents of the
  // struct have changed, so we need to call NPP_SetWindow again if the context
  // changes.
  if (instance()->event_model() == NPEventModelCarbon &&
      context != cg_context_.context) {
    cg_context_.context = context;
    WindowlessSetWindow(true);
  }
#endif
}

void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
  DCHECK(windowless_);
  WindowlessPaint(context, rect);
}

void WebPluginDelegateImpl::Print(CGContextRef context) {
  // Disabling the call to NPP_Print as it causes a crash in
  // flash in some cases. In any case this does not work as expected
  // as the EMF meta file dc passed in needs to be created with the
  // the plugin window dc as its sibling dc and the window rect
  // in .01 mm units.
}

void WebPluginDelegateImpl::InstallMissingPlugin() {
  NOTIMPLEMENTED();
}

bool WebPluginDelegateImpl::WindowedCreatePlugin() {
  NOTREACHED();
  return false;
}

void WebPluginDelegateImpl::WindowedDestroyWindow() {
  NOTREACHED();
}

bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
                                               const gfx::Rect& clip_rect) {
  NOTREACHED();
  return false;
}

void WebPluginDelegateImpl::WindowedSetWindow() {
  NOTREACHED();
}

void WebPluginDelegateImpl::WindowlessUpdateGeometry(
    const gfx::Rect& window_rect,
    const gfx::Rect& clip_rect) {
  bool old_clip_was_empty = clip_rect_.IsEmpty();
  cached_clip_rect_ = clip_rect;
  if (container_is_visible_)  // Remove check when cached_clip_rect_ is removed.
    clip_rect_ = clip_rect;
  bool new_clip_is_empty = clip_rect_.IsEmpty();

  // Only resend to the instance if the geometry has changed (see note in
  // WindowlessSetWindow for why we only care about the clip rect switching
  // empty state).
  if (window_rect == window_rect_ && old_clip_was_empty == new_clip_is_empty)
    return;

#ifndef NP_NO_CARBON
  // If visibility has changed, switch our idle event rate.
  if (instance()->event_model() == NPEventModelCarbon &&
      old_clip_was_empty != new_clip_is_empty) {
    UpdateIdleEventRate();
  }
#endif

  SetPluginRect(window_rect);
  WindowlessSetWindow(true);
}

void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
                                            const gfx::Rect& damage_rect) {
#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    // If we somehow get a paint before we've set up the plugin window, bail.
    if (!cg_context_.context)
      return;
    DCHECK(cg_context_.context == context);
  }
#endif

  static StatsRate plugin_paint("Plugin.Paint");
  StatsScope<StatsRate> scope(plugin_paint);

  // Plugin invalidates trigger asynchronous paints with the original
  // invalidation rect; the plugin may be resized before the paint is handled,
  // so we need to ensure that the damage rect is still sane.
  const gfx::Rect paint_rect(damage_rect.Intersect(
      gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));

  ScopedActiveDelegate active_delegate(this);

  switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
    case NPDrawingModelQuickDraw: {
      // Plugins using the QuickDraw drawing model do not restrict their
      // drawing to update events the way that CoreGraphics-based plugins
      // do.  When we are asked to paint, we therefore just copy from the
      // plugin's hidden window into our shared memory bitmap context.
      CGRect window_bounds = CGRectMake(0, 0,
                                        window_rect_.width(),
                                        window_rect_.height());
      CGWindowID window_id = HIWindowGetCGWindowID(
          reinterpret_cast<WindowRef>(cg_context_.window));
      CGContextSaveGState(context);
      CGContextTranslateCTM(context, 0, window_rect_.height());
      CGContextScaleCTM(context, 1.0, -1.0);
      CGContextCopyWindowCaptureContentsToRect(context, window_bounds,
                                               _CGSDefaultConnection(),
                                               window_id, 0);
      CGContextRestoreGState(context);
    }
#endif
    case NPDrawingModelCoreGraphics: {
      CGContextSaveGState(context);
      switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
        case NPEventModelCarbon: {
          NPEvent paint_event = { 0 };
          paint_event.what = updateEvt;
          paint_event.message = reinterpret_cast<uint32>(cg_context_.window);
          paint_event.when = TickCount();
          instance()->NPP_HandleEvent(&paint_event);
          break;
        }
#endif
        case NPEventModelCocoa: {
          NPCocoaEvent paint_event;
          memset(&paint_event, 0, sizeof(NPCocoaEvent));
          paint_event.type = NPCocoaEventDrawRect;
          paint_event.data.draw.context = context;
          paint_event.data.draw.x = paint_rect.x();
          paint_event.data.draw.y = paint_rect.y();
          paint_event.data.draw.width = paint_rect.width();
          paint_event.data.draw.height = paint_rect.height();
          instance()->NPP_HandleEvent(&paint_event);
          break;
        }
      }
      CGContextRestoreGState(context);
    }
  }
}

void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) {
  if (!instance())
    return;

  window_.x = 0;
  window_.y = 0;
  window_.height = window_rect_.height();
  window_.width = window_rect_.width();
  window_.clipRect.left = window_.x;
  window_.clipRect.top = window_.y;
  window_.clipRect.right = window_.clipRect.left;
  window_.clipRect.bottom = window_.clipRect.top;
  if (container_is_visible_ && !clip_rect_.IsEmpty()) {
    // We never tell plugins that they are only partially visible; because the
    // drawing target doesn't change size, the positioning of what plugins drew
    // would be wrong, as would any transforms they did on the context.
    window_.clipRect.right += window_.width;
    window_.clipRect.bottom += window_.height;
  }

  NPError err = instance()->NPP_SetWindow(&window_);

  // Send an appropriate window focus event after the first SetWindow.
  if (!have_called_set_window_) {
    have_called_set_window_ = true;
    SetWindowHasFocus(initial_window_focus_);
  }

  DCHECK(err == NPERR_NO_ERROR);
}

WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
  return g_active_delegate;
}

std::set<WebPluginDelegateImpl*> WebPluginDelegateImpl::GetActiveDelegates() {
  std::set<WebPluginDelegateImpl*>* delegates = g_active_delegates.Pointer();
  return *delegates;
}

void WebPluginDelegateImpl::FocusChanged(bool has_focus) {
  if (has_focus == have_focus_)
    return;
  have_focus_ = has_focus;

  ScopedActiveDelegate active_delegate(this);

  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent focus_event = { 0 };
      if (have_focus_)
        focus_event.what = NPEventType_GetFocusEvent;
      else
        focus_event.what = NPEventType_LoseFocusEvent;
      focus_event.when = TickCount();
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent focus_event;
      memset(&focus_event, 0, sizeof(focus_event));
      focus_event.type = NPCocoaEventFocusChanged;
      focus_event.data.focus.hasFocus = have_focus_;
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
  }
}

void WebPluginDelegateImpl::SetFocus() {
  if (focus_notifier_)
    focus_notifier_(this);
  else
    FocusChanged(true);
}

void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
  // If we get a window focus event before calling SetWindow, just remember the
  // states (WindowlessSetWindow will then send it on the first call).
  if (!have_called_set_window_) {
    initial_window_focus_ = has_focus;
    return;
  }

  if (has_focus == containing_window_has_focus_)
    return;
  containing_window_has_focus_ = has_focus;

  ScopedActiveDelegate active_delegate(this);
  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent focus_event = { 0 };
      focus_event.what = activateEvt;
      if (has_focus)
        focus_event.modifiers |= activeFlag;
      focus_event.message = reinterpret_cast<unsigned long>(cg_context_.window);
      focus_event.when = TickCount();
      instance()->NPP_HandleEvent(&focus_event);
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent focus_event;
      memset(&focus_event, 0, sizeof(focus_event));
      focus_event.type = NPCocoaEventWindowFocusChanged;
      focus_event.data.focus.hasFocus = has_focus;
      instance()->NPP_HandleEvent(&focus_event);
    }
  }
}

void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
  if (is_visible == container_is_visible_)
    return;
  container_is_visible_ = is_visible;

  // TODO(stuartmorgan): This is a temporary workarond for
  // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
  // should all be removed.
  if (is_visible) {
    clip_rect_ = cached_clip_rect_;
  } else {
    clip_rect_.set_width(0);
    clip_rect_.set_height(0);
  }

  // TODO(stuartmorgan): We may need to remember whether we had focus, and
  // restore it ourselves when we become visible again. Revisit once SetFocus
  // is actually being called in all the cases it should be, at which point
  // we'll know whether or not that's handled for us by WebKit.
  if (!is_visible)
    FocusChanged(false);

  // If the plugin is changing visibility, let the plugin know. If it's scrolled
  // off screen (i.e., cached_clip_rect_ is empty), then container visibility
  // doesn't change anything.
  if (!cached_clip_rect_.IsEmpty()) {
#ifndef NP_NO_CARBON
    if (instance() && instance()->event_model() == NPEventModelCarbon)
      UpdateIdleEventRate();
#endif
    WindowlessSetWindow(true);
  }
}

void WebPluginDelegateImpl::WindowFrameChanged(gfx::Rect window_frame,
                                               gfx::Rect view_frame) {
  instance()->set_window_frame(window_frame);
  SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
}

void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
  current_windowless_cursor_.InitFromThemeCursor(cursor);
}

void WebPluginDelegateImpl::SetCursor(const Cursor* cursor) {
  current_windowless_cursor_.InitFromCursor(cursor);
}

void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
  current_windowless_cursor_.InitFromNSCursor(cursor);
}

void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
  window_rect_ = rect;
  PluginScreenLocationChanged();
}

void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
  content_area_origin_ = origin;
  PluginScreenLocationChanged();
}

void WebPluginDelegateImpl::PluginScreenLocationChanged() {
  gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
                           content_area_origin_.y() + window_rect_.y());
  instance()->set_plugin_origin(plugin_origin);

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    UpdateDummyWindowBounds(plugin_origin);
  }
#endif
}

#ifndef NP_NO_CARBON
void WebPluginDelegateImpl::UpdateDummyWindowBounds(
    const gfx::Point& plugin_origin) {
  WindowRef window = reinterpret_cast<WindowRef>(cg_context_.window);
  Rect current_bounds;
  GetWindowBounds(window, kWindowContentRgn, &current_bounds);

  Rect new_bounds;
  // We never want to resize the window to 0x0, so if the plugin is 0x0 just
  // move the window without resizing it.
  if (window_rect_.width() > 0 && window_rect_.height() > 0) {
    SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
    OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
  } else {
    new_bounds = current_bounds;
    OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
               plugin_origin.y() - current_bounds.top);
  }

  if (new_bounds.left != current_bounds.left ||
      new_bounds.top != current_bounds.top ||
      new_bounds.right != current_bounds.right ||
      new_bounds.bottom != current_bounds.bottom)
    SetWindowBounds(window, kWindowContentRgn, &new_bounds);
}

void WebPluginDelegateImpl::UpdateIdleEventRate() {
  bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
  CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
                                                            plugin_visible);
}
#endif  // !NP_NO_CARBON

static bool WebInputEventIsWebMouseEvent(const WebInputEvent& event) {
  switch (event.type) {
    case WebInputEvent::MouseMove:
    case WebInputEvent::MouseLeave:
    case WebInputEvent::MouseEnter:
    case WebInputEvent::MouseDown:
    case WebInputEvent::MouseUp:
      if (event.size < sizeof(WebMouseEvent)) {
        NOTREACHED();
        return false;
      }
      return true;
    default:
      return false;
  }
}

static bool WebInputEventIsWebKeyboardEvent(const WebInputEvent& event) {
  switch (event.type) {
    case WebInputEvent::KeyDown:
    case WebInputEvent::KeyUp:
      if (event.size < sizeof(WebKeyboardEvent)) {
        NOTREACHED();
        return false;
      }
      return true;
    default:
      return false;
  }
}

#ifndef NP_NO_CARBON
static NSInteger CarbonModifiersFromWebEvent(const WebInputEvent& event) {
  NSInteger modifiers = 0;
  if (event.modifiers & WebInputEvent::ControlKey)
    modifiers |= controlKey;
  if (event.modifiers & WebInputEvent::ShiftKey)
    modifiers |= shiftKey;
  if (event.modifiers & WebInputEvent::AltKey)
    modifiers |= optionKey;
  if (event.modifiers & WebInputEvent::MetaKey)
    modifiers |= cmdKey;
  return modifiers;
}

static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
                                     NPEvent *np_event) {
  np_event->where.h = event.globalX;
  np_event->where.v = event.globalY;

  np_event->modifiers |= CarbonModifiersFromWebEvent(event);

  // default to "button up"; override this for mouse down events below.
  np_event->modifiers |= btnState;

  switch (event.button) {
    case WebMouseEvent::ButtonLeft:
      break;
    case WebMouseEvent::ButtonMiddle:
      np_event->modifiers |= cmdKey;
      break;
    case WebMouseEvent::ButtonRight:
      np_event->modifiers |= controlKey;
      break;
    default:
      NOTIMPLEMENTED();
  }
  switch (event.type) {
    case WebInputEvent::MouseMove:
      np_event->what = nullEvent;
      return true;
    case WebInputEvent::MouseLeave:
    case WebInputEvent::MouseEnter:
      np_event->what = NPEventType_AdjustCursorEvent;
      return true;
    case WebInputEvent::MouseDown:
      np_event->modifiers &= ~btnState;
      np_event->what = mouseDown;
      return true;
    case WebInputEvent::MouseUp:
      np_event->what = mouseUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
                                        NPEvent *np_event) {
  // TODO: figure out how to handle Unicode input to plugins, if that's
  // even possible in the NPAPI Carbon event model.
  np_event->message = (event.nativeKeyCode << 8) & keyCodeMask;
  np_event->message |= event.text[0] & charCodeMask;
  np_event->modifiers |= btnState;
  np_event->modifiers |= CarbonModifiersFromWebEvent(event);

  switch (event.type) {
    case WebInputEvent::KeyDown:
      if (event.modifiers & WebInputEvent::IsAutoRepeat)
        np_event->what = autoKey;
      else
        np_event->what = keyDown;
      return true;
    case WebInputEvent::KeyUp:
      np_event->what = keyUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPEventFromWebInputEvent(const WebInputEvent& event,
                                     NPEvent* np_event) {
  np_event->when = TickCount();
  if (WebInputEventIsWebMouseEvent(event)) {
    return NPEventFromWebMouseEvent(*static_cast<const WebMouseEvent*>(&event),
                                    np_event);
  } else if (WebInputEventIsWebKeyboardEvent(event)) {
    return NPEventFromWebKeyboardEvent(
        *static_cast<const WebKeyboardEvent*>(&event), np_event);
  }
  DLOG(WARNING) << "unknown event type" << event.type;
  return false;
}
#endif  // !NP_NO_CARBON

static NSInteger CocoaModifiersFromWebEvent(const WebInputEvent& event) {
  NSInteger modifiers = 0;
  if (event.modifiers & WebInputEvent::ControlKey)
    modifiers |= NSControlKeyMask;
  if (event.modifiers & WebInputEvent::ShiftKey)
    modifiers |= NSShiftKeyMask;
  if (event.modifiers & WebInputEvent::AltKey)
    modifiers |= NSAlternateKeyMask;
  if (event.modifiers & WebInputEvent::MetaKey)
    modifiers |= NSCommandKeyMask;
  return modifiers;
}

static bool KeyIsModifier(int native_key_code) {
  switch (native_key_code) {
    case 55:  // Left command
    case 54:  // Right command
    case 58:  // Left option
    case 61:  // Right option
    case 59:  // Left control
    case 62:  // Right control
    case 56:  // Left shift
    case 60:  // Right shift
    case 57:  // Caps lock
      return true;
    default:
      return false;
  }
}

static bool NPCocoaEventFromWebMouseEvent(const WebMouseEvent& event,
                                          NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->data.mouse.pluginX = event.x;
  np_cocoa_event->data.mouse.pluginY = event.y;
  np_cocoa_event->data.mouse.modifierFlags |= CocoaModifiersFromWebEvent(event);
  np_cocoa_event->data.mouse.clickCount = event.clickCount;
  switch (event.button) {
    case WebMouseEvent::ButtonLeft:
      np_cocoa_event->data.mouse.buttonNumber = 0;
      break;
    case WebMouseEvent::ButtonMiddle:
      np_cocoa_event->data.mouse.buttonNumber = 2;
      break;
    case WebMouseEvent::ButtonRight:
      np_cocoa_event->data.mouse.buttonNumber = 1;
      break;
    default:
      np_cocoa_event->data.mouse.buttonNumber = event.button;
      break;
  }
  switch (event.type) {
    case WebInputEvent::MouseDown:
      np_cocoa_event->type = NPCocoaEventMouseDown;
      return true;
    case WebInputEvent::MouseUp:
      np_cocoa_event->type = NPCocoaEventMouseUp;
      return true;
    case WebInputEvent::MouseMove: {
      bool mouse_is_down = (event.modifiers & WebInputEvent::LeftButtonDown) ||
                           (event.modifiers & WebInputEvent::RightButtonDown) ||
                           (event.modifiers & WebInputEvent::MiddleButtonDown);
      np_cocoa_event->type = mouse_is_down ? NPCocoaEventMouseDragged
                                           : NPCocoaEventMouseMoved;
      return true;
    }
    case WebInputEvent::MouseEnter:
      np_cocoa_event->type = NPCocoaEventMouseEntered;
      return true;
    case WebInputEvent::MouseLeave:
      np_cocoa_event->type = NPCocoaEventMouseExited;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPCocoaEventFromWebMouseWheelEvent(const WebMouseWheelEvent& event,
                                               NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->type = NPCocoaEventScrollWheel;
  np_cocoa_event->data.mouse.pluginX = event.x;
  np_cocoa_event->data.mouse.pluginY = event.y;
  np_cocoa_event->data.mouse.modifierFlags |= CocoaModifiersFromWebEvent(event);
  np_cocoa_event->data.mouse.deltaX = event.deltaX;
  np_cocoa_event->data.mouse.deltaY = event.deltaY;
  return true;
}

static bool NPCocoaEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
                                             NPCocoaEvent *np_cocoa_event) {
  np_cocoa_event->data.key.keyCode = event.nativeKeyCode;

  np_cocoa_event->data.key.modifierFlags |= CocoaModifiersFromWebEvent(event);

  // Modifier keys have their own event type, and don't get character or
  // repeat data.
  if (KeyIsModifier(event.nativeKeyCode)) {
    np_cocoa_event->type = NPCocoaEventFlagsChanged;
    return true;
  }

  np_cocoa_event->data.key.characters = reinterpret_cast<NPNSString*>(
      [NSString stringWithFormat:@"%S", event.text]);
  np_cocoa_event->data.key.charactersIgnoringModifiers =
      reinterpret_cast<NPNSString*>(
          [NSString stringWithFormat:@"%S", event.unmodifiedText]);

  if (event.modifiers & WebInputEvent::IsAutoRepeat)
    np_cocoa_event->data.key.isARepeat = true;

  switch (event.type) {
    case WebInputEvent::KeyDown:
      np_cocoa_event->type = NPCocoaEventKeyDown;
      return true;
    case WebInputEvent::KeyUp:
      np_cocoa_event->type = NPCocoaEventKeyUp;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

static bool NPCocoaEventFromWebInputEvent(const WebInputEvent& event,
                                          NPCocoaEvent *np_cocoa_event) {
  memset(np_cocoa_event, 0, sizeof(NPCocoaEvent));
  if (event.type == WebInputEvent::MouseWheel) {
    return NPCocoaEventFromWebMouseWheelEvent(
        *static_cast<const WebMouseWheelEvent*>(&event), np_cocoa_event);
  } else if (WebInputEventIsWebMouseEvent(event)) {
    return NPCocoaEventFromWebMouseEvent(
        *static_cast<const WebMouseEvent*>(&event), np_cocoa_event);
  } else if (WebInputEventIsWebKeyboardEvent(event)) {
    return NPCocoaEventFromWebKeyboardEvent(
        *static_cast<const WebKeyboardEvent*>(&event), np_cocoa_event);
  }
  DLOG(WARNING) << "unknown event type " << event.type;
  return false;
}

bool WebPluginDelegateImpl::PlatformHandleInputEvent(
    const WebInputEvent& event, WebCursorInfo* cursor_info) {
  DCHECK(cursor_info != NULL);

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon &&
      !cg_context_.context) {
    // If we somehow get an event before we've set up the plugin window, bail.
    return false;
  }
#endif

  if (WebInputEventIsWebMouseEvent(event)) {
    // Check our plugin location before we send the event to the plugin, just
    // in case we somehow missed a plugin frame change.
    const WebMouseEvent* mouse_event =
        static_cast<const WebMouseEvent*>(&event);
    gfx::Point content_origin(
        mouse_event->globalX - mouse_event->x - window_rect_.x(),
        mouse_event->globalY - mouse_event->y - window_rect_.y());
    if (content_origin.x() != content_area_origin_.x() ||
        content_origin.y() != content_area_origin_.y()) {
      DLOG(WARNING) << "Stale plugin location: " << content_area_origin_
                    << " instead of " << content_origin;
      SetContentAreaOrigin(content_origin);
    }

    current_windowless_cursor_.GetCursorInfo(cursor_info);
  }

  // if we do not currently have focus and this is a mouseDown, trigger a
  // notification that we are taking the keyboard focus.  We can't just key
  // off of incoming calls to SetFocus, since WebKit may already think we
  // have it if we were the most recently focused element on our parent tab.
  if (event.type == WebInputEvent::MouseDown && !have_focus_) {
    SetFocus();
    // Make sure that the plugin is still there after handling the focus event.
    if (!instance())
      return false;
  }

#ifndef NP_NO_CARBON
  if (instance()->event_model() == NPEventModelCarbon) {
    if (event.type == WebInputEvent::MouseMove) {
      return true;  // The recurring OnNull will send null events.
    }

    switch (instance()->drawing_model()) {
#ifndef NP_NO_QUICKDRAW
      case NPDrawingModelQuickDraw:
        SetPort(qd_port_.port);
        break;
#endif
      case NPDrawingModelCoreGraphics:
        CGContextSaveGState(cg_context_.context);
        break;
    }
  }
#endif

  ScopedActiveDelegate active_delegate(this);

#ifndef NP_NO_CARBON
  // cgcontext_.context can change during event handling (because of a geometry
  // change triggered by the event); we need to know if that happens so we
  // don't keep trying to use the context. It is not an owning ref, so shouldn't
  // be used for anything but pointer comparison.
  CGContextRef old_context_weak = cg_context_.context;
#endif

  // Create the plugin event structure, and send it to the plugin.
  bool ret = false;
  switch (instance()->event_model()) {
#ifndef NP_NO_CARBON
    case NPEventModelCarbon: {
      NPEvent np_event = {0};
      if (!NPEventFromWebInputEvent(event, &np_event)) {
        LOG(WARNING) << "NPEventFromWebInputEvent failed";
        return false;
      }
      ret = instance()->NPP_HandleEvent(&np_event) != 0;
      break;
    }
#endif
    case NPEventModelCocoa: {
      NPCocoaEvent np_cocoa_event;
      if (!NPCocoaEventFromWebInputEvent(event, &np_cocoa_event)) {
        LOG(WARNING) << "NPCocoaEventFromWebInputEvent failed";
        return false;
      }
      NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &np_cocoa_event);
      ret = instance()->NPP_HandleEvent(&np_cocoa_event) != 0;
      break;
    }
  }

  if (WebInputEventIsWebMouseEvent(event)) {
    // Plugins are not good about giving accurate information about whether or
    // not they handled events, and other browsers on the Mac generally ignore
    // the return value. We may need to expand this to other input types, but
    // we'll need to be careful about things like Command-keys.
    ret = true;
  }

#ifndef NP_NO_CARBON
  if (instance() && instance()->event_model() == NPEventModelCarbon &&
      instance()->drawing_model() == NPDrawingModelCoreGraphics &&
      cg_context_.context == old_context_weak)
    CGContextRestoreGState(cg_context_.context);
#endif

  return ret;
}

#ifndef NP_NO_CARBON
void WebPluginDelegateImpl::FireIdleEvent() {
  // Avoid a race condition between IO and UI threads during plugin shutdown
  if (!instance())
    return;

  ScopedActiveDelegate active_delegate(this);

  if (!webkit_glue::IsPluginRunningInRendererProcess()) {
    // If the plugin is running in a subprocess, drain any pending system
    // events so that the plugin's event handlers will get called on any
    // windows it has created.  Filter out activate/deactivate events on
    // the fake browser window, but pass everything else through.
    EventRecord event;
    while (GetNextEvent(everyEvent, &event)) {
      if (!instance())
        return;
      if (event.what == activateEvt && cg_context_.window &&
          reinterpret_cast<void *>(event.message) != cg_context_.window)
        continue;
      instance()->NPP_HandleEvent(&event);
    }
    // If the plugin went away during event handling, we're done.
    if (!instance())
      return;
  }

  // Send an idle event so that the plugin can do background work
  NPEvent np_event = {0};
  np_event.what = nullEvent;
  np_event.when = TickCount();
  np_event.modifiers = GetCurrentKeyModifiers();
  if (!Button())
    np_event.modifiers |= btnState;
  HIPoint mouse_location;
  HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
  np_event.where.h = mouse_location.x;
  np_event.where.v = mouse_location.y;
  instance()->NPP_HandleEvent(&np_event);

#ifndef NP_NO_QUICKDRAW
  // Quickdraw-based plugins can draw at any time, so tell the renderer to
  // repaint.
  // TODO: only do this if the contents of the offscreen window has changed,
  // so as not to spam the renderer with an unchanging image.
  if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
    instance()->webplugin()->Invalidate();
#endif
}
#endif  // !NP_NO_CARBON

{'fraction_non_alphanumeric': 0.09944534046654341, 'fraction_numerical': 0.0027274038524579417, 'mean_word_length': 3.6003136687175776, 'pattern_counts': {'":': 0, '<': 117, '<?xml version=': 0, '>': 293, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 56, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3012716', 'n_tokens_mistral': 23475, 'n_tokens_neox': 22843, 'n_words': 5538}