Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: webkit/glue/plugins/webplugin_delegate_impl_mac.mm

Issue 6012002: Move the NPAPI files from webkit/glue/plugins to webkit/plugins/npapi and put... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 #import <Cocoa/Cocoa.h>
6 #import <QuartzCore/QuartzCore.h>
7
8 #include "webkit/glue/plugins/webplugin_delegate_impl.h"
9
10 #include <string>
11 #include <unistd.h>
12 #include <set>
13
14 #include "base/file_util.h"
15 #include "base/message_loop.h"
16 #include "base/metrics/stats_counters.h"
17 #include "base/scoped_ptr.h"
18 #include "base/string_util.h"
19 #include "base/utf_string_conversions.h"
20 #include "base/sys_string_conversions.h"
21 #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
22 #include "webkit/glue/plugins/plugin_instance.h"
23 #include "webkit/glue/plugins/plugin_lib.h"
24 #include "webkit/glue/plugins/plugin_list.h"
25 #include "webkit/glue/plugins/plugin_stream_url.h"
26 #include "webkit/glue/plugins/plugin_web_event_converter_mac.h"
27 #include "webkit/glue/plugins/webplugin.h"
28 #include "webkit/glue/plugins/webplugin_accelerated_surface_mac.h"
29 #include "webkit/glue/webkit_glue.h"
30
31 #ifndef NP_NO_CARBON
32 #include "webkit/glue/plugins/carbon_plugin_window_tracker_mac.h"
33 #endif
34
35 #ifndef NP_NO_QUICKDRAW
36 #include "webkit/glue/plugins/quickdraw_drawing_manager_mac.h"
37 #endif
38
39 using webkit_glue::WebPlugin;
40 using webkit_glue::WebPluginDelegate;
41 using webkit_glue::WebPluginResourceClient;
42 using WebKit::WebCursorInfo;
43 using WebKit::WebKeyboardEvent;
44 using WebKit::WebInputEvent;
45 using WebKit::WebMouseEvent;
46 using WebKit::WebMouseWheelEvent;
47
48 const int kCoreAnimationRedrawPeriodMs = 10; // 100 Hz
49
50 // Important implementation notes: The Mac definition of NPAPI, particularly
51 // the distinction between windowed and windowless modes, differs from the
52 // Windows and Linux definitions. Most of those differences are
53 // accomodated by the WebPluginDelegate class.
54
55 namespace {
56
57 WebPluginDelegateImpl* g_active_delegate;
58
59 // Helper to simplify correct usage of g_active_delegate. Instantiating will
60 // set the active delegate to |delegate| for the lifetime of the object, then
61 // NULL when it goes out of scope.
62 class ScopedActiveDelegate {
63 public:
64 explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
65 g_active_delegate = delegate;
66 }
67 ~ScopedActiveDelegate() {
68 g_active_delegate = NULL;
69 }
70 private:
71 DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
72 };
73
74 #ifndef NP_NO_CARBON
75 // Timer periods for sending idle events to Carbon plugins. The visible value
76 // (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
77 // Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
78 // going lower than that causes issues.
79 const int kVisibleIdlePeriodMs = 20; // (50Hz)
80 const int kHiddenIdlePeriodMs = 125; // (8Hz)
81
82 class CarbonIdleEventSource {
83 public:
84 // Returns the shared Carbon idle event source.
85 static CarbonIdleEventSource* SharedInstance() {
86 DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
87 static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
88 return event_source;
89 }
90
91 // Registers the plugin delegate as interested in receiving idle events at
92 // a rate appropriate for the given visibility. A delegate can safely be
93 // re-registered any number of times, with the latest registration winning.
94 void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
95 if (visible) {
96 visible_delegates_->RegisterDelegate(delegate);
97 hidden_delegates_->UnregisterDelegate(delegate);
98 } else {
99 hidden_delegates_->RegisterDelegate(delegate);
100 visible_delegates_->UnregisterDelegate(delegate);
101 }
102 }
103
104 // Removes the plugin delegate from the list of plugins receiving idle events.
105 void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
106 visible_delegates_->UnregisterDelegate(delegate);
107 hidden_delegates_->UnregisterDelegate(delegate);
108 }
109
110 private:
111 class VisibilityGroup {
112 public:
113 explicit VisibilityGroup(int timer_period)
114 : timer_period_(timer_period), iterator_(delegates_.end()) {}
115
116 // Adds |delegate| to this visibility group.
117 void RegisterDelegate(WebPluginDelegateImpl* delegate) {
118 if (delegates_.empty()) {
119 timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
120 this, &VisibilityGroup::SendIdleEvents);
121 }
122 delegates_.insert(delegate);
123 }
124
125 // Removes |delegate| from this visibility group.
126 void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
127 // If a plugin changes visibility during idle event handling, it
128 // may be removed from this set while SendIdleEvents is still iterating;
129 // if that happens and it's next on the list, increment the iterator
130 // before erasing so that the iteration won't be corrupted.
131 if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
132 ++iterator_;
133 size_t removed = delegates_.erase(delegate);
134 if (removed > 0 && delegates_.empty())
135 timer_.Stop();
136 }
137
138 private:
139 // Fires off idle events for each delegate in the group.
140 void SendIdleEvents() {
141 for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
142 // Pre-increment so that the skip logic in UnregisterDelegates works.
143 WebPluginDelegateImpl* delegate = *(iterator_++);
144 delegate->FireIdleEvent();
145 }
146 }
147
148 int timer_period_;
149 base::RepeatingTimer<VisibilityGroup> timer_;
150 std::set<WebPluginDelegateImpl*> delegates_;
151 std::set<WebPluginDelegateImpl*>::iterator iterator_;
152 };
153
154 CarbonIdleEventSource()
155 : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
156 hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}
157
158 scoped_ptr<VisibilityGroup> visible_delegates_;
159 scoped_ptr<VisibilityGroup> hidden_delegates_;
160
161 DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
162 };
163 #endif // !NP_NO_CARBON
164
165 } // namespace
166
167 // Helper to build and maintain a model of a drag entering the plugin but not
168 // starting there. See explanation in PlatformHandleInputEvent.
169 class ExternalDragTracker {
170 public:
171 ExternalDragTracker() : pressed_buttons_(0) {}
172
173 // Returns true if an external drag is in progress.
174 bool IsDragInProgress() { return pressed_buttons_ != 0; };
175
176 // Returns true if the given event appears to be related to an external drag.
177 bool EventIsRelatedToDrag(const WebInputEvent& event);
178
179 // Updates the tracking of whether an external drag is in progress--and if
180 // so what buttons it involves--based on the given event.
181 void UpdateDragStateFromEvent(const WebInputEvent& event);
182
183 private:
184 // Returns the mask for just the button state in a WebInputEvent's modifiers.
185 static int WebEventButtonModifierMask();
186
187 // The WebInputEvent modifier flags for any buttons that were down when an
188 // external drag entered the plugin, and which and are still down now.
189 int pressed_buttons_;
190
191 DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
192 };
193
194 void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
195 switch (event.type) {
196 case WebInputEvent::MouseEnter:
197 pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
198 break;
199 case WebInputEvent::MouseUp: {
200 const WebMouseEvent* mouse_event =
201 static_cast<const WebMouseEvent*>(&event);
202 if (mouse_event->button == WebMouseEvent::ButtonLeft)
203 pressed_buttons_ &= ~WebInputEvent::LeftButtonDown;
204 if (mouse_event->button == WebMouseEvent::ButtonMiddle)
205 pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown;
206 if (mouse_event->button == WebMouseEvent::ButtonRight)
207 pressed_buttons_ &= ~WebInputEvent::RightButtonDown;
208 break;
209 }
210 default:
211 break;
212 }
213 }
214
215 bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) {
216 const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event);
217 switch (event.type) {
218 case WebInputEvent::MouseUp:
219 // We only care about release of buttons that were part of the drag.
220 return ((mouse_event->button == WebMouseEvent::ButtonLeft &&
221 (pressed_buttons_ & WebInputEvent::LeftButtonDown)) ||
222 (mouse_event->button == WebMouseEvent::ButtonMiddle &&
223 (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) ||
224 (mouse_event->button == WebMouseEvent::ButtonRight &&
225 (pressed_buttons_ & WebInputEvent::RightButtonDown)));
226 case WebInputEvent::MouseEnter:
227 return (event.modifiers & WebEventButtonModifierMask()) != 0;
228 case WebInputEvent::MouseLeave:
229 case WebInputEvent::MouseMove: {
230 int event_buttons = (event.modifiers & WebEventButtonModifierMask());
231 return (pressed_buttons_ &&
232 pressed_buttons_ == event_buttons);
233 }
234 default:
235 return false;
236 }
237 return false;
238 }
239
240 int ExternalDragTracker::WebEventButtonModifierMask() {
241 return WebInputEvent::LeftButtonDown |
242 WebInputEvent::RightButtonDown |
243 WebInputEvent::MiddleButtonDown;
244 }
245
246 #pragma mark -
247 #pragma mark Core WebPluginDelegate implementation
248
249 WebPluginDelegateImpl::WebPluginDelegateImpl(
250 gfx::PluginWindowHandle containing_view,
251 NPAPI::PluginInstance *instance)
252 : windowed_handle_(NULL),
253 // all Mac plugins are "windowless" in the Windows/X11 sense
254 windowless_(true),
255 plugin_(NULL),
256 instance_(instance),
257 parent_(containing_view),
258 quirks_(0),
259 buffer_context_(NULL),
260 layer_(nil),
261 surface_(NULL),
262 renderer_(nil),
263 containing_window_has_focus_(false),
264 initial_window_focus_(false),
265 container_is_visible_(false),
266 have_called_set_window_(false),
267 ime_enabled_(false),
268 external_drag_tracker_(new ExternalDragTracker()),
269 handle_event_depth_(0),
270 first_set_window_call_(true),
271 plugin_has_focus_(false),
272 has_webkit_focus_(false),
273 containing_view_has_focus_(true),
274 creation_succeeded_(false) {
275 memset(&window_, 0, sizeof(window_));
276 #ifndef NP_NO_CARBON
277 memset(&np_cg_context_, 0, sizeof(np_cg_context_));
278 #endif
279 #ifndef NP_NO_QUICKDRAW
280 memset(&qd_port_, 0, sizeof(qd_port_));
281 #endif
282 instance->set_windowless(true);
283 }
284
285 WebPluginDelegateImpl::~WebPluginDelegateImpl() {
286 DestroyInstance();
287
288 #ifndef NP_NO_CARBON
289 if (np_cg_context_.window) {
290 CarbonPluginWindowTracker::SharedInstance()->DestroyDummyWindowForDelegate(
291 this, reinterpret_cast<WindowRef>(np_cg_context_.window));
292 }
293 #endif
294 }
295
296 bool WebPluginDelegateImpl::PlatformInitialize() {
297 // Don't set a NULL window handle on destroy for Mac plugins. This matches
298 // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
299 // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
300 // destroyPlugin in WebNetscapePluginView.mm, for examples).
301 quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
302
303 // Mac plugins don't expect to be unloaded, and they don't always do so
304 // cleanly, so don't unload them at shutdown.
305 instance()->plugin_lib()->PreventLibraryUnload();
306
307 #ifndef NP_NO_QUICKDRAW
308 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
309 // For some QuickDraw plugins, we can sometimes get away with giving them
310 // a port pointing to a pixel buffer instead of a our actual dummy window.
311 // This gives us much better frame rates, because the window scraping we
312 // normally use is very slow.
313 // This breaks down if the plugin does anything complicated with the port
314 // (as QuickTime seems to during event handling, and sometimes when painting
315 // its controls), so we switch on the fly as necessary. (It might be
316 // possible to interpose sufficiently that we wouldn't have to switch back
317 // and forth, but the current approach gets us most of the benefit.)
318 // We can't do this at all with plugins that bypass the port entirely and
319 // attaches their own surface to the window.
320 // TODO(stuartmorgan): Test other QuickDraw plugins that we support and
321 // see if any others can use the fast path.
322 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
323 if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
324 quirks_ |= PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH;
325 }
326 #endif
327
328 #ifndef NP_NO_CARBON
329 if (instance()->event_model() == NPEventModelCarbon) {
330 // Create a stand-in for the browser window so that the plugin will have
331 // a non-NULL WindowRef to which it can refer.
332 CarbonPluginWindowTracker* window_tracker =
333 CarbonPluginWindowTracker::SharedInstance();
334 np_cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this);
335 np_cg_context_.context = NULL;
336 UpdateDummyWindowBounds(gfx::Point(0, 0));
337 }
338 #endif
339
340 NPDrawingModel drawing_model = instance()->drawing_model();
341 switch (drawing_model) {
342 #ifndef NP_NO_QUICKDRAW
343 case NPDrawingModelQuickDraw:
344 if (instance()->event_model() != NPEventModelCarbon)
345 return false;
346 qd_manager_.reset(new QuickDrawDrawingManager());
347 qd_manager_->SetPluginWindow(
348 reinterpret_cast<WindowRef>(np_cg_context_.window));
349 qd_port_.port = qd_manager_->port();
350 window_.window = &qd_port_;
351 window_.type = NPWindowTypeDrawable;
352 break;
353 #endif
354 case NPDrawingModelCoreGraphics:
355 #ifndef NP_NO_CARBON
356 if (instance()->event_model() == NPEventModelCarbon)
357 window_.window = &np_cg_context_;
358 #endif
359 window_.type = NPWindowTypeDrawable;
360 break;
361 case NPDrawingModelCoreAnimation:
362 case NPDrawingModelInvalidatingCoreAnimation: {
363 if (instance()->event_model() != NPEventModelCocoa)
364 return false;
365 window_.type = NPWindowTypeDrawable;
366 // Ask the plug-in for the CALayer it created for rendering content.
367 // Create a surface to host it, and request a "window" handle to identify
368 // the surface.
369 CALayer* layer = nil;
370 NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
371 reinterpret_cast<void*>(&layer));
372 if (!err) {
373 if (drawing_model == NPDrawingModelCoreAnimation) {
374 // Create the timer; it will be started when we get a window handle.
375 redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>);
376 }
377 layer_ = layer;
378 surface_ = plugin_->GetAcceleratedSurface();
379
380 // If surface initialization fails for some reason, just continue
381 // without any drawing; returning false would be a more confusing user
382 // experience (since it triggers a missing plugin placeholder).
383 if (surface_->context()) {
384 renderer_ = [[CARenderer rendererWithCGLContext:surface_->context()
385 options:NULL] retain];
386 [renderer_ setLayer:layer_];
387 }
388 plugin_->BindFakePluginWindowHandle(false);
389 }
390 break;
391 }
392 default:
393 NOTREACHED();
394 break;
395 }
396
397 // Let the WebPlugin know that we are windowless (unless this is a
398 // Core Animation plugin, in which case BindFakePluginWindowHandle will take
399 // care of setting up the appropriate window handle).
400 if (!layer_)
401 plugin_->SetWindow(NULL);
402
403 #ifndef NP_NO_CARBON
404 // If the plugin wants Carbon events, hook up to the source of idle events.
405 if (instance()->event_model() == NPEventModelCarbon)
406 UpdateIdleEventRate();
407 #endif
408
409 // QuickTime (in QD mode only) can crash if it gets other calls (e.g.,
410 // NPP_Write) before it gets a SetWindow call, so call SetWindow (with a 0x0
411 // rect) immediately.
412 #ifndef NP_NO_QUICKDRAW
413 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
414 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
415 if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
416 WindowlessSetWindow();
417 }
418 #endif
419
420 return true;
421 }
422
423 void WebPluginDelegateImpl::PlatformDestroyInstance() {
424 #ifndef NP_NO_CARBON
425 if (instance()->event_model() == NPEventModelCarbon)
426 CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
427 #endif
428 if (redraw_timer_.get())
429 redraw_timer_->Stop();
430 [renderer_ release];
431 renderer_ = nil;
432 layer_ = nil;
433 }
434
435 void WebPluginDelegateImpl::UpdateGeometryAndContext(
436 const gfx::Rect& window_rect, const gfx::Rect& clip_rect,
437 CGContextRef context) {
438 buffer_context_ = context;
439 #ifndef NP_NO_CARBON
440 if (instance()->event_model() == NPEventModelCarbon) {
441 // Update the structure that is passed to Carbon+CoreGraphics plugins in
442 // NPP_SetWindow before calling UpdateGeometry, since that will trigger an
443 // NPP_SetWindow call if the geometry changes (which is the only time the
444 // context would be different), and some plugins (e.g., Flash) have an
445 // internal cache of the context that they only update when NPP_SetWindow
446 // is called.
447 np_cg_context_.context = context;
448 }
449 #endif
450 #ifndef NP_NO_QUICKDRAW
451 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
452 qd_manager_->SetTargetContext(context, window_rect.size());
453 #endif
454 UpdateGeometry(window_rect, clip_rect);
455 }
456
457 void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
458 WindowlessPaint(context, rect);
459
460 #ifndef NP_NO_QUICKDRAW
461 // Paint events are our cue to dump the current plugin bits into the buffer
462 // context if we are dealing with a QuickDraw plugin.
463 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
464 qd_manager_->UpdateContext();
465 }
466 #endif
467 }
468
469 void WebPluginDelegateImpl::Print(CGContextRef context) {
470 NOTIMPLEMENTED();
471 }
472
473 bool WebPluginDelegateImpl::PlatformHandleInputEvent(
474 const WebInputEvent& event, WebCursorInfo* cursor_info) {
475 DCHECK(cursor_info != NULL);
476
477 // If we get an event before we've set up the plugin, bail.
478 if (!have_called_set_window_)
479 return false;
480 #ifndef NP_NO_CARBON
481 if (instance()->event_model() == NPEventModelCarbon &&
482 !np_cg_context_.context) {
483 return false;
484 }
485 #endif
486
487 if (WebInputEvent::isMouseEventType(event.type) ||
488 event.type == WebInputEvent::MouseWheel) {
489 // Check our plugin location before we send the event to the plugin, just
490 // in case we somehow missed a plugin frame change.
491 const WebMouseEvent* mouse_event =
492 static_cast<const WebMouseEvent*>(&event);
493 gfx::Point content_origin(
494 mouse_event->globalX - mouse_event->x - window_rect_.x(),
495 mouse_event->globalY - mouse_event->y - window_rect_.y());
496 if (content_origin.x() != content_area_origin_.x() ||
497 content_origin.y() != content_area_origin_.y()) {
498 DLOG(WARNING) << "Stale plugin content area location: "
499 << content_area_origin_ << " instead of "
500 << content_origin;
501 SetContentAreaOrigin(content_origin);
502 }
503
504 current_windowless_cursor_.GetCursorInfo(cursor_info);
505 }
506
507 #ifndef NP_NO_CARBON
508 if (instance()->event_model() == NPEventModelCarbon) {
509 #ifndef NP_NO_QUICKDRAW
510 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
511 if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) {
512 // Mouse event handling doesn't work correctly in the fast path mode,
513 // so any time we get a mouse event turn the fast path off, but set a
514 // time to switch it on again (we don't rely just on MouseLeave because
515 // we don't want poor performance in the case of clicking the play
516 // button and then leaving the mouse there).
517 // This isn't perfect (specifically, click-and-hold doesn't seem to work
518 // if the fast path is on), but the slight regression is worthwhile
519 // for the improved framerates.
520 if (WebInputEvent::isMouseEventType(event.type)) {
521 if (event.type == WebInputEvent::MouseLeave) {
522 SetQuickDrawFastPathEnabled(true);
523 } else {
524 SetQuickDrawFastPathEnabled(false);
525 }
526 // Make sure the plugin wasn't destroyed during the switch.
527 if (!instance())
528 return false;
529 }
530 }
531
532 qd_manager_->MakePortCurrent();
533 }
534 #endif
535
536 if (event.type == WebInputEvent::MouseMove) {
537 return true; // The recurring FireIdleEvent will send null events.
538 }
539 }
540 #endif
541
542 ScopedActiveDelegate active_delegate(this);
543
544 // Create the plugin event structure.
545 NPEventModel event_model = instance()->event_model();
546 scoped_ptr<PluginWebEventConverter> event_converter(
547 PluginWebEventConverterFactory::CreateConverterForModel(event_model));
548 if (!(event_converter.get() && event_converter->InitWithEvent(event))) {
549 // Silently consume any keyboard event types that we don't handle, so that
550 // they don't fall through to the page.
551 if (WebInputEvent::isKeyboardEventType(event.type))
552 return true;
553 return false;
554 }
555 void* plugin_event = event_converter->plugin_event();
556
557 if (instance()->event_model() == NPEventModelCocoa) {
558 // We recieve events related to drags starting outside the plugin, but the
559 // NPAPI Cocoa event model spec says plugins shouldn't receive them, so
560 // filter them out.
561 // If we add a page capture mode at the WebKit layer (like the plugin
562 // capture mode that handles drags starting inside) this can be removed.
563 bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event);
564 external_drag_tracker_->UpdateDragStateFromEvent(event);
565 if (drag_related) {
566 if (event.type == WebInputEvent::MouseUp &&
567 !external_drag_tracker_->IsDragInProgress()) {
568 // When an external drag ends, we need to synthesize a MouseEntered.
569 NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event));
570 enter_event.type = NPCocoaEventMouseEntered;
571 NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &enter_event);
572 instance()->NPP_HandleEvent(&enter_event);
573 }
574 return false;
575 }
576 }
577
578 // Send the plugin the event.
579 scoped_ptr<NPAPI::ScopedCurrentPluginEvent> event_scope(NULL);
580 if (instance()->event_model() == NPEventModelCocoa) {
581 event_scope.reset(new NPAPI::ScopedCurrentPluginEvent(
582 instance(), static_cast<NPCocoaEvent*>(plugin_event)));
583 }
584 int16_t handle_response = instance()->NPP_HandleEvent(plugin_event);
585 bool handled = handle_response != kNPEventNotHandled;
586
587 if (handled && event.type == WebInputEvent::KeyDown) {
588 // Update IME state as requested by the plugin.
589 SetImeEnabled(handle_response == kNPEventStartIME);
590 }
591
592 // Plugins don't give accurate information about whether or not they handled
593 // events, so browsers on the Mac ignore the return value.
594 // Scroll events are the exception, since the Cocoa spec defines a meaning
595 // for the return value.
596 if (WebInputEvent::isMouseEventType(event.type)) {
597 handled = true;
598 } else if (WebInputEvent::isKeyboardEventType(event.type)) {
599 // For Command-key events, trust the return value since eating all menu
600 // shortcuts is not ideal.
601 // TODO(stuartmorgan): Implement the advanced key handling spec, and trust
602 // trust the key event return value from plugins that implement it.
603 if (!(event.modifiers & WebInputEvent::MetaKey))
604 handled = true;
605 }
606
607 return handled;
608 }
609
610 void WebPluginDelegateImpl::InstallMissingPlugin() {
611 NOTIMPLEMENTED();
612 }
613
614 #pragma mark -
615
616 void WebPluginDelegateImpl::WindowlessUpdateGeometry(
617 const gfx::Rect& window_rect,
618 const gfx::Rect& clip_rect) {
619 gfx::Rect old_clip_rect = clip_rect_;
620 cached_clip_rect_ = clip_rect;
621 if (container_is_visible_) // Remove check when cached_clip_rect_ is removed.
622 clip_rect_ = clip_rect;
623 bool clip_rect_changed = (clip_rect_ != old_clip_rect);
624 bool window_size_changed = (window_rect.size() != window_rect_.size());
625
626 bool force_set_window = false;
627 #ifndef NP_NO_QUICKDRAW
628 // In a QuickDraw plugin, a geometry update might have caused a port change;
629 // if so, we need to call SetWindow even if nothing else changed.
630 if (qd_manager_.get() && (qd_port_.port != qd_manager_->port())) {
631 qd_port_.port = qd_manager_->port();
632 force_set_window = true;
633 }
634 #endif
635
636 if (window_rect == window_rect_ && !clip_rect_changed && !force_set_window)
637 return;
638
639 if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
640 PluginVisibilityChanged();
641 }
642
643 SetPluginRect(window_rect);
644
645 #ifndef NP_NO_QUICKDRAW
646 if (window_size_changed && qd_manager_.get() &&
647 qd_manager_->IsFastPathEnabled()) {
648 // If the window size has changed, we need to turn off the fast path so that
649 // the full redraw goes to the window and we get a correct baseline paint.
650 SetQuickDrawFastPathEnabled(false);
651 return; // SetQuickDrawFastPathEnabled will call SetWindow for us.
652 }
653 #endif
654
655 if (window_size_changed || clip_rect_changed || force_set_window)
656 WindowlessSetWindow();
657 }
658
659 void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
660 const gfx::Rect& damage_rect) {
661 // If we get a paint event before we are completely set up (e.g., a nested
662 // call while the plugin is still in NPP_SetWindow), bail.
663 if (!have_called_set_window_ || !buffer_context_)
664 return;
665 DCHECK(buffer_context_ == context);
666
667 static base::StatsRate plugin_paint("Plugin.Paint");
668 base::StatsScope<base::StatsRate> scope(plugin_paint);
669
670 // Plugin invalidates trigger asynchronous paints with the original
671 // invalidation rect; the plugin may be resized before the paint is handled,
672 // so we need to ensure that the damage rect is still sane.
673 const gfx::Rect paint_rect(damage_rect.Intersect(
674 gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));
675
676 ScopedActiveDelegate active_delegate(this);
677
678 #ifndef NP_NO_QUICKDRAW
679 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
680 qd_manager_->MakePortCurrent();
681 #endif
682
683 CGContextSaveGState(context);
684
685 switch (instance()->event_model()) {
686 #ifndef NP_NO_CARBON
687 case NPEventModelCarbon: {
688 NPEvent paint_event = { 0 };
689 paint_event.what = updateEvt;
690 paint_event.message = reinterpret_cast<uint32>(np_cg_context_.window);
691 paint_event.when = TickCount();
692 instance()->NPP_HandleEvent(&paint_event);
693 break;
694 }
695 #endif
696 case NPEventModelCocoa: {
697 NPCocoaEvent paint_event;
698 memset(&paint_event, 0, sizeof(NPCocoaEvent));
699 paint_event.type = NPCocoaEventDrawRect;
700 paint_event.data.draw.context = context;
701 paint_event.data.draw.x = paint_rect.x();
702 paint_event.data.draw.y = paint_rect.y();
703 paint_event.data.draw.width = paint_rect.width();
704 paint_event.data.draw.height = paint_rect.height();
705 instance()->NPP_HandleEvent(&paint_event);
706 break;
707 }
708 }
709
710 // The backing buffer can change during the call to NPP_HandleEvent, in which
711 // case the old context is (or is about to be) invalid.
712 if (context == buffer_context_)
713 CGContextRestoreGState(context);
714 }
715
716 void WebPluginDelegateImpl::WindowlessSetWindow() {
717 if (!instance())
718 return;
719
720 window_.x = 0;
721 window_.y = 0;
722 window_.height = window_rect_.height();
723 window_.width = window_rect_.width();
724 window_.clipRect.left = clip_rect_.x();
725 window_.clipRect.top = clip_rect_.y();
726 window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
727 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
728
729 NPError err = instance()->NPP_SetWindow(&window_);
730
731 // Send an appropriate window focus event after the first SetWindow.
732 if (!have_called_set_window_) {
733 have_called_set_window_ = true;
734 SetWindowHasFocus(initial_window_focus_);
735 }
736
737 #ifndef NP_NO_QUICKDRAW
738 if ((quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) &&
739 !qd_manager_->IsFastPathEnabled() && !clip_rect_.IsEmpty()) {
740 // Give the plugin a few seconds to stabilize so we get a good initial paint
741 // to use as a baseline, then switch to the fast path.
742 fast_path_enable_tick_ = base::TimeTicks::Now() +
743 base::TimeDelta::FromSeconds(3);
744 }
745 #endif
746
747 DCHECK(err == NPERR_NO_ERROR);
748 }
749
750 #pragma mark -
751
752 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
753 NOTREACHED();
754 return false;
755 }
756
757 void WebPluginDelegateImpl::WindowedDestroyWindow() {
758 NOTREACHED();
759 }
760
761 bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
762 const gfx::Rect& clip_rect) {
763 NOTREACHED();
764 return false;
765 }
766
767 void WebPluginDelegateImpl::WindowedSetWindow() {
768 NOTREACHED();
769 }
770
771 #pragma mark -
772 #pragma mark Mac Extensions
773
774 void WebPluginDelegateImpl::PluginDidInvalidate() {
775 if (instance()->drawing_model() == NPDrawingModelInvalidatingCoreAnimation)
776 DrawLayerInSurface();
777 }
778
779 WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
780 return g_active_delegate;
781 }
782
783 void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
784 // If we get a window focus event before calling SetWindow, just remember the
785 // states (WindowlessSetWindow will then send it on the first call).
786 if (!have_called_set_window_) {
787 initial_window_focus_ = has_focus;
788 return;
789 }
790
791 if (has_focus == containing_window_has_focus_)
792 return;
793 containing_window_has_focus_ = has_focus;
794
795 if (!has_focus)
796 SetImeEnabled(false);
797
798 #ifndef NP_NO_QUICKDRAW
799 // Make sure controls repaint with the correct look.
800 if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH)
801 SetQuickDrawFastPathEnabled(false);
802 #endif
803
804 ScopedActiveDelegate active_delegate(this);
805 switch (instance()->event_model()) {
806 #ifndef NP_NO_CARBON
807 case NPEventModelCarbon: {
808 NPEvent focus_event = { 0 };
809 focus_event.what = activateEvt;
810 if (has_focus)
811 focus_event.modifiers |= activeFlag;
812 focus_event.message =
813 reinterpret_cast<unsigned long>(np_cg_context_.window);
814 focus_event.when = TickCount();
815 instance()->NPP_HandleEvent(&focus_event);
816 break;
817 }
818 #endif
819 case NPEventModelCocoa: {
820 NPCocoaEvent focus_event;
821 memset(&focus_event, 0, sizeof(focus_event));
822 focus_event.type = NPCocoaEventWindowFocusChanged;
823 focus_event.data.focus.hasFocus = has_focus;
824 instance()->NPP_HandleEvent(&focus_event);
825 break;
826 }
827 }
828 }
829
830 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
831 if (!have_called_set_window_)
832 return false;
833
834 if (!focused)
835 SetImeEnabled(false);
836
837 ScopedActiveDelegate active_delegate(this);
838
839 switch (instance()->event_model()) {
840 #ifndef NP_NO_CARBON
841 case NPEventModelCarbon: {
842 NPEvent focus_event = { 0 };
843 if (focused)
844 focus_event.what = NPEventType_GetFocusEvent;
845 else
846 focus_event.what = NPEventType_LoseFocusEvent;
847 focus_event.when = TickCount();
848 instance()->NPP_HandleEvent(&focus_event);
849 break;
850 }
851 #endif
852 case NPEventModelCocoa: {
853 NPCocoaEvent focus_event;
854 memset(&focus_event, 0, sizeof(focus_event));
855 focus_event.type = NPCocoaEventFocusChanged;
856 focus_event.data.focus.hasFocus = focused;
857 instance()->NPP_HandleEvent(&focus_event);
858 break;
859 }
860 }
861 return true;
862 }
863
864 void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
865 if (is_visible == container_is_visible_)
866 return;
867 container_is_visible_ = is_visible;
868
869 // TODO(stuartmorgan): This is a temporary workarond for
870 // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
871 // should all be removed.
872 if (is_visible) {
873 clip_rect_ = cached_clip_rect_;
874 } else {
875 clip_rect_.set_width(0);
876 clip_rect_.set_height(0);
877 }
878
879 // If the plugin is changing visibility, let the plugin know. If it's scrolled
880 // off screen (i.e., cached_clip_rect_ is empty), then container visibility
881 // doesn't change anything.
882 if (!cached_clip_rect_.IsEmpty()) {
883 PluginVisibilityChanged();
884 WindowlessSetWindow();
885 }
886
887 // When the plugin become visible, send an empty invalidate. If there were any
888 // pending invalidations this will trigger a paint event for the damaged
889 // region, and if not it's a no-op. This is necessary since higher levels
890 // that would normally do this weren't responsible for the clip_rect_ change).
891 if (!clip_rect_.IsEmpty())
892 instance()->webplugin()->InvalidateRect(gfx::Rect());
893 }
894
895 void WebPluginDelegateImpl::WindowFrameChanged(const gfx::Rect& window_frame,
896 const gfx::Rect& view_frame) {
897 instance()->set_window_frame(window_frame);
898 SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
899 }
900
901 void WebPluginDelegateImpl::ImeCompositionConfirmed(const string16& text) {
902 if (instance()->event_model() != NPEventModelCocoa) {
903 DLOG(ERROR) << "IME text receieved in Carbon event model";
904 return;
905 }
906
907 NPCocoaEvent text_event;
908 memset(&text_event, 0, sizeof(NPCocoaEvent));
909 text_event.type = NPCocoaEventTextInput;
910 text_event.data.text.text =
911 reinterpret_cast<NPNSString*>(base::SysUTF16ToNSString(text));
912 instance()->NPP_HandleEvent(&text_event);
913 }
914
915 void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
916 current_windowless_cursor_.InitFromThemeCursor(cursor);
917 }
918
919 void WebPluginDelegateImpl::SetCursor(const Cursor* cursor) {
920 current_windowless_cursor_.InitFromCursor(cursor);
921 }
922
923 void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
924 current_windowless_cursor_.InitFromNSCursor(cursor);
925 }
926
927 #pragma mark -
928 #pragma mark Internal Tracking
929
930 void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
931 bool plugin_size_changed = rect.width() != window_rect_.width() ||
932 rect.height() != window_rect_.height();
933 window_rect_ = rect;
934 PluginScreenLocationChanged();
935 if (plugin_size_changed)
936 UpdateAcceleratedSurface();
937 }
938
939 void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
940 content_area_origin_ = origin;
941 PluginScreenLocationChanged();
942 }
943
944 void WebPluginDelegateImpl::PluginScreenLocationChanged() {
945 gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
946 content_area_origin_.y() + window_rect_.y());
947 instance()->set_plugin_origin(plugin_origin);
948
949 #ifndef NP_NO_CARBON
950 if (instance()->event_model() == NPEventModelCarbon) {
951 UpdateDummyWindowBounds(plugin_origin);
952 }
953 #endif
954 }
955
956 void WebPluginDelegateImpl::PluginVisibilityChanged() {
957 #ifndef NP_NO_CARBON
958 if (instance()->event_model() == NPEventModelCarbon)
959 UpdateIdleEventRate();
960 #endif
961 if (instance()->drawing_model() == NPDrawingModelCoreAnimation) {
962 bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
963 if (plugin_visible && !redraw_timer_->IsRunning() && windowed_handle()) {
964 redraw_timer_->Start(
965 base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs),
966 this, &WebPluginDelegateImpl::DrawLayerInSurface);
967 } else if (!plugin_visible) {
968 redraw_timer_->Stop();
969 }
970 }
971 }
972
973 void WebPluginDelegateImpl::SetImeEnabled(bool enabled) {
974 if (instance()->event_model() != NPEventModelCocoa)
975 return;
976 if (enabled == ime_enabled_)
977 return;
978 ime_enabled_ = enabled;
979 plugin_->SetImeEnabled(enabled);
980 }
981
982 #pragma mark -
983 #pragma mark Core Animation Support
984
985 void WebPluginDelegateImpl::DrawLayerInSurface() {
986 // If we haven't plumbed up the surface yet, don't try to draw.
987 if (!windowed_handle() || !renderer_)
988 return;
989
990 [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
991 if (CGRectIsEmpty([renderer_ updateBounds])) {
992 // If nothing has changed, we are done.
993 [renderer_ endFrame];
994 return;
995 }
996
997 surface_->StartDrawing();
998
999 CGRect layerRect = [layer_ bounds];
1000 [renderer_ addUpdateRect:layerRect];
1001 [renderer_ render];
1002 [renderer_ endFrame];
1003
1004 surface_->EndDrawing();
1005 }
1006
1007 // Update the size of the surface to match the current size of the plug-in.
1008 void WebPluginDelegateImpl::UpdateAcceleratedSurface() {
1009 // Will only have a window handle when using a Core Animation drawing model.
1010 if (!windowed_handle() || !layer_)
1011 return;
1012
1013 [CATransaction begin];
1014 [CATransaction setValue:[NSNumber numberWithInt:0]
1015 forKey:kCATransactionAnimationDuration];
1016 [layer_ setFrame:CGRectMake(0, 0,
1017 window_rect_.width(), window_rect_.height())];
1018 [CATransaction commit];
1019
1020 [renderer_ setBounds:[layer_ bounds]];
1021 surface_->SetSize(window_rect_.size());
1022 }
1023
1024 void WebPluginDelegateImpl::set_windowed_handle(
1025 gfx::PluginWindowHandle handle) {
1026 windowed_handle_ = handle;
1027 surface_->SetWindowHandle(handle);
1028 UpdateAcceleratedSurface();
1029 // Kick off the drawing timer, if necessary.
1030 PluginVisibilityChanged();
1031 }
1032
1033 #pragma mark -
1034 #pragma mark Carbon Event support
1035
1036 #ifndef NP_NO_CARBON
1037 void WebPluginDelegateImpl::UpdateDummyWindowBounds(
1038 const gfx::Point& plugin_origin) {
1039 WindowRef window = reinterpret_cast<WindowRef>(np_cg_context_.window);
1040 Rect current_bounds;
1041 GetWindowBounds(window, kWindowContentRgn, &current_bounds);
1042
1043 Rect new_bounds;
1044 // We never want to resize the window to 0x0, so if the plugin is 0x0 just
1045 // move the window without resizing it.
1046 if (window_rect_.width() > 0 && window_rect_.height() > 0) {
1047 SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
1048 OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
1049 } else {
1050 new_bounds = current_bounds;
1051 OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
1052 plugin_origin.y() - current_bounds.top);
1053 }
1054
1055 if (new_bounds.left != current_bounds.left ||
1056 new_bounds.top != current_bounds.top ||
1057 new_bounds.right != current_bounds.right ||
1058 new_bounds.bottom != current_bounds.bottom)
1059 SetWindowBounds(window, kWindowContentRgn, &new_bounds);
1060 }
1061
1062 void WebPluginDelegateImpl::UpdateIdleEventRate() {
1063 bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
1064 CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
1065 plugin_visible);
1066 }
1067
1068 void WebPluginDelegateImpl::FireIdleEvent() {
1069 // Avoid a race condition between IO and UI threads during plugin shutdown
1070 if (!instance())
1071 return;
1072 // Don't send idle events until we've called SetWindow.
1073 if (!have_called_set_window_)
1074 return;
1075
1076 #ifndef NP_NO_QUICKDRAW
1077 // Check whether it's time to turn the QuickDraw fast path back on.
1078 if (!fast_path_enable_tick_.is_null() &&
1079 (base::TimeTicks::Now() > fast_path_enable_tick_)) {
1080 SetQuickDrawFastPathEnabled(true);
1081 fast_path_enable_tick_ = base::TimeTicks();
1082 }
1083 #endif
1084
1085 ScopedActiveDelegate active_delegate(this);
1086
1087 #ifndef NP_NO_QUICKDRAW
1088 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
1089 qd_manager_->MakePortCurrent();
1090 #endif
1091
1092 // Send an idle event so that the plugin can do background work
1093 NPEvent np_event = {0};
1094 np_event.what = nullEvent;
1095 np_event.when = TickCount();
1096 np_event.modifiers = GetCurrentKeyModifiers();
1097 if (!Button())
1098 np_event.modifiers |= btnState;
1099 HIPoint mouse_location;
1100 HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
1101 np_event.where.h = mouse_location.x;
1102 np_event.where.v = mouse_location.y;
1103 instance()->NPP_HandleEvent(&np_event);
1104
1105 #ifndef NP_NO_QUICKDRAW
1106 // Quickdraw-based plugins can draw at any time, so tell the renderer to
1107 // repaint.
1108 if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
1109 instance()->webplugin()->Invalidate();
1110 #endif
1111 }
1112 #endif // !NP_NO_CARBON
1113
1114 #pragma mark -
1115 #pragma mark QuickDraw Support
1116
1117 #ifndef NP_NO_QUICKDRAW
1118 void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) {
1119 if (!enabled) {
1120 // Wait a couple of seconds, then turn the fast path back on. If we're
1121 // turning it off for event handling, that ensures that the common case of
1122 // move-mouse-then-click works (as well as making it likely that a second
1123 // click attempt will work if the first one fails). If we're turning it
1124 // off to force a new baseline image, this leaves plenty of time for the
1125 // plugin to draw.
1126 fast_path_enable_tick_ = base::TimeTicks::Now() +
1127 base::TimeDelta::FromSeconds(2);
1128 }
1129
1130 if (enabled == qd_manager_->IsFastPathEnabled())
1131 return;
1132 if (enabled && clip_rect_.IsEmpty()) {
1133 // Don't switch to the fast path while the plugin is completely clipped;
1134 // we can only switch when the window has an up-to-date image for us to
1135 // scrape. We'll automatically switch after we become visible again.
1136 return;
1137 }
1138
1139 qd_manager_->SetFastPathEnabled(enabled);
1140 qd_port_.port = qd_manager_->port();
1141 WindowlessSetWindow();
1142 // Send a paint event so that the new buffer gets updated immediately.
1143 WindowlessPaint(buffer_context_, clip_rect_);
1144 }
1145 #endif // !NP_NO_QUICKDRAW
OLDNEW
« no previous file with comments | « webkit/glue/plugins/webplugin_delegate_impl_gtk.cc ('k') | webkit/glue/plugins/webplugin_delegate_impl_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698