Home | History | Annotate | Download | only in npapi
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/child/npapi/webplugin_delegate_impl.h"
      6 
      7 #import <Cocoa/Cocoa.h>
      8 #import <QuartzCore/QuartzCore.h>
      9 #include <unistd.h>
     10 
     11 #include <set>
     12 #include <string>
     13 
     14 #include "base/mac/mac_util.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/metrics/stats_counters.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/sys_string_conversions.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "content/child/npapi/plugin_instance.h"
     21 #include "content/child/npapi/plugin_lib.h"
     22 #include "content/child/npapi/plugin_web_event_converter_mac.h"
     23 #include "content/child/npapi/webplugin.h"
     24 #include "content/child/npapi/webplugin_accelerated_surface_mac.h"
     25 #include "skia/ext/skia_utils_mac.h"
     26 #include "third_party/WebKit/public/web/WebInputEvent.h"
     27 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
     28 #include "webkit/common/cursors/webcursor.h"
     29 
     30 using blink::WebKeyboardEvent;
     31 using blink::WebInputEvent;
     32 using blink::WebMouseEvent;
     33 using blink::WebMouseWheelEvent;
     34 
     35 // Important implementation notes: The Mac definition of NPAPI, particularly
     36 // the distinction between windowed and windowless modes, differs from the
     37 // Windows and Linux definitions.  Most of those differences are
     38 // accomodated by the WebPluginDelegate class.
     39 
     40 namespace content {
     41 
     42 namespace {
     43 
     44 const int kCoreAnimationRedrawPeriodMs = 10;  // 100 Hz
     45 
     46 WebPluginDelegateImpl* g_active_delegate;
     47 
     48 // Helper to simplify correct usage of g_active_delegate.  Instantiating will
     49 // set the active delegate to |delegate| for the lifetime of the object, then
     50 // NULL when it goes out of scope.
     51 class ScopedActiveDelegate {
     52  public:
     53   explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
     54     g_active_delegate = delegate;
     55   }
     56   ~ScopedActiveDelegate() {
     57     g_active_delegate = NULL;
     58   }
     59 
     60  private:
     61   DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
     62 };
     63 
     64 }  // namespace
     65 
     66 // Helper to build and maintain a model of a drag entering the plugin but not
     67 // starting there. See explanation in PlatformHandleInputEvent.
     68 class ExternalDragTracker {
     69  public:
     70   ExternalDragTracker() : pressed_buttons_(0) {}
     71 
     72   // Returns true if an external drag is in progress.
     73   bool IsDragInProgress() { return pressed_buttons_ != 0; };
     74 
     75   // Returns true if the given event appears to be related to an external drag.
     76   bool EventIsRelatedToDrag(const WebInputEvent& event);
     77 
     78   // Updates the tracking of whether an external drag is in progress--and if
     79   // so what buttons it involves--based on the given event.
     80   void UpdateDragStateFromEvent(const WebInputEvent& event);
     81 
     82  private:
     83   // Returns the mask for just the button state in a WebInputEvent's modifiers.
     84   static int WebEventButtonModifierMask();
     85 
     86   // The WebInputEvent modifier flags for any buttons that were down when an
     87   // external drag entered the plugin, and which and are still down now.
     88   int pressed_buttons_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
     91 };
     92 
     93 void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
     94   switch (event.type) {
     95     case WebInputEvent::MouseEnter:
     96       pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
     97       break;
     98     case WebInputEvent::MouseUp: {
     99       const WebMouseEvent* mouse_event =
    100           static_cast<const WebMouseEvent*>(&event);
    101       if (mouse_event->button == WebMouseEvent::ButtonLeft)
    102         pressed_buttons_ &= ~WebInputEvent::LeftButtonDown;
    103       if (mouse_event->button == WebMouseEvent::ButtonMiddle)
    104         pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown;
    105       if (mouse_event->button == WebMouseEvent::ButtonRight)
    106         pressed_buttons_ &= ~WebInputEvent::RightButtonDown;
    107       break;
    108     }
    109     default:
    110       break;
    111   }
    112 }
    113 
    114 bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) {
    115   const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event);
    116   switch (event.type) {
    117     case WebInputEvent::MouseUp:
    118       // We only care about release of buttons that were part of the drag.
    119       return ((mouse_event->button == WebMouseEvent::ButtonLeft &&
    120                (pressed_buttons_ & WebInputEvent::LeftButtonDown)) ||
    121               (mouse_event->button == WebMouseEvent::ButtonMiddle &&
    122                (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) ||
    123               (mouse_event->button == WebMouseEvent::ButtonRight &&
    124                (pressed_buttons_ & WebInputEvent::RightButtonDown)));
    125     case WebInputEvent::MouseEnter:
    126       return (event.modifiers & WebEventButtonModifierMask()) != 0;
    127     case WebInputEvent::MouseLeave:
    128     case WebInputEvent::MouseMove: {
    129       int event_buttons = (event.modifiers & WebEventButtonModifierMask());
    130       return (pressed_buttons_ &&
    131               pressed_buttons_ == event_buttons);
    132     }
    133     default:
    134       return false;
    135   }
    136   return false;
    137 }
    138 
    139 int ExternalDragTracker::WebEventButtonModifierMask() {
    140   return WebInputEvent::LeftButtonDown |
    141          WebInputEvent::RightButtonDown |
    142          WebInputEvent::MiddleButtonDown;
    143 }
    144 
    145 #pragma mark -
    146 #pragma mark Core WebPluginDelegate implementation
    147 
    148 WebPluginDelegateImpl::WebPluginDelegateImpl(
    149     WebPlugin* plugin,
    150     PluginInstance* instance)
    151     : windowed_handle_(gfx::kNullPluginWindow),
    152       // all Mac plugins are "windowless" in the Windows/X11 sense
    153       windowless_(true),
    154       plugin_(plugin),
    155       instance_(instance),
    156       quirks_(0),
    157       use_buffer_context_(true),
    158       buffer_context_(NULL),
    159       layer_(nil),
    160       surface_(NULL),
    161       renderer_(nil),
    162       containing_window_has_focus_(false),
    163       initial_window_focus_(false),
    164       container_is_visible_(false),
    165       have_called_set_window_(false),
    166       ime_enabled_(false),
    167       keyup_ignore_count_(0),
    168       external_drag_tracker_(new ExternalDragTracker()),
    169       handle_event_depth_(0),
    170       first_set_window_call_(true),
    171       plugin_has_focus_(false),
    172       has_webkit_focus_(false),
    173       containing_view_has_focus_(true),
    174       creation_succeeded_(false) {
    175   memset(&window_, 0, sizeof(window_));
    176   instance->set_windowless(true);
    177 }
    178 
    179 WebPluginDelegateImpl::~WebPluginDelegateImpl() {
    180   DestroyInstance();
    181 }
    182 
    183 bool WebPluginDelegateImpl::PlatformInitialize() {
    184   // Don't set a NULL window handle on destroy for Mac plugins.  This matches
    185   // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
    186   // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
    187   // destroyPlugin in WebNetscapePluginView.mm, for examples).
    188   quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
    189 
    190   // Mac plugins don't expect to be unloaded, and they don't always do so
    191   // cleanly, so don't unload them at shutdown.
    192   instance()->plugin_lib()->PreventLibraryUnload();
    193 
    194 #ifndef NP_NO_CARBON
    195   if (instance()->event_model() == NPEventModelCarbon)
    196     return false;
    197 #endif
    198 
    199   window_.type = NPWindowTypeDrawable;
    200   NPDrawingModel drawing_model = instance()->drawing_model();
    201   switch (drawing_model) {
    202 #ifndef NP_NO_QUICKDRAW
    203     case NPDrawingModelQuickDraw:
    204       return false;
    205 #endif
    206     case NPDrawingModelCoreGraphics:
    207       break;
    208     case NPDrawingModelCoreAnimation:
    209     case NPDrawingModelInvalidatingCoreAnimation: {
    210       // Ask the plug-in for the CALayer it created for rendering content.
    211       // Create a surface to host it, and request a "window" handle to identify
    212       // the surface.
    213       CALayer* layer = nil;
    214       NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
    215                                              reinterpret_cast<void*>(&layer));
    216       if (!err) {
    217         if (drawing_model == NPDrawingModelCoreAnimation) {
    218           // Create the timer; it will be started when we get a window handle.
    219           redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>);
    220         }
    221         layer_ = layer;
    222 
    223         gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
    224         // On dual GPU systems, force the use of the discrete GPU for
    225         // the CARenderer underlying our Core Animation backend for
    226         // all plugins except Flash. For some reason Unity3D's output
    227         // doesn't show up if the integrated GPU is used. Safari keeps
    228         // even Flash 11 with Stage3D on the integrated GPU, so mirror
    229         // that behavior here.
    230         const WebPluginInfo& plugin_info =
    231             instance_->plugin_lib()->plugin_info();
    232         if (plugin_info.name.find(ASCIIToUTF16("Flash")) !=
    233             base::string16::npos)
    234           gpu_preference = gfx::PreferIntegratedGpu;
    235         surface_ = plugin_->GetAcceleratedSurface(gpu_preference);
    236 
    237         // If surface initialization fails for some reason, just continue
    238         // without any drawing; returning false would be a more confusing user
    239         // experience (since it triggers a missing plugin placeholder).
    240         if (surface_ && surface_->context()) {
    241           renderer_ = [[CARenderer rendererWithCGLContext:surface_->context()
    242                                                   options:NULL] retain];
    243           [renderer_ setLayer:layer_];
    244           plugin_->AcceleratedPluginEnabledRendering();
    245         }
    246       }
    247       break;
    248     }
    249     default:
    250       NOTREACHED();
    251       break;
    252   }
    253 
    254   // Let the WebPlugin know that we are windowless, unless this is a Core
    255   // Animation plugin, in which case AcceleratedPluginEnabledRendering
    256   // calls SetWindow. Rendering breaks if SetWindow is called before
    257   // accelerated rendering is enabled.
    258   if (!layer_)
    259     plugin_->SetWindow(gfx::kNullPluginWindow);
    260 
    261   return true;
    262 }
    263 
    264 void WebPluginDelegateImpl::PlatformDestroyInstance() {
    265   if (redraw_timer_)
    266     redraw_timer_->Stop();
    267   [renderer_ release];
    268   renderer_ = nil;
    269   layer_ = nil;
    270 }
    271 
    272 void WebPluginDelegateImpl::UpdateGeometryAndContext(
    273     const gfx::Rect& window_rect, const gfx::Rect& clip_rect,
    274     CGContextRef context) {
    275   buffer_context_ = context;
    276   UpdateGeometry(window_rect, clip_rect);
    277 }
    278 
    279 void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) {
    280   gfx::SkiaBitLocker bit_locker(canvas);
    281   CGContextRef context = bit_locker.cgContext();
    282   CGPaint(context, rect);
    283 }
    284 
    285 void WebPluginDelegateImpl::CGPaint(CGContextRef context,
    286                                     const gfx::Rect& rect) {
    287   WindowlessPaint(context, rect);
    288 }
    289 
    290 bool WebPluginDelegateImpl::PlatformHandleInputEvent(
    291     const WebInputEvent& event, WebCursor::CursorInfo* cursor_info) {
    292   DCHECK(cursor_info != NULL);
    293 
    294   // If an event comes in before the plugin has been set up, bail.
    295   if (!have_called_set_window_)
    296     return false;
    297 
    298   // WebKit sometimes sends spurious mouse move events when the window doesn't
    299   // have focus; Cocoa event model plugins don't expect to receive mouse move
    300   // events when they are in a background window, so drop those events.
    301   if (!containing_window_has_focus_ &&
    302       (event.type == WebInputEvent::MouseMove ||
    303        event.type == WebInputEvent::MouseEnter ||
    304        event.type == WebInputEvent::MouseLeave)) {
    305     return false;
    306   }
    307 
    308   if (WebInputEvent::isMouseEventType(event.type) ||
    309       event.type == WebInputEvent::MouseWheel) {
    310     // Check our plugin location before we send the event to the plugin, just
    311     // in case we somehow missed a plugin frame change.
    312     const WebMouseEvent* mouse_event =
    313         static_cast<const WebMouseEvent*>(&event);
    314     gfx::Point content_origin(
    315         mouse_event->globalX - mouse_event->x - window_rect_.x(),
    316         mouse_event->globalY - mouse_event->y - window_rect_.y());
    317     if (content_origin.x() != content_area_origin_.x() ||
    318         content_origin.y() != content_area_origin_.y()) {
    319       DLOG(WARNING) << "Stale plugin content area location: "
    320                     << content_area_origin_.ToString() << " instead of "
    321                     << content_origin.ToString();
    322       SetContentAreaOrigin(content_origin);
    323     }
    324 
    325     current_windowless_cursor_.GetCursorInfo(cursor_info);
    326   }
    327 
    328   // Per the Cocoa Plugin IME spec, plugins shoudn't receive keydown or keyup
    329   // events while composition is in progress. Treat them as handled, however,
    330   // since IME is consuming them on behalf of the plugin.
    331   if ((event.type == WebInputEvent::KeyDown && ime_enabled_) ||
    332       (event.type == WebInputEvent::KeyUp && keyup_ignore_count_)) {
    333     // Composition ends on a keydown, so ime_enabled_ will be false at keyup;
    334     // because the keydown wasn't sent to the plugin, the keyup shouldn't be
    335     // either (per the spec).
    336     if (event.type == WebInputEvent::KeyDown)
    337       ++keyup_ignore_count_;
    338     else
    339       --keyup_ignore_count_;
    340     return true;
    341   }
    342 
    343   ScopedActiveDelegate active_delegate(this);
    344 
    345   // Create the plugin event structure.
    346   scoped_ptr<PluginWebEventConverter> event_converter(
    347       new PluginWebEventConverter);
    348   if (!event_converter->InitWithEvent(event)) {
    349     // Silently consume any keyboard event types that aren't handled, so that
    350     // they don't fall through to the page.
    351     if (WebInputEvent::isKeyboardEventType(event.type))
    352       return true;
    353     return false;
    354   }
    355   NPCocoaEvent* plugin_event = event_converter->plugin_event();
    356 
    357   // The plugin host recieves events related to drags starting outside the
    358   // plugin, but the NPAPI Cocoa event model spec says plugins shouldn't receive
    359   // them, so filter them out.
    360   // If WebKit adds a page capture mode (like the plugin capture mode that
    361   // handles drags starting inside) this can be removed.
    362   bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event);
    363   external_drag_tracker_->UpdateDragStateFromEvent(event);
    364   if (drag_related) {
    365     if (event.type == WebInputEvent::MouseUp &&
    366         !external_drag_tracker_->IsDragInProgress()) {
    367       // When an external drag ends, we need to synthesize a MouseEntered.
    368       NPCocoaEvent enter_event = *plugin_event;
    369       enter_event.type = NPCocoaEventMouseEntered;
    370       ScopedCurrentPluginEvent event_scope(instance(), &enter_event);
    371       instance()->NPP_HandleEvent(&enter_event);
    372     }
    373     return false;
    374   }
    375 
    376   // Send the plugin the event.
    377   scoped_ptr<ScopedCurrentPluginEvent> event_scope(
    378       new ScopedCurrentPluginEvent(instance(), plugin_event));
    379   int16_t handle_response = instance()->NPP_HandleEvent(plugin_event);
    380   bool handled = handle_response != kNPEventNotHandled;
    381 
    382   // Start IME if requested by the plugin.
    383   if (handled && handle_response == kNPEventStartIME &&
    384       event.type == WebInputEvent::KeyDown) {
    385     StartIme();
    386     ++keyup_ignore_count_;
    387   }
    388 
    389   // Plugins don't give accurate information about whether or not they handled
    390   // events, so browsers on the Mac ignore the return value.
    391   // Scroll events are the exception, since the Cocoa spec defines a meaning
    392   // for the return value.
    393   if (WebInputEvent::isMouseEventType(event.type)) {
    394     handled = true;
    395   } else if (WebInputEvent::isKeyboardEventType(event.type)) {
    396     // For Command-key events, trust the return value since eating all menu
    397     // shortcuts is not ideal.
    398     // TODO(stuartmorgan): Implement the advanced key handling spec, and trust
    399     // trust the key event return value from plugins that implement it.
    400     if (!(event.modifiers & WebInputEvent::MetaKey))
    401       handled = true;
    402   }
    403 
    404   return handled;
    405 }
    406 
    407 #pragma mark -
    408 
    409 void WebPluginDelegateImpl::WindowlessUpdateGeometry(
    410     const gfx::Rect& window_rect,
    411     const gfx::Rect& clip_rect) {
    412   gfx::Rect old_clip_rect = clip_rect_;
    413   cached_clip_rect_ = clip_rect;
    414   if (container_is_visible_)  // Remove check when cached_clip_rect_ is removed.
    415     clip_rect_ = clip_rect;
    416   bool clip_rect_changed = (clip_rect_ != old_clip_rect);
    417   bool window_size_changed = (window_rect.size() != window_rect_.size());
    418 
    419   if (window_rect == window_rect_ && !clip_rect_changed)
    420     return;
    421 
    422   if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
    423     PluginVisibilityChanged();
    424   }
    425 
    426   SetPluginRect(window_rect);
    427 
    428   if (window_size_changed || clip_rect_changed)
    429     WindowlessSetWindow();
    430 }
    431 
    432 void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
    433                                             const gfx::Rect& damage_rect) {
    434   // If we get a paint event before we are completely set up (e.g., a nested
    435   // call while the plugin is still in NPP_SetWindow), bail.
    436   if (!have_called_set_window_ || (use_buffer_context_ && !buffer_context_))
    437     return;
    438   DCHECK(!use_buffer_context_ || buffer_context_ == context);
    439 
    440   base::StatsRate plugin_paint("Plugin.Paint");
    441   base::StatsScope<base::StatsRate> scope(plugin_paint);
    442 
    443   gfx::Rect paint_rect = damage_rect;
    444   if (use_buffer_context_) {
    445     // Plugin invalidates trigger asynchronous paints with the original
    446     // invalidation rect; the plugin may be resized before the paint is handled,
    447     // so we need to ensure that the damage rect is still sane.
    448     paint_rect.Intersect(
    449         gfx::Rect(0, 0, window_rect_.width(), window_rect_.height()));
    450   } else {
    451     // Use the actual window region when drawing directly to the window context.
    452     paint_rect.Intersect(window_rect_);
    453   }
    454 
    455   ScopedActiveDelegate active_delegate(this);
    456 
    457   CGContextSaveGState(context);
    458 
    459   if (!use_buffer_context_) {
    460     // Reposition the context origin so that plugins will draw at the correct
    461     // location in the window.
    462     CGContextClipToRect(context, paint_rect.ToCGRect());
    463     CGContextTranslateCTM(context, window_rect_.x(), window_rect_.y());
    464   }
    465 
    466   NPCocoaEvent paint_event;
    467   memset(&paint_event, 0, sizeof(NPCocoaEvent));
    468   paint_event.type = NPCocoaEventDrawRect;
    469   paint_event.data.draw.context = context;
    470   paint_event.data.draw.x = paint_rect.x();
    471   paint_event.data.draw.y = paint_rect.y();
    472   paint_event.data.draw.width = paint_rect.width();
    473   paint_event.data.draw.height = paint_rect.height();
    474   instance()->NPP_HandleEvent(&paint_event);
    475 
    476   if (use_buffer_context_) {
    477     // The backing buffer can change during the call to NPP_HandleEvent, in
    478     // which case the old context is (or is about to be) invalid.
    479     if (context == buffer_context_)
    480       CGContextRestoreGState(context);
    481   } else {
    482     // Always restore the context to the saved state.
    483     CGContextRestoreGState(context);
    484   }
    485 }
    486 
    487 void WebPluginDelegateImpl::WindowlessSetWindow() {
    488   if (!instance())
    489     return;
    490 
    491   window_.x = 0;
    492   window_.y = 0;
    493   window_.height = window_rect_.height();
    494   window_.width = window_rect_.width();
    495   window_.clipRect.left = clip_rect_.x();
    496   window_.clipRect.top = clip_rect_.y();
    497   window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
    498   window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
    499 
    500   NPError err = instance()->NPP_SetWindow(&window_);
    501 
    502   // Send an appropriate window focus event after the first SetWindow.
    503   if (!have_called_set_window_) {
    504     have_called_set_window_ = true;
    505     SetWindowHasFocus(initial_window_focus_);
    506   }
    507 
    508   DCHECK(err == NPERR_NO_ERROR);
    509 }
    510 
    511 #pragma mark -
    512 
    513 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
    514   NOTREACHED();
    515   return false;
    516 }
    517 
    518 void WebPluginDelegateImpl::WindowedDestroyWindow() {
    519   NOTREACHED();
    520 }
    521 
    522 bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
    523                                                const gfx::Rect& clip_rect) {
    524   NOTREACHED();
    525   return false;
    526 }
    527 
    528 void WebPluginDelegateImpl::WindowedSetWindow() {
    529   NOTREACHED();
    530 }
    531 
    532 #pragma mark -
    533 #pragma mark Mac Extensions
    534 
    535 void WebPluginDelegateImpl::PluginDidInvalidate() {
    536   if (instance()->drawing_model() == NPDrawingModelInvalidatingCoreAnimation)
    537     DrawLayerInSurface();
    538 }
    539 
    540 WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
    541   return g_active_delegate;
    542 }
    543 
    544 void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
    545   // If we get a window focus event before calling SetWindow, just remember the
    546   // states (WindowlessSetWindow will then send it on the first call).
    547   if (!have_called_set_window_) {
    548     initial_window_focus_ = has_focus;
    549     return;
    550   }
    551 
    552   if (has_focus == containing_window_has_focus_)
    553     return;
    554   containing_window_has_focus_ = has_focus;
    555 
    556   ScopedActiveDelegate active_delegate(this);
    557   NPCocoaEvent focus_event;
    558   memset(&focus_event, 0, sizeof(focus_event));
    559   focus_event.type = NPCocoaEventWindowFocusChanged;
    560   focus_event.data.focus.hasFocus = has_focus;
    561   instance()->NPP_HandleEvent(&focus_event);
    562 }
    563 
    564 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
    565   if (!have_called_set_window_)
    566     return false;
    567 
    568   plugin_->FocusChanged(focused);
    569 
    570   ScopedActiveDelegate active_delegate(this);
    571 
    572   NPCocoaEvent focus_event;
    573   memset(&focus_event, 0, sizeof(focus_event));
    574   focus_event.type = NPCocoaEventFocusChanged;
    575   focus_event.data.focus.hasFocus = focused;
    576   instance()->NPP_HandleEvent(&focus_event);
    577 
    578   return true;
    579 }
    580 
    581 void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
    582   if (is_visible == container_is_visible_)
    583     return;
    584   container_is_visible_ = is_visible;
    585 
    586   // TODO(stuartmorgan): This is a temporary workarond for
    587   // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
    588   // should all be removed.
    589   if (is_visible) {
    590     clip_rect_ = cached_clip_rect_;
    591   } else {
    592     clip_rect_.set_width(0);
    593     clip_rect_.set_height(0);
    594   }
    595 
    596   // If the plugin is changing visibility, let the plugin know. If it's scrolled
    597   // off screen (i.e., cached_clip_rect_ is empty), then container visibility
    598   // doesn't change anything.
    599   if (!cached_clip_rect_.IsEmpty()) {
    600     PluginVisibilityChanged();
    601     WindowlessSetWindow();
    602   }
    603 
    604   // When the plugin become visible, send an empty invalidate. If there were any
    605   // pending invalidations this will trigger a paint event for the damaged
    606   // region, and if not it's a no-op. This is necessary since higher levels
    607   // that would normally do this weren't responsible for the clip_rect_ change).
    608   if (!clip_rect_.IsEmpty())
    609     instance()->webplugin()->InvalidateRect(gfx::Rect());
    610 }
    611 
    612 void WebPluginDelegateImpl::WindowFrameChanged(const gfx::Rect& window_frame,
    613                                                const gfx::Rect& view_frame) {
    614   instance()->set_window_frame(window_frame);
    615   SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
    616 }
    617 
    618 void WebPluginDelegateImpl::ImeCompositionCompleted(
    619     const base::string16& text) {
    620   ime_enabled_ = false;
    621 
    622   // If |text| is empty this was just called to tell us composition was
    623   // cancelled externally (e.g., the user pressed esc).
    624   if (!text.empty()) {
    625     NPCocoaEvent text_event;
    626     memset(&text_event, 0, sizeof(NPCocoaEvent));
    627     text_event.type = NPCocoaEventTextInput;
    628     text_event.data.text.text =
    629         reinterpret_cast<NPNSString*>(base::SysUTF16ToNSString(text));
    630     instance()->NPP_HandleEvent(&text_event);
    631   }
    632 }
    633 
    634 void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
    635   current_windowless_cursor_.InitFromNSCursor(cursor);
    636 }
    637 
    638 void WebPluginDelegateImpl::SetNoBufferContext() {
    639   use_buffer_context_ = false;
    640 }
    641 
    642 #pragma mark -
    643 #pragma mark Internal Tracking
    644 
    645 void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
    646   bool plugin_size_changed = rect.width() != window_rect_.width() ||
    647                              rect.height() != window_rect_.height();
    648   window_rect_ = rect;
    649   PluginScreenLocationChanged();
    650   if (plugin_size_changed)
    651     UpdateAcceleratedSurface();
    652 }
    653 
    654 void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
    655   content_area_origin_ = origin;
    656   PluginScreenLocationChanged();
    657 }
    658 
    659 void WebPluginDelegateImpl::PluginScreenLocationChanged() {
    660   gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
    661                            content_area_origin_.y() + window_rect_.y());
    662   instance()->set_plugin_origin(plugin_origin);
    663 }
    664 
    665 void WebPluginDelegateImpl::PluginVisibilityChanged() {
    666   if (instance()->drawing_model() == NPDrawingModelCoreAnimation) {
    667     bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
    668     if (plugin_visible && !redraw_timer_->IsRunning()) {
    669       redraw_timer_->Start(FROM_HERE,
    670           base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs),
    671           this, &WebPluginDelegateImpl::DrawLayerInSurface);
    672     } else if (!plugin_visible) {
    673       redraw_timer_->Stop();
    674     }
    675   }
    676 }
    677 
    678 void WebPluginDelegateImpl::StartIme() {
    679   if (ime_enabled_)
    680     return;
    681   ime_enabled_ = true;
    682   plugin_->StartIme();
    683 }
    684 
    685 #pragma mark -
    686 #pragma mark Core Animation Support
    687 
    688 void WebPluginDelegateImpl::DrawLayerInSurface() {
    689   // If we haven't plumbed up the surface yet, don't try to draw.
    690   if (!renderer_)
    691     return;
    692 
    693   [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
    694   if (CGRectIsEmpty([renderer_ updateBounds])) {
    695     // If nothing has changed, we are done.
    696     [renderer_ endFrame];
    697     return;
    698   }
    699 
    700   surface_->StartDrawing();
    701 
    702   CGRect layerRect = [layer_ bounds];
    703   [renderer_ addUpdateRect:layerRect];
    704   [renderer_ render];
    705   [renderer_ endFrame];
    706 
    707   surface_->EndDrawing();
    708 }
    709 
    710 // Update the size of the surface to match the current size of the plug-in.
    711 void WebPluginDelegateImpl::UpdateAcceleratedSurface() {
    712   if (!surface_ || !layer_)
    713     return;
    714 
    715   [CATransaction begin];
    716   [CATransaction setValue:[NSNumber numberWithInt:0]
    717                    forKey:kCATransactionAnimationDuration];
    718   [layer_ setFrame:CGRectMake(0, 0,
    719                               window_rect_.width(), window_rect_.height())];
    720   [CATransaction commit];
    721 
    722   [renderer_ setBounds:[layer_ bounds]];
    723   surface_->SetSize(window_rect_.size());
    724   // Kick off the drawing timer, if necessary.
    725   PluginVisibilityChanged();
    726 }
    727 
    728 }  // namespace content
    729