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