blob: cfa5f4e256bafc0f2e0de6ad758929ffa8f8c051 [file] [log] [blame]
[email protected]6ce58c8a2011-02-07 16:19:511// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]39b8c5132009-02-11 07:48:222// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]6bf1a812009-07-11 01:57:285#import <Cocoa/Cocoa.h>
[email protected]3d1e89d2010-03-10 20:01:356#import <QuartzCore/QuartzCore.h>
[email protected]6bf1a812009-07-11 01:57:287
[email protected]191eb3f72010-12-21 06:27:508#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
[email protected]39b8c5132009-02-11 07:48:229
10#include <string>
[email protected]27f5a6c82009-11-20 18:26:1611#include <unistd.h>
[email protected]bc384e4a2010-01-21 17:26:2812#include <set>
[email protected]39b8c5132009-02-11 07:48:2213
14#include "base/file_util.h"
[email protected]3b63f8f42011-03-28 01:54:1515#include "base/memory/scoped_ptr.h"
[email protected]39b8c5132009-02-11 07:48:2216#include "base/message_loop.h"
[email protected]835d7c82010-10-14 04:38:3817#include "base/metrics/stats_counters.h"
[email protected]39b8c5132009-02-11 07:48:2218#include "base/string_util.h"
[email protected]81e34d82010-08-19 18:36:2519#include "base/utf_string_conversions.h"
[email protected]b7f75862011-01-21 21:15:1320#include "base/sys_info.h"
[email protected]935d63d2010-10-15 23:31:5521#include "base/sys_string_conversions.h"
[email protected]c1d9cdc2011-01-17 06:50:0122#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
[email protected]39b8c5132009-02-11 07:48:2223#include "webkit/glue/webkit_glue.h"
[email protected]191eb3f72010-12-21 06:27:5024#include "webkit/plugins/npapi/plugin_instance.h"
25#include "webkit/plugins/npapi/plugin_lib.h"
26#include "webkit/plugins/npapi/plugin_list.h"
27#include "webkit/plugins/npapi/plugin_stream_url.h"
28#include "webkit/plugins/npapi/plugin_web_event_converter_mac.h"
29#include "webkit/plugins/npapi/webplugin.h"
30#include "webkit/plugins/npapi/webplugin_accelerated_surface_mac.h"
[email protected]39b8c5132009-02-11 07:48:2231
[email protected]ae40ccd2010-03-02 23:45:2132#ifndef NP_NO_CARBON
[email protected]191eb3f72010-12-21 06:27:5033#include "webkit/plugins/npapi/carbon_plugin_window_tracker_mac.h"
[email protected]ae40ccd2010-03-02 23:45:2134#endif
35
[email protected]760cd2122009-10-13 17:53:2236#ifndef NP_NO_QUICKDRAW
[email protected]191eb3f72010-12-21 06:27:5037#include "webkit/plugins/npapi/quickdraw_drawing_manager_mac.h"
[email protected]760cd2122009-10-13 17:53:2238#endif
39
[email protected]7c51b0ee2009-07-08 21:49:3040using WebKit::WebCursorInfo;
[email protected]41fbf092009-05-22 01:29:0541using WebKit::WebKeyboardEvent;
42using WebKit::WebInputEvent;
43using WebKit::WebMouseEvent;
[email protected]7b032aa62009-11-06 17:08:3644using WebKit::WebMouseWheelEvent;
[email protected]41fbf092009-05-22 01:29:0545
[email protected]6bf1a812009-07-11 01:57:2846// Important implementation notes: The Mac definition of NPAPI, particularly
47// the distinction between windowed and windowless modes, differs from the
48// Windows and Linux definitions. Most of those differences are
49// accomodated by the WebPluginDelegate class.
50
[email protected]191eb3f72010-12-21 06:27:5051namespace webkit {
52namespace npapi {
53
[email protected]39b8c5132009-02-11 07:48:2254namespace {
55
[email protected]191eb3f72010-12-21 06:27:5056const int kCoreAnimationRedrawPeriodMs = 10; // 100 Hz
57
[email protected]b4ec5d92010-01-19 19:24:2458WebPluginDelegateImpl* g_active_delegate;
59
60// Helper to simplify correct usage of g_active_delegate. Instantiating will
61// set the active delegate to |delegate| for the lifetime of the object, then
62// NULL when it goes out of scope.
63class ScopedActiveDelegate {
[email protected]191eb3f72010-12-21 06:27:5064 public:
[email protected]b4ec5d92010-01-19 19:24:2465 explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
66 g_active_delegate = delegate;
67 }
68 ~ScopedActiveDelegate() {
69 g_active_delegate = NULL;
70 }
[email protected]191eb3f72010-12-21 06:27:5071
72 private:
[email protected]b4ec5d92010-01-19 19:24:2473 DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
74};
75
[email protected]bc384e4a2010-01-21 17:26:2876#ifndef NP_NO_CARBON
[email protected]385579b2010-01-21 20:18:4877// Timer periods for sending idle events to Carbon plugins. The visible value
78// (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
79// Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
80// going lower than that causes issues.
81const int kVisibleIdlePeriodMs = 20; // (50Hz)
82const int kHiddenIdlePeriodMs = 125; // (8Hz)
83
[email protected]bc384e4a2010-01-21 17:26:2884class CarbonIdleEventSource {
85 public:
86 // Returns the shared Carbon idle event source.
87 static CarbonIdleEventSource* SharedInstance() {
88 DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
89 static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
90 return event_source;
91 }
92
[email protected]806350632010-02-05 17:39:3693 // Registers the plugin delegate as interested in receiving idle events at
94 // a rate appropriate for the given visibility. A delegate can safely be
95 // re-registered any number of times, with the latest registration winning.
96 void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
97 if (visible) {
98 visible_delegates_->RegisterDelegate(delegate);
99 hidden_delegates_->UnregisterDelegate(delegate);
100 } else {
101 hidden_delegates_->RegisterDelegate(delegate);
102 visible_delegates_->UnregisterDelegate(delegate);
[email protected]bc384e4a2010-01-21 17:26:28103 }
[email protected]bc384e4a2010-01-21 17:26:28104 }
105
106 // Removes the plugin delegate from the list of plugins receiving idle events.
107 void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
[email protected]806350632010-02-05 17:39:36108 visible_delegates_->UnregisterDelegate(delegate);
109 hidden_delegates_->UnregisterDelegate(delegate);
[email protected]bc384e4a2010-01-21 17:26:28110 }
111
112 private:
[email protected]806350632010-02-05 17:39:36113 class VisibilityGroup {
114 public:
115 explicit VisibilityGroup(int timer_period)
116 : timer_period_(timer_period), iterator_(delegates_.end()) {}
[email protected]bc384e4a2010-01-21 17:26:28117
[email protected]806350632010-02-05 17:39:36118 // Adds |delegate| to this visibility group.
119 void RegisterDelegate(WebPluginDelegateImpl* delegate) {
120 if (delegates_.empty()) {
121 timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
122 this, &VisibilityGroup::SendIdleEvents);
123 }
124 delegates_.insert(delegate);
[email protected]bc384e4a2010-01-21 17:26:28125 }
[email protected]bc384e4a2010-01-21 17:26:28126
[email protected]806350632010-02-05 17:39:36127 // Removes |delegate| from this visibility group.
128 void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
129 // If a plugin changes visibility during idle event handling, it
130 // may be removed from this set while SendIdleEvents is still iterating;
131 // if that happens and it's next on the list, increment the iterator
132 // before erasing so that the iteration won't be corrupted.
133 if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
134 ++iterator_;
135 size_t removed = delegates_.erase(delegate);
136 if (removed > 0 && delegates_.empty())
137 timer_.Stop();
138 }
139
140 private:
141 // Fires off idle events for each delegate in the group.
142 void SendIdleEvents() {
143 for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
144 // Pre-increment so that the skip logic in UnregisterDelegates works.
145 WebPluginDelegateImpl* delegate = *(iterator_++);
146 delegate->FireIdleEvent();
147 }
148 }
149
150 int timer_period_;
151 base::RepeatingTimer<VisibilityGroup> timer_;
152 std::set<WebPluginDelegateImpl*> delegates_;
153 std::set<WebPluginDelegateImpl*>::iterator iterator_;
154 };
155
156 CarbonIdleEventSource()
157 : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
158 hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}
159
160 scoped_ptr<VisibilityGroup> visible_delegates_;
161 scoped_ptr<VisibilityGroup> hidden_delegates_;
162
163 DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
[email protected]bc384e4a2010-01-21 17:26:28164};
[email protected]385579b2010-01-21 20:18:48165#endif // !NP_NO_CARBON
[email protected]bc384e4a2010-01-21 17:26:28166
[email protected]39b8c5132009-02-11 07:48:22167} // namespace
168
[email protected]7ba381832010-04-16 15:02:37169// Helper to build and maintain a model of a drag entering the plugin but not
170// starting there. See explanation in PlatformHandleInputEvent.
171class ExternalDragTracker {
172 public:
173 ExternalDragTracker() : pressed_buttons_(0) {}
174
175 // Returns true if an external drag is in progress.
176 bool IsDragInProgress() { return pressed_buttons_ != 0; };
177
178 // Returns true if the given event appears to be related to an external drag.
179 bool EventIsRelatedToDrag(const WebInputEvent& event);
180
181 // Updates the tracking of whether an external drag is in progress--and if
182 // so what buttons it involves--based on the given event.
183 void UpdateDragStateFromEvent(const WebInputEvent& event);
184
185 private:
186 // Returns the mask for just the button state in a WebInputEvent's modifiers.
187 static int WebEventButtonModifierMask();
188
189 // The WebInputEvent modifier flags for any buttons that were down when an
190 // external drag entered the plugin, and which and are still down now.
191 int pressed_buttons_;
192
193 DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
194};
195
196void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
197 switch (event.type) {
198 case WebInputEvent::MouseEnter:
199 pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
200 break;
201 case WebInputEvent::MouseUp: {
202 const WebMouseEvent* mouse_event =
203 static_cast<const WebMouseEvent*>(&event);
204 if (mouse_event->button == WebMouseEvent::ButtonLeft)
205 pressed_buttons_ &= ~WebInputEvent::LeftButtonDown;
206 if (mouse_event->button == WebMouseEvent::ButtonMiddle)
207 pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown;
208 if (mouse_event->button == WebMouseEvent::ButtonRight)
209 pressed_buttons_ &= ~WebInputEvent::RightButtonDown;
210 break;
211 }
212 default:
213 break;
214 }
215}
216
217bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) {
218 const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event);
219 switch (event.type) {
220 case WebInputEvent::MouseUp:
221 // We only care about release of buttons that were part of the drag.
222 return ((mouse_event->button == WebMouseEvent::ButtonLeft &&
223 (pressed_buttons_ & WebInputEvent::LeftButtonDown)) ||
224 (mouse_event->button == WebMouseEvent::ButtonMiddle &&
225 (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) ||
226 (mouse_event->button == WebMouseEvent::ButtonRight &&
227 (pressed_buttons_ & WebInputEvent::RightButtonDown)));
228 case WebInputEvent::MouseEnter:
229 return (event.modifiers & WebEventButtonModifierMask()) != 0;
230 case WebInputEvent::MouseLeave:
231 case WebInputEvent::MouseMove: {
232 int event_buttons = (event.modifiers & WebEventButtonModifierMask());
233 return (pressed_buttons_ &&
234 pressed_buttons_ == event_buttons);
235 }
236 default:
237 return false;
238 }
239 return false;
240}
241
242int ExternalDragTracker::WebEventButtonModifierMask() {
243 return WebInputEvent::LeftButtonDown |
244 WebInputEvent::RightButtonDown |
245 WebInputEvent::MiddleButtonDown;
246}
247
[email protected]3d1e89d2010-03-10 20:01:35248#pragma mark -
[email protected]7ba381832010-04-16 15:02:37249#pragma mark Core WebPluginDelegate implementation
[email protected]3d1e89d2010-03-10 20:01:35250
[email protected]39b8c5132009-02-11 07:48:22251WebPluginDelegateImpl::WebPluginDelegateImpl(
[email protected]700d3d52009-07-07 17:40:46252 gfx::PluginWindowHandle containing_view,
[email protected]191eb3f72010-12-21 06:27:50253 PluginInstance *instance)
[email protected]3d1e89d2010-03-10 20:01:35254 : windowed_handle_(NULL),
[email protected]6bf1a812009-07-11 01:57:28255 // all Mac plugins are "windowless" in the Windows/X11 sense
256 windowless_(true),
[email protected]be215552009-09-21 20:12:56257 plugin_(NULL),
258 instance_(instance),
259 parent_(containing_view),
[email protected]7ba381832010-04-16 15:02:37260 quirks_(0),
[email protected]33f39d52010-03-05 16:09:11261 buffer_context_(NULL),
[email protected]3d1e89d2010-03-10 20:01:35262 layer_(nil),
263 surface_(NULL),
264 renderer_(nil),
[email protected]2f2ba982010-01-21 16:47:08265 containing_window_has_focus_(false),
[email protected]1e6e3c992010-02-08 15:52:13266 initial_window_focus_(false),
[email protected]941e4552010-02-01 21:23:43267 container_is_visible_(false),
268 have_called_set_window_(false),
[email protected]935d63d2010-10-15 23:31:55269 ime_enabled_(false),
[email protected]b7f75862011-01-21 21:15:13270 keyup_ignore_count_(0),
[email protected]7ba381832010-04-16 15:02:37271 external_drag_tracker_(new ExternalDragTracker()),
[email protected]0c860d02010-06-15 22:08:35272 handle_event_depth_(0),
[email protected]784ea1ab2010-09-18 00:02:34273 first_set_window_call_(true),
274 plugin_has_focus_(false),
275 has_webkit_focus_(false),
[email protected]5fd5d032010-11-30 01:01:01276 containing_view_has_focus_(true),
277 creation_succeeded_(false) {
[email protected]39b8c5132009-02-11 07:48:22278 memset(&window_, 0, sizeof(window_));
[email protected]8aff32f2010-01-07 17:16:27279#ifndef NP_NO_CARBON
[email protected]33f39d52010-03-05 16:09:11280 memset(&np_cg_context_, 0, sizeof(np_cg_context_));
[email protected]8aff32f2010-01-07 17:16:27281#endif
[email protected]760cd2122009-10-13 17:53:22282#ifndef NP_NO_QUICKDRAW
283 memset(&qd_port_, 0, sizeof(qd_port_));
284#endif
[email protected]81895a42009-11-11 21:33:12285 instance->set_windowless(true);
[email protected]39b8c5132009-02-11 07:48:22286}
287
288WebPluginDelegateImpl::~WebPluginDelegateImpl() {
[email protected]c1a94432010-02-11 16:17:39289 DestroyInstance();
290
[email protected]8aff32f2010-01-07 17:16:27291#ifndef NP_NO_CARBON
[email protected]33f39d52010-03-05 16:09:11292 if (np_cg_context_.window) {
[email protected]ae40ccd2010-03-02 23:45:21293 CarbonPluginWindowTracker::SharedInstance()->DestroyDummyWindowForDelegate(
[email protected]33f39d52010-03-05 16:09:11294 this, reinterpret_cast<WindowRef>(np_cg_context_.window));
[email protected]8aff32f2010-01-07 17:16:27295 }
[email protected]8aff32f2010-01-07 17:16:27296#endif
[email protected]39b8c5132009-02-11 07:48:22297}
298
[email protected]34110602010-04-06 16:03:08299bool WebPluginDelegateImpl::PlatformInitialize() {
[email protected]81895a42009-11-11 21:33:12300 // Don't set a NULL window handle on destroy for Mac plugins. This matches
301 // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
302 // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
303 // destroyPlugin in WebNetscapePluginView.mm, for examples).
304 quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
[email protected]4d9ef386a2009-11-06 20:13:11305
[email protected]42ed07c2010-04-14 16:11:49306 // Mac plugins don't expect to be unloaded, and they don't always do so
307 // cleanly, so don't unload them at shutdown.
308 instance()->plugin_lib()->PreventLibraryUnload();
[email protected]c18388fb2010-02-11 21:10:25309
[email protected]779b8812010-03-26 18:55:09310#ifndef NP_NO_QUICKDRAW
311 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
312 // For some QuickDraw plugins, we can sometimes get away with giving them
313 // a port pointing to a pixel buffer instead of a our actual dummy window.
314 // This gives us much better frame rates, because the window scraping we
315 // normally use is very slow.
316 // This breaks down if the plugin does anything complicated with the port
317 // (as QuickTime seems to during event handling, and sometimes when painting
318 // its controls), so we switch on the fly as necessary. (It might be
319 // possible to interpose sufficiently that we wouldn't have to switch back
320 // and forth, but the current approach gets us most of the benefit.)
321 // We can't do this at all with plugins that bypass the port entirely and
322 // attaches their own surface to the window.
323 // TODO(stuartmorgan): Test other QuickDraw plugins that we support and
324 // see if any others can use the fast path.
325 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
[email protected]c9d811372010-06-23 21:44:57326 if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
[email protected]779b8812010-03-26 18:55:09327 quirks_ |= PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH;
328 }
329#endif
330
[email protected]8aff32f2010-01-07 17:16:27331#ifndef NP_NO_CARBON
332 if (instance()->event_model() == NPEventModelCarbon) {
333 // Create a stand-in for the browser window so that the plugin will have
334 // a non-NULL WindowRef to which it can refer.
[email protected]ae40ccd2010-03-02 23:45:21335 CarbonPluginWindowTracker* window_tracker =
336 CarbonPluginWindowTracker::SharedInstance();
[email protected]33f39d52010-03-05 16:09:11337 np_cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this);
338 np_cg_context_.context = NULL;
[email protected]f374cebc2010-03-03 23:46:03339 UpdateDummyWindowBounds(gfx::Point(0, 0));
[email protected]8aff32f2010-01-07 17:16:27340 }
341#endif
[email protected]760cd2122009-10-13 17:53:22342
[email protected]51a0bb52010-06-03 21:34:06343 NPDrawingModel drawing_model = instance()->drawing_model();
344 switch (drawing_model) {
[email protected]760cd2122009-10-13 17:53:22345#ifndef NP_NO_QUICKDRAW
346 case NPDrawingModelQuickDraw:
[email protected]34110602010-04-06 16:03:08347 if (instance()->event_model() != NPEventModelCarbon)
348 return false;
[email protected]4491a782010-04-12 15:38:44349 qd_manager_.reset(new QuickDrawDrawingManager());
350 qd_manager_->SetPluginWindow(
351 reinterpret_cast<WindowRef>(np_cg_context_.window));
352 qd_port_.port = qd_manager_->port();
[email protected]760cd2122009-10-13 17:53:22353 window_.window = &qd_port_;
354 window_.type = NPWindowTypeDrawable;
355 break;
356#endif
357 case NPDrawingModelCoreGraphics:
[email protected]8aff32f2010-01-07 17:16:27358#ifndef NP_NO_CARBON
[email protected]227b5432010-01-07 17:56:51359 if (instance()->event_model() == NPEventModelCarbon)
[email protected]33f39d52010-03-05 16:09:11360 window_.window = &np_cg_context_;
[email protected]8aff32f2010-01-07 17:16:27361#endif
[email protected]760cd2122009-10-13 17:53:22362 window_.type = NPWindowTypeDrawable;
363 break;
[email protected]51a0bb52010-06-03 21:34:06364 case NPDrawingModelCoreAnimation:
365 case NPDrawingModelInvalidatingCoreAnimation: {
[email protected]34110602010-04-06 16:03:08366 if (instance()->event_model() != NPEventModelCocoa)
367 return false;
[email protected]3d1e89d2010-03-10 20:01:35368 window_.type = NPWindowTypeDrawable;
[email protected]8b4755f2010-10-01 18:04:06369 // Ask the plug-in for the CALayer it created for rendering content.
370 // Create a surface to host it, and request a "window" handle to identify
371 // the surface.
[email protected]3d1e89d2010-03-10 20:01:35372 CALayer* layer = nil;
373 NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
374 reinterpret_cast<void*>(&layer));
375 if (!err) {
[email protected]51a0bb52010-06-03 21:34:06376 if (drawing_model == NPDrawingModelCoreAnimation) {
377 // Create the timer; it will be started when we get a window handle.
378 redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>);
379 }
[email protected]3d1e89d2010-03-10 20:01:35380 layer_ = layer;
[email protected]8b4755f2010-10-01 18:04:06381 surface_ = plugin_->GetAcceleratedSurface();
382
[email protected]d92bec42010-11-22 22:49:34383 // If surface initialization fails for some reason, just continue
384 // without any drawing; returning false would be a more confusing user
385 // experience (since it triggers a missing plugin placeholder).
[email protected]c651f6e2011-02-24 09:19:48386 if (surface_ && surface_->context()) {
[email protected]d92bec42010-11-22 22:49:34387 renderer_ = [[CARenderer rendererWithCGLContext:surface_->context()
388 options:NULL] retain];
389 [renderer_ setLayer:layer_];
390 }
[email protected]4736973b2010-04-28 17:56:42391 plugin_->BindFakePluginWindowHandle(false);
[email protected]3d1e89d2010-03-10 20:01:35392 }
393 break;
394 }
[email protected]760cd2122009-10-13 17:53:22395 default:
396 NOTREACHED();
397 break;
398 }
[email protected]6ef2d78d2009-09-02 23:49:05399
[email protected]ee48d172010-03-18 17:30:52400 // Let the WebPlugin know that we are windowless (unless this is a
401 // Core Animation plugin, in which case BindFakePluginWindowHandle will take
402 // care of setting up the appropriate window handle).
[email protected]23607ccf2010-06-03 23:15:33403 if (!layer_)
[email protected]ee48d172010-03-18 17:30:52404 plugin_->SetWindow(NULL);
405
[email protected]8aff32f2010-01-07 17:16:27406#ifndef NP_NO_CARBON
[email protected]bc384e4a2010-01-21 17:26:28407 // If the plugin wants Carbon events, hook up to the source of idle events.
408 if (instance()->event_model() == NPEventModelCarbon)
[email protected]385579b2010-01-21 20:18:48409 UpdateIdleEventRate();
[email protected]8aff32f2010-01-07 17:16:27410#endif
[email protected]0a46d192010-02-26 00:16:21411
[email protected]cbc02a62010-03-30 15:10:18412 // QuickTime (in QD mode only) can crash if it gets other calls (e.g.,
413 // NPP_Write) before it gets a SetWindow call, so call SetWindow (with a 0x0
414 // rect) immediately.
415#ifndef NP_NO_QUICKDRAW
416 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
417 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
[email protected]c9d811372010-06-23 21:44:57418 if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
[email protected]bc5fc9f2010-04-15 14:47:41419 WindowlessSetWindow();
[email protected]cbc02a62010-03-30 15:10:18420 }
421#endif
[email protected]34110602010-04-06 16:03:08422
423 return true;
[email protected]459c7002009-08-26 01:31:20424}
[email protected]f103ab72009-09-02 17:10:59425
[email protected]459c7002009-08-26 01:31:20426void WebPluginDelegateImpl::PlatformDestroyInstance() {
[email protected]23563dc1e2010-02-16 16:11:00427#ifndef NP_NO_CARBON
[email protected]bc384e4a2010-01-21 17:26:28428 if (instance()->event_model() == NPEventModelCarbon)
429 CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
[email protected]23563dc1e2010-02-16 16:11:00430#endif
[email protected]3d1e89d2010-03-10 20:01:35431 if (redraw_timer_.get())
432 redraw_timer_->Stop();
433 [renderer_ release];
434 renderer_ = nil;
435 layer_ = nil;
[email protected]6ef2d78d2009-09-02 23:49:05436}
437
[email protected]2b5b8ba2010-03-06 15:32:44438void WebPluginDelegateImpl::UpdateGeometryAndContext(
439 const gfx::Rect& window_rect, const gfx::Rect& clip_rect,
440 CGContextRef context) {
[email protected]33f39d52010-03-05 16:09:11441 buffer_context_ = context;
[email protected]8aff32f2010-01-07 17:16:27442#ifndef NP_NO_CARBON
[email protected]2b5b8ba2010-03-06 15:32:44443 if (instance()->event_model() == NPEventModelCarbon) {
444 // Update the structure that is passed to Carbon+CoreGraphics plugins in
445 // NPP_SetWindow before calling UpdateGeometry, since that will trigger an
446 // NPP_SetWindow call if the geometry changes (which is the only time the
447 // context would be different), and some plugins (e.g., Flash) have an
448 // internal cache of the context that they only update when NPP_SetWindow
449 // is called.
[email protected]33f39d52010-03-05 16:09:11450 np_cg_context_.context = context;
[email protected]278429b2009-08-13 17:56:55451 }
[email protected]8aff32f2010-01-07 17:16:27452#endif
[email protected]4491a782010-04-12 15:38:44453#ifndef NP_NO_QUICKDRAW
454 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
455 qd_manager_->SetTargetContext(context, window_rect.size());
456#endif
[email protected]2b5b8ba2010-03-06 15:32:44457 UpdateGeometry(window_rect, clip_rect);
[email protected]278429b2009-08-13 17:56:55458}
459
[email protected]39b8c5132009-02-11 07:48:22460void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
[email protected]d5a8a422009-07-29 20:27:09461 WindowlessPaint(context, rect);
[email protected]33f39d52010-03-05 16:09:11462
463#ifndef NP_NO_QUICKDRAW
[email protected]4491a782010-04-12 15:38:44464 // Paint events are our cue to dump the current plugin bits into the buffer
465 // context if we are dealing with a QuickDraw plugin.
[email protected]33f39d52010-03-05 16:09:11466 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
[email protected]4491a782010-04-12 15:38:44467 qd_manager_->UpdateContext();
[email protected]33f39d52010-03-05 16:09:11468 }
469#endif
[email protected]39b8c5132009-02-11 07:48:22470}
471
472void WebPluginDelegateImpl::Print(CGContextRef context) {
[email protected]7b032aa62009-11-06 17:08:36473 NOTIMPLEMENTED();
[email protected]39b8c5132009-02-11 07:48:22474}
475
[email protected]d7e2df12010-02-11 23:25:26476bool WebPluginDelegateImpl::PlatformHandleInputEvent(
477 const WebInputEvent& event, WebCursorInfo* cursor_info) {
478 DCHECK(cursor_info != NULL);
[email protected]39b8c5132009-02-11 07:48:22479
[email protected]32c0554d2010-04-05 20:57:17480 // If we get an event before we've set up the plugin, bail.
[email protected]33f39d52010-03-05 16:09:11481 if (!have_called_set_window_)
482 return false;
[email protected]673590a2010-01-26 01:40:02483#ifndef NP_NO_CARBON
484 if (instance()->event_model() == NPEventModelCarbon &&
[email protected]33f39d52010-03-05 16:09:11485 !np_cg_context_.context) {
[email protected]673590a2010-01-26 01:40:02486 return false;
487 }
488#endif
[email protected]d5a8a422009-07-29 20:27:09489
[email protected]6c9c89b2010-03-30 18:02:11490 if (WebInputEvent::isMouseEventType(event.type) ||
491 event.type == WebInputEvent::MouseWheel) {
[email protected]1e6e3c992010-02-08 15:52:13492 // Check our plugin location before we send the event to the plugin, just
493 // in case we somehow missed a plugin frame change.
[email protected]0aa54d62010-01-12 18:31:00494 const WebMouseEvent* mouse_event =
495 static_cast<const WebMouseEvent*>(&event);
[email protected]1e6e3c992010-02-08 15:52:13496 gfx::Point content_origin(
497 mouse_event->globalX - mouse_event->x - window_rect_.x(),
498 mouse_event->globalY - mouse_event->y - window_rect_.y());
499 if (content_origin.x() != content_area_origin_.x() ||
500 content_origin.y() != content_area_origin_.y()) {
[email protected]f42e1ec32010-04-12 14:54:55501 DLOG(WARNING) << "Stale plugin content area location: "
502 << content_area_origin_ << " instead of "
503 << content_origin;
504 SetContentAreaOrigin(content_origin);
[email protected]1e6e3c992010-02-08 15:52:13505 }
[email protected]997a47a2010-01-15 21:51:19506
[email protected]d7e2df12010-02-11 23:25:26507 current_windowless_cursor_.GetCursorInfo(cursor_info);
[email protected]0aa54d62010-01-12 18:31:00508 }
509
[email protected]b7f75862011-01-21 21:15:13510 // Per the Cocoa Plugin IME spec, plugins shoudn't receive keydown or keyup
511 // events while composition is in progress. Treat them as handled, however,
512 // since IME is consuming them on behalf of the plugin.
513 if ((event.type == WebInputEvent::KeyDown && ime_enabled_) ||
514 (event.type == WebInputEvent::KeyUp && keyup_ignore_count_)) {
515 // Composition ends on a keydown, so ime_enabled_ will be false at keyup;
516 // because the keydown wasn't sent to the plugin, the keyup shouldn't be
517 // either (per the spec).
518 if (event.type == WebInputEvent::KeyDown)
519 ++keyup_ignore_count_;
520 else
521 --keyup_ignore_count_;
522 return true;
523 }
524
[email protected]779b8812010-03-26 18:55:09525#ifndef NP_NO_CARBON
526 if (instance()->event_model() == NPEventModelCarbon) {
527#ifndef NP_NO_QUICKDRAW
528 if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
529 if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) {
530 // Mouse event handling doesn't work correctly in the fast path mode,
531 // so any time we get a mouse event turn the fast path off, but set a
532 // time to switch it on again (we don't rely just on MouseLeave because
533 // we don't want poor performance in the case of clicking the play
534 // button and then leaving the mouse there).
535 // This isn't perfect (specifically, click-and-hold doesn't seem to work
536 // if the fast path is on), but the slight regression is worthwhile
537 // for the improved framerates.
[email protected]7c64a2f2010-03-29 20:49:41538 if (WebInputEvent::isMouseEventType(event.type)) {
[email protected]779b8812010-03-26 18:55:09539 if (event.type == WebInputEvent::MouseLeave) {
540 SetQuickDrawFastPathEnabled(true);
541 } else {
542 SetQuickDrawFastPathEnabled(false);
543 }
544 // Make sure the plugin wasn't destroyed during the switch.
545 if (!instance())
546 return false;
547 }
548 }
549
[email protected]4491a782010-04-12 15:38:44550 qd_manager_->MakePortCurrent();
[email protected]779b8812010-03-26 18:55:09551 }
552#endif
553
554 if (event.type == WebInputEvent::MouseMove) {
555 return true; // The recurring FireIdleEvent will send null events.
556 }
557 }
558#endif
559
[email protected]b4ec5d92010-01-19 19:24:24560 ScopedActiveDelegate active_delegate(this);
561
[email protected]7c64a2f2010-03-29 20:49:41562 // Create the plugin event structure.
563 NPEventModel event_model = instance()->event_model();
564 scoped_ptr<PluginWebEventConverter> event_converter(
565 PluginWebEventConverterFactory::CreateConverterForModel(event_model));
[email protected]f5b84362010-04-01 22:02:49566 if (!(event_converter.get() && event_converter->InitWithEvent(event))) {
567 // Silently consume any keyboard event types that we don't handle, so that
568 // they don't fall through to the page.
569 if (WebInputEvent::isKeyboardEventType(event.type))
570 return true;
[email protected]7c64a2f2010-03-29 20:49:41571 return false;
[email protected]f5b84362010-04-01 22:02:49572 }
[email protected]7c64a2f2010-03-29 20:49:41573 void* plugin_event = event_converter->plugin_event();
[email protected]e612d762010-03-31 04:32:30574
[email protected]7c64a2f2010-03-29 20:49:41575 if (instance()->event_model() == NPEventModelCocoa) {
576 // We recieve events related to drags starting outside the plugin, but the
577 // NPAPI Cocoa event model spec says plugins shouldn't receive them, so
578 // filter them out.
579 // If we add a page capture mode at the WebKit layer (like the plugin
580 // capture mode that handles drags starting inside) this can be removed.
[email protected]7ba381832010-04-16 15:02:37581 bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event);
582 external_drag_tracker_->UpdateDragStateFromEvent(event);
[email protected]7c64a2f2010-03-29 20:49:41583 if (drag_related) {
[email protected]7ba381832010-04-16 15:02:37584 if (event.type == WebInputEvent::MouseUp &&
585 !external_drag_tracker_->IsDragInProgress()) {
[email protected]7c64a2f2010-03-29 20:49:41586 // When an external drag ends, we need to synthesize a MouseEntered.
587 NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event));
588 enter_event.type = NPCocoaEventMouseEntered;
[email protected]191eb3f72010-12-21 06:27:50589 ScopedCurrentPluginEvent event_scope(instance(), &enter_event);
[email protected]7c64a2f2010-03-29 20:49:41590 instance()->NPP_HandleEvent(&enter_event);
[email protected]3be438a2010-03-15 22:18:09591 }
[email protected]7c64a2f2010-03-29 20:49:41592 return false;
[email protected]5c6ac7672009-12-10 23:45:31593 }
594 }
595
[email protected]7c64a2f2010-03-29 20:49:41596 // Send the plugin the event.
[email protected]191eb3f72010-12-21 06:27:50597 scoped_ptr<ScopedCurrentPluginEvent> event_scope(NULL);
[email protected]7c64a2f2010-03-29 20:49:41598 if (instance()->event_model() == NPEventModelCocoa) {
[email protected]191eb3f72010-12-21 06:27:50599 event_scope.reset(new ScopedCurrentPluginEvent(
[email protected]7c64a2f2010-03-29 20:49:41600 instance(), static_cast<NPCocoaEvent*>(plugin_event)));
601 }
[email protected]935d63d2010-10-15 23:31:55602 int16_t handle_response = instance()->NPP_HandleEvent(plugin_event);
603 bool handled = handle_response != kNPEventNotHandled;
604
[email protected]b7f75862011-01-21 21:15:13605 // Start IME if requested by the plugin.
606 if (handled && handle_response == kNPEventStartIME &&
607 event.type == WebInputEvent::KeyDown) {
608 StartIme();
609 ++keyup_ignore_count_;
[email protected]935d63d2010-10-15 23:31:55610 }
[email protected]7c64a2f2010-03-29 20:49:41611
[email protected]0c395ac2010-06-15 18:17:26612 // Plugins don't give accurate information about whether or not they handled
613 // events, so browsers on the Mac ignore the return value.
614 // Scroll events are the exception, since the Cocoa spec defines a meaning
615 // for the return value.
[email protected]7c64a2f2010-03-29 20:49:41616 if (WebInputEvent::isMouseEventType(event.type)) {
[email protected]7c64a2f2010-03-29 20:49:41617 handled = true;
[email protected]0c395ac2010-06-15 18:17:26618 } else if (WebInputEvent::isKeyboardEventType(event.type)) {
619 // For Command-key events, trust the return value since eating all menu
620 // shortcuts is not ideal.
621 // TODO(stuartmorgan): Implement the advanced key handling spec, and trust
622 // trust the key event return value from plugins that implement it.
623 if (!(event.modifiers & WebInputEvent::MetaKey))
624 handled = true;
[email protected]01938c92010-01-15 17:05:13625 }
626
[email protected]7c64a2f2010-03-29 20:49:41627 return handled;
[email protected]39b8c5132009-02-11 07:48:22628}
629
[email protected]7ba381832010-04-16 15:02:37630void WebPluginDelegateImpl::InstallMissingPlugin() {
631 NOTIMPLEMENTED();
632}
633
634#pragma mark -
635
636void WebPluginDelegateImpl::WindowlessUpdateGeometry(
637 const gfx::Rect& window_rect,
638 const gfx::Rect& clip_rect) {
639 gfx::Rect old_clip_rect = clip_rect_;
640 cached_clip_rect_ = clip_rect;
641 if (container_is_visible_) // Remove check when cached_clip_rect_ is removed.
642 clip_rect_ = clip_rect;
643 bool clip_rect_changed = (clip_rect_ != old_clip_rect);
644 bool window_size_changed = (window_rect.size() != window_rect_.size());
645
[email protected]372a33a2010-05-04 15:48:33646 bool force_set_window = false;
647#ifndef NP_NO_QUICKDRAW
648 // In a QuickDraw plugin, a geometry update might have caused a port change;
649 // if so, we need to call SetWindow even if nothing else changed.
650 if (qd_manager_.get() && (qd_port_.port != qd_manager_->port())) {
[email protected]372a33a2010-05-04 15:48:33651 qd_port_.port = qd_manager_->port();
652 force_set_window = true;
653 }
654#endif
655
656 if (window_rect == window_rect_ && !clip_rect_changed && !force_set_window)
[email protected]7ba381832010-04-16 15:02:37657 return;
658
659 if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
660 PluginVisibilityChanged();
661 }
662
663 SetPluginRect(window_rect);
664
665#ifndef NP_NO_QUICKDRAW
666 if (window_size_changed && qd_manager_.get() &&
667 qd_manager_->IsFastPathEnabled()) {
668 // If the window size has changed, we need to turn off the fast path so that
669 // the full redraw goes to the window and we get a correct baseline paint.
670 SetQuickDrawFastPathEnabled(false);
671 return; // SetQuickDrawFastPathEnabled will call SetWindow for us.
672 }
673#endif
674
[email protected]372a33a2010-05-04 15:48:33675 if (window_size_changed || clip_rect_changed || force_set_window)
[email protected]7ba381832010-04-16 15:02:37676 WindowlessSetWindow();
677}
678
679void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
680 const gfx::Rect& damage_rect) {
681 // If we get a paint event before we are completely set up (e.g., a nested
682 // call while the plugin is still in NPP_SetWindow), bail.
[email protected]2fc51e5b2011-02-04 03:36:00683 if (!have_called_set_window_ || !buffer_context_)
[email protected]7ba381832010-04-16 15:02:37684 return;
[email protected]2fc51e5b2011-02-04 03:36:00685 DCHECK(buffer_context_ == context);
[email protected]7ba381832010-04-16 15:02:37686
[email protected]835d7c82010-10-14 04:38:38687 static base::StatsRate plugin_paint("Plugin.Paint");
688 base::StatsScope<base::StatsRate> scope(plugin_paint);
[email protected]7ba381832010-04-16 15:02:37689
690 // Plugin invalidates trigger asynchronous paints with the original
691 // invalidation rect; the plugin may be resized before the paint is handled,
692 // so we need to ensure that the damage rect is still sane.
693 const gfx::Rect paint_rect(damage_rect.Intersect(
694 gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));
695
696 ScopedActiveDelegate active_delegate(this);
697
698#ifndef NP_NO_QUICKDRAW
699 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
700 qd_manager_->MakePortCurrent();
701#endif
702
703 CGContextSaveGState(context);
704
705 switch (instance()->event_model()) {
[email protected]385579b2010-01-21 20:18:48706#ifndef NP_NO_CARBON
[email protected]7ba381832010-04-16 15:02:37707 case NPEventModelCarbon: {
708 NPEvent paint_event = { 0 };
709 paint_event.what = updateEvt;
710 paint_event.message = reinterpret_cast<uint32>(np_cg_context_.window);
711 paint_event.when = TickCount();
712 instance()->NPP_HandleEvent(&paint_event);
713 break;
714 }
715#endif
716 case NPEventModelCocoa: {
717 NPCocoaEvent paint_event;
718 memset(&paint_event, 0, sizeof(NPCocoaEvent));
719 paint_event.type = NPCocoaEventDrawRect;
720 paint_event.data.draw.context = context;
721 paint_event.data.draw.x = paint_rect.x();
722 paint_event.data.draw.y = paint_rect.y();
723 paint_event.data.draw.width = paint_rect.width();
724 paint_event.data.draw.height = paint_rect.height();
725 instance()->NPP_HandleEvent(&paint_event);
726 break;
727 }
728 }
729
730 // The backing buffer can change during the call to NPP_HandleEvent, in which
731 // case the old context is (or is about to be) invalid.
[email protected]2fc51e5b2011-02-04 03:36:00732 if (context == buffer_context_)
733 CGContextRestoreGState(context);
[email protected]7ba381832010-04-16 15:02:37734}
735
736void WebPluginDelegateImpl::WindowlessSetWindow() {
737 if (!instance())
738 return;
739
740 window_.x = 0;
741 window_.y = 0;
742 window_.height = window_rect_.height();
743 window_.width = window_rect_.width();
744 window_.clipRect.left = clip_rect_.x();
745 window_.clipRect.top = clip_rect_.y();
746 window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
747 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
748
749 NPError err = instance()->NPP_SetWindow(&window_);
750
751 // Send an appropriate window focus event after the first SetWindow.
752 if (!have_called_set_window_) {
753 have_called_set_window_ = true;
754 SetWindowHasFocus(initial_window_focus_);
755 }
756
757#ifndef NP_NO_QUICKDRAW
758 if ((quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) &&
759 !qd_manager_->IsFastPathEnabled() && !clip_rect_.IsEmpty()) {
760 // Give the plugin a few seconds to stabilize so we get a good initial paint
761 // to use as a baseline, then switch to the fast path.
762 fast_path_enable_tick_ = base::TimeTicks::Now() +
763 base::TimeDelta::FromSeconds(3);
764 }
765#endif
766
767 DCHECK(err == NPERR_NO_ERROR);
768}
769
770#pragma mark -
771
772bool WebPluginDelegateImpl::WindowedCreatePlugin() {
773 NOTREACHED();
774 return false;
775}
776
777void WebPluginDelegateImpl::WindowedDestroyWindow() {
778 NOTREACHED();
779}
780
781bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
782 const gfx::Rect& clip_rect) {
783 NOTREACHED();
784 return false;
785}
786
787void WebPluginDelegateImpl::WindowedSetWindow() {
788 NOTREACHED();
789}
790
791#pragma mark -
792#pragma mark Mac Extensions
793
[email protected]51a0bb52010-06-03 21:34:06794void WebPluginDelegateImpl::PluginDidInvalidate() {
795 if (instance()->drawing_model() == NPDrawingModelInvalidatingCoreAnimation)
796 DrawLayerInSurface();
797}
798
[email protected]7ba381832010-04-16 15:02:37799WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
800 return g_active_delegate;
801}
802
[email protected]7ba381832010-04-16 15:02:37803void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
804 // If we get a window focus event before calling SetWindow, just remember the
805 // states (WindowlessSetWindow will then send it on the first call).
806 if (!have_called_set_window_) {
807 initial_window_focus_ = has_focus;
808 return;
809 }
810
811 if (has_focus == containing_window_has_focus_)
812 return;
813 containing_window_has_focus_ = has_focus;
814
815#ifndef NP_NO_QUICKDRAW
816 // Make sure controls repaint with the correct look.
817 if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH)
818 SetQuickDrawFastPathEnabled(false);
819#endif
820
821 ScopedActiveDelegate active_delegate(this);
822 switch (instance()->event_model()) {
823#ifndef NP_NO_CARBON
824 case NPEventModelCarbon: {
825 NPEvent focus_event = { 0 };
826 focus_event.what = activateEvt;
827 if (has_focus)
828 focus_event.modifiers |= activeFlag;
829 focus_event.message =
830 reinterpret_cast<unsigned long>(np_cg_context_.window);
831 focus_event.when = TickCount();
832 instance()->NPP_HandleEvent(&focus_event);
833 break;
834 }
835#endif
836 case NPEventModelCocoa: {
837 NPCocoaEvent focus_event;
838 memset(&focus_event, 0, sizeof(focus_event));
839 focus_event.type = NPCocoaEventWindowFocusChanged;
840 focus_event.data.focus.hasFocus = has_focus;
841 instance()->NPP_HandleEvent(&focus_event);
842 break;
843 }
844 }
845}
846
[email protected]784ea1ab2010-09-18 00:02:34847bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
[email protected]7d3c02c2010-05-05 23:10:31848 if (!have_called_set_window_)
[email protected]784ea1ab2010-09-18 00:02:34849 return false;
[email protected]7d3c02c2010-05-05 23:10:31850
[email protected]b7f75862011-01-21 21:15:13851 plugin_->FocusChanged(focused);
[email protected]935d63d2010-10-15 23:31:55852
[email protected]7d3c02c2010-05-05 23:10:31853 ScopedActiveDelegate active_delegate(this);
854
855 switch (instance()->event_model()) {
856#ifndef NP_NO_CARBON
857 case NPEventModelCarbon: {
858 NPEvent focus_event = { 0 };
[email protected]784ea1ab2010-09-18 00:02:34859 if (focused)
[email protected]7d3c02c2010-05-05 23:10:31860 focus_event.what = NPEventType_GetFocusEvent;
861 else
862 focus_event.what = NPEventType_LoseFocusEvent;
863 focus_event.when = TickCount();
864 instance()->NPP_HandleEvent(&focus_event);
865 break;
866 }
867#endif
868 case NPEventModelCocoa: {
869 NPCocoaEvent focus_event;
870 memset(&focus_event, 0, sizeof(focus_event));
871 focus_event.type = NPCocoaEventFocusChanged;
[email protected]784ea1ab2010-09-18 00:02:34872 focus_event.data.focus.hasFocus = focused;
[email protected]7d3c02c2010-05-05 23:10:31873 instance()->NPP_HandleEvent(&focus_event);
874 break;
875 }
876 }
[email protected]784ea1ab2010-09-18 00:02:34877 return true;
[email protected]7d3c02c2010-05-05 23:10:31878}
879
[email protected]7ba381832010-04-16 15:02:37880void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
881 if (is_visible == container_is_visible_)
882 return;
883 container_is_visible_ = is_visible;
884
885 // TODO(stuartmorgan): This is a temporary workarond for
886 // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
887 // should all be removed.
888 if (is_visible) {
889 clip_rect_ = cached_clip_rect_;
890 } else {
891 clip_rect_.set_width(0);
892 clip_rect_.set_height(0);
893 }
894
[email protected]7ba381832010-04-16 15:02:37895 // If the plugin is changing visibility, let the plugin know. If it's scrolled
896 // off screen (i.e., cached_clip_rect_ is empty), then container visibility
897 // doesn't change anything.
898 if (!cached_clip_rect_.IsEmpty()) {
899 PluginVisibilityChanged();
900 WindowlessSetWindow();
901 }
902
903 // When the plugin become visible, send an empty invalidate. If there were any
904 // pending invalidations this will trigger a paint event for the damaged
905 // region, and if not it's a no-op. This is necessary since higher levels
906 // that would normally do this weren't responsible for the clip_rect_ change).
907 if (!clip_rect_.IsEmpty())
908 instance()->webplugin()->InvalidateRect(gfx::Rect());
909}
910
[email protected]784ea1ab2010-09-18 00:02:34911void WebPluginDelegateImpl::WindowFrameChanged(const gfx::Rect& window_frame,
912 const gfx::Rect& view_frame) {
[email protected]7ba381832010-04-16 15:02:37913 instance()->set_window_frame(window_frame);
914 SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
915}
916
[email protected]b7f75862011-01-21 21:15:13917void WebPluginDelegateImpl::ImeCompositionCompleted(const string16& text) {
[email protected]935d63d2010-10-15 23:31:55918 if (instance()->event_model() != NPEventModelCocoa) {
[email protected]b7f75862011-01-21 21:15:13919 DLOG(ERROR) << "IME notification receieved in Carbon event model";
[email protected]935d63d2010-10-15 23:31:55920 return;
921 }
922
[email protected]b7f75862011-01-21 21:15:13923 ime_enabled_ = false;
924
925 // If |text| is empty this was just called to tell us composition was
926 // cancelled externally (e.g., the user pressed esc).
927 if (!text.empty()) {
928 NPCocoaEvent text_event;
929 memset(&text_event, 0, sizeof(NPCocoaEvent));
930 text_event.type = NPCocoaEventTextInput;
931 text_event.data.text.text =
932 reinterpret_cast<NPNSString*>(base::SysUTF16ToNSString(text));
933 instance()->NPP_HandleEvent(&text_event);
934 }
[email protected]935d63d2010-10-15 23:31:55935}
936
[email protected]6ce58c8a2011-02-07 16:19:51937#ifndef NP_NO_CARBON
[email protected]7ba381832010-04-16 15:02:37938void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
939 current_windowless_cursor_.InitFromThemeCursor(cursor);
940}
941
[email protected]6ce58c8a2011-02-07 16:19:51942void WebPluginDelegateImpl::SetCarbonCursor(const Cursor* cursor) {
[email protected]7ba381832010-04-16 15:02:37943 current_windowless_cursor_.InitFromCursor(cursor);
944}
[email protected]6ce58c8a2011-02-07 16:19:51945#endif
[email protected]7ba381832010-04-16 15:02:37946
947void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
948 current_windowless_cursor_.InitFromNSCursor(cursor);
949}
950
951#pragma mark -
952#pragma mark Internal Tracking
953
954void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
955 bool plugin_size_changed = rect.width() != window_rect_.width() ||
956 rect.height() != window_rect_.height();
957 window_rect_ = rect;
958 PluginScreenLocationChanged();
959 if (plugin_size_changed)
960 UpdateAcceleratedSurface();
961}
962
963void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
964 content_area_origin_ = origin;
965 PluginScreenLocationChanged();
966}
967
968void WebPluginDelegateImpl::PluginScreenLocationChanged() {
969 gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
970 content_area_origin_.y() + window_rect_.y());
971 instance()->set_plugin_origin(plugin_origin);
972
973#ifndef NP_NO_CARBON
974 if (instance()->event_model() == NPEventModelCarbon) {
975 UpdateDummyWindowBounds(plugin_origin);
976 }
977#endif
978}
979
980void WebPluginDelegateImpl::PluginVisibilityChanged() {
981#ifndef NP_NO_CARBON
982 if (instance()->event_model() == NPEventModelCarbon)
983 UpdateIdleEventRate();
984#endif
985 if (instance()->drawing_model() == NPDrawingModelCoreAnimation) {
986 bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
[email protected]4736973b2010-04-28 17:56:42987 if (plugin_visible && !redraw_timer_->IsRunning() && windowed_handle()) {
[email protected]7ba381832010-04-16 15:02:37988 redraw_timer_->Start(
989 base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs),
990 this, &WebPluginDelegateImpl::DrawLayerInSurface);
991 } else if (!plugin_visible) {
992 redraw_timer_->Stop();
993 }
994 }
995}
996
[email protected]b7f75862011-01-21 21:15:13997void WebPluginDelegateImpl::StartIme() {
998 if (instance()->event_model() != NPEventModelCocoa ||
999 !IsImeSupported()) {
[email protected]935d63d2010-10-15 23:31:551000 return;
[email protected]b7f75862011-01-21 21:15:131001 }
1002 if (ime_enabled_)
[email protected]935d63d2010-10-15 23:31:551003 return;
[email protected]b7f75862011-01-21 21:15:131004 ime_enabled_ = true;
1005 plugin_->StartIme();
1006}
1007
1008bool WebPluginDelegateImpl::IsImeSupported() {
1009 // Currently the plugin IME implementation only works on 10.6.
1010 static BOOL sImeSupported = NO;
1011 static BOOL sHaveCheckedSupport = NO;
1012 if (!sHaveCheckedSupport) {
1013 int32 major, minor, bugfix;
1014 base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
1015 sImeSupported = major > 10 || (major == 10 && minor > 5);
1016 sHaveCheckedSupport = YES;
1017 }
1018 return sImeSupported;
[email protected]935d63d2010-10-15 23:31:551019}
1020
[email protected]7ba381832010-04-16 15:02:371021#pragma mark -
1022#pragma mark Core Animation Support
1023
1024void WebPluginDelegateImpl::DrawLayerInSurface() {
[email protected]4736973b2010-04-28 17:56:421025 // If we haven't plumbed up the surface yet, don't try to draw.
[email protected]d92bec42010-11-22 22:49:341026 if (!windowed_handle() || !renderer_)
[email protected]4736973b2010-04-28 17:56:421027 return;
1028
[email protected]7ba381832010-04-16 15:02:371029 [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
[email protected]daaaef42010-06-15 16:16:201030 if (CGRectIsEmpty([renderer_ updateBounds])) {
1031 // If nothing has changed, we are done.
1032 [renderer_ endFrame];
1033 return;
1034 }
[email protected]65d52b92010-11-18 17:44:261035
1036 surface_->StartDrawing();
1037
[email protected]7ba381832010-04-16 15:02:371038 CGRect layerRect = [layer_ bounds];
1039 [renderer_ addUpdateRect:layerRect];
1040 [renderer_ render];
1041 [renderer_ endFrame];
1042
[email protected]8b4755f2010-10-01 18:04:061043 surface_->EndDrawing();
[email protected]7ba381832010-04-16 15:02:371044}
1045
[email protected]8b4755f2010-10-01 18:04:061046// Update the size of the surface to match the current size of the plug-in.
[email protected]7ba381832010-04-16 15:02:371047void WebPluginDelegateImpl::UpdateAcceleratedSurface() {
[email protected]51a0bb52010-06-03 21:34:061048 // Will only have a window handle when using a Core Animation drawing model.
1049 if (!windowed_handle() || !layer_)
[email protected]7ba381832010-04-16 15:02:371050 return;
1051
[email protected]515040e2010-10-11 19:55:101052 [CATransaction begin];
1053 [CATransaction setValue:[NSNumber numberWithInt:0]
1054 forKey:kCATransactionAnimationDuration];
[email protected]7ba381832010-04-16 15:02:371055 [layer_ setFrame:CGRectMake(0, 0,
1056 window_rect_.width(), window_rect_.height())];
[email protected]515040e2010-10-11 19:55:101057 [CATransaction commit];
1058
[email protected]7ba381832010-04-16 15:02:371059 [renderer_ setBounds:[layer_ bounds]];
[email protected]8b4755f2010-10-01 18:04:061060 surface_->SetSize(window_rect_.size());
[email protected]7ba381832010-04-16 15:02:371061}
1062
[email protected]4736973b2010-04-28 17:56:421063void WebPluginDelegateImpl::set_windowed_handle(
1064 gfx::PluginWindowHandle handle) {
1065 windowed_handle_ = handle;
[email protected]8b4755f2010-10-01 18:04:061066 surface_->SetWindowHandle(handle);
[email protected]4736973b2010-04-28 17:56:421067 UpdateAcceleratedSurface();
1068 // Kick off the drawing timer, if necessary.
1069 PluginVisibilityChanged();
1070}
1071
[email protected]7ba381832010-04-16 15:02:371072#pragma mark -
1073#pragma mark Carbon Event support
1074
1075#ifndef NP_NO_CARBON
1076void WebPluginDelegateImpl::UpdateDummyWindowBounds(
1077 const gfx::Point& plugin_origin) {
1078 WindowRef window = reinterpret_cast<WindowRef>(np_cg_context_.window);
1079 Rect current_bounds;
1080 GetWindowBounds(window, kWindowContentRgn, &current_bounds);
1081
1082 Rect new_bounds;
1083 // We never want to resize the window to 0x0, so if the plugin is 0x0 just
1084 // move the window without resizing it.
1085 if (window_rect_.width() > 0 && window_rect_.height() > 0) {
1086 SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
1087 OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
1088 } else {
1089 new_bounds = current_bounds;
1090 OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
1091 plugin_origin.y() - current_bounds.top);
1092 }
1093
1094 if (new_bounds.left != current_bounds.left ||
1095 new_bounds.top != current_bounds.top ||
1096 new_bounds.right != current_bounds.right ||
1097 new_bounds.bottom != current_bounds.bottom)
1098 SetWindowBounds(window, kWindowContentRgn, &new_bounds);
1099}
1100
1101void WebPluginDelegateImpl::UpdateIdleEventRate() {
1102 bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
1103 CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
1104 plugin_visible);
1105}
1106
[email protected]bc384e4a2010-01-21 17:26:281107void WebPluginDelegateImpl::FireIdleEvent() {
[email protected]28dbd6c2009-11-20 18:52:161108 // Avoid a race condition between IO and UI threads during plugin shutdown
[email protected]47e9c75c2010-02-16 21:31:541109 if (!instance())
[email protected]28dbd6c2009-11-20 18:52:161110 return;
[email protected]32c0554d2010-04-05 20:57:171111 // Don't send idle events until we've called SetWindow.
1112 if (!have_called_set_window_)
1113 return;
[email protected]b4ec5d92010-01-19 19:24:241114
[email protected]779b8812010-03-26 18:55:091115#ifndef NP_NO_QUICKDRAW
1116 // Check whether it's time to turn the QuickDraw fast path back on.
1117 if (!fast_path_enable_tick_.is_null() &&
1118 (base::TimeTicks::Now() > fast_path_enable_tick_)) {
1119 SetQuickDrawFastPathEnabled(true);
1120 fast_path_enable_tick_ = base::TimeTicks();
1121 }
1122#endif
1123
[email protected]b4ec5d92010-01-19 19:24:241124 ScopedActiveDelegate active_delegate(this);
1125
[email protected]779b8812010-03-26 18:55:091126#ifndef NP_NO_QUICKDRAW
[email protected]4491a782010-04-12 15:38:441127 if (instance()->drawing_model() == NPDrawingModelQuickDraw)
1128 qd_manager_->MakePortCurrent();
[email protected]779b8812010-03-26 18:55:091129#endif
1130
[email protected]44b671552010-03-01 21:19:061131 // Send an idle event so that the plugin can do background work
1132 NPEvent np_event = {0};
1133 np_event.what = nullEvent;
1134 np_event.when = TickCount();
1135 np_event.modifiers = GetCurrentKeyModifiers();
1136 if (!Button())
1137 np_event.modifiers |= btnState;
1138 HIPoint mouse_location;
1139 HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
1140 np_event.where.h = mouse_location.x;
1141 np_event.where.v = mouse_location.y;
1142 instance()->NPP_HandleEvent(&np_event);
[email protected]9ee23c5b2009-07-13 21:02:001143
[email protected]760cd2122009-10-13 17:53:221144#ifndef NP_NO_QUICKDRAW
1145 // Quickdraw-based plugins can draw at any time, so tell the renderer to
1146 // repaint.
[email protected]47e9c75c2010-02-16 21:31:541147 if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
[email protected]760cd2122009-10-13 17:53:221148 instance()->webplugin()->Invalidate();
1149#endif
[email protected]9ee23c5b2009-07-13 21:02:001150}
[email protected]385579b2010-01-21 20:18:481151#endif // !NP_NO_CARBON
[email protected]7ba381832010-04-16 15:02:371152
1153#pragma mark -
1154#pragma mark QuickDraw Support
1155
1156#ifndef NP_NO_QUICKDRAW
1157void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) {
1158 if (!enabled) {
1159 // Wait a couple of seconds, then turn the fast path back on. If we're
1160 // turning it off for event handling, that ensures that the common case of
1161 // move-mouse-then-click works (as well as making it likely that a second
1162 // click attempt will work if the first one fails). If we're turning it
1163 // off to force a new baseline image, this leaves plenty of time for the
1164 // plugin to draw.
1165 fast_path_enable_tick_ = base::TimeTicks::Now() +
1166 base::TimeDelta::FromSeconds(2);
1167 }
1168
1169 if (enabled == qd_manager_->IsFastPathEnabled())
1170 return;
1171 if (enabled && clip_rect_.IsEmpty()) {
1172 // Don't switch to the fast path while the plugin is completely clipped;
1173 // we can only switch when the window has an up-to-date image for us to
1174 // scrape. We'll automatically switch after we become visible again.
1175 return;
1176 }
1177
1178 qd_manager_->SetFastPathEnabled(enabled);
1179 qd_port_.port = qd_manager_->port();
1180 WindowlessSetWindow();
1181 // Send a paint event so that the new buffer gets updated immediately.
1182 WindowlessPaint(buffer_context_, clip_rect_);
1183}
1184#endif // !NP_NO_QUICKDRAW
[email protected]191eb3f72010-12-21 06:27:501185
1186} // namespace npapi
1187} // namespace webkit