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>
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.
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 {
51 explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
52 g_active_delegate = delegate;
54 ~ScopedActiveDelegate() {
55 g_active_delegate = NULL;
59 DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
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 {
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);
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.
88 DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
91 void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
93 case WebInputEvent::MouseEnter:
94 pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
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;
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);
137 int ExternalDragTracker::WebEventButtonModifierMask() {
138 return WebInputEvent::LeftButtonDown |
139 WebInputEvent::RightButtonDown |
140 WebInputEvent::MiddleButtonDown;
144 #pragma mark Core WebPluginDelegate implementation
146 WebPluginDelegateImpl::WebPluginDelegateImpl(
148 PluginInstance* instance)
149 : windowed_handle_(gfx::kNullPluginWindow),
150 // all Mac plugins are "windowless" in the Windows/X11 sense
155 use_buffer_context_(true),
156 buffer_context_(NULL),
160 containing_window_has_focus_(false),
161 initial_window_focus_(false),
162 container_is_visible_(false),
163 have_called_set_window_(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() {
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();
193 if (instance()->event_model() == NPEventModelCarbon)
197 window_.type = NPWindowTypeDrawable;
198 NPDrawingModel drawing_model = instance()->drawing_model();
199 switch (drawing_model) {
200 #ifndef NP_NO_QUICKDRAW
201 case NPDrawingModelQuickDraw:
204 case NPDrawingModelCoreGraphics:
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
211 CALayer* layer = nil;
212 NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
213 reinterpret_cast<void*>(&layer));
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>);
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();
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.
257 plugin_->SetWindow(gfx::kNullPluginWindow);
262 void WebPluginDelegateImpl::PlatformDestroyInstance() {
264 redraw_timer_->Stop();
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_)
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)) {
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);
323 current_windowless_cursor_.GetCursorInfo(cursor_info);
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_;
337 --keyup_ignore_count_;
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))
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);
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);
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) {
384 ++keyup_ignore_count_;
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)) {
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))
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)
420 if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
421 PluginVisibilityChanged();
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_))
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()));
446 // Use the actual window region when drawing directly to the window context.
447 paint_rect.Intersect(window_rect_);
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());
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);
477 // Always restore the context to the saved state.
478 CGContextRestoreGState(context);
482 void WebPluginDelegateImpl::WindowlessSetWindow() {
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_);
503 DCHECK(err == NPERR_NO_ERROR);
508 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
513 void WebPluginDelegateImpl::WindowedDestroyWindow() {
517 bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
518 const gfx::Rect& clip_rect) {
523 void WebPluginDelegateImpl::WindowedSetWindow() {
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;
547 if (has_focus == containing_window_has_focus_)
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_)
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);
576 void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
577 if (is_visible == container_is_visible_)
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.
585 clip_rect_ = cached_clip_rect_;
587 clip_rect_.set_width(0);
588 clip_rect_.set_height(0);
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();
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).
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);
629 void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
630 current_windowless_cursor_.InitFromNSCursor(cursor);
633 void WebPluginDelegateImpl::SetNoBufferContext() {
634 use_buffer_context_ = false;
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();
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();
673 void WebPluginDelegateImpl::StartIme() {
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.
688 [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
689 if (CGRectIsEmpty([renderer_ updateBounds])) {
690 // If nothing has changed, we are done.
691 [renderer_ endFrame];
695 surface_->StartDrawing();
697 CGRect layerRect = [layer_ bounds];
698 [renderer_ addUpdateRect:layerRect];
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_)
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