OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/child/npapi/webplugin_delegate_impl.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <string.h> | |
9 | |
10 #include <map> | |
11 #include <set> | |
12 #include <string> | |
13 #include <vector> | |
14 | |
15 #include "base/bind.h" | |
16 #include "base/compiler_specific.h" | |
17 #include "base/lazy_instance.h" | |
18 #include "base/macros.h" | |
19 #include "base/memory/scoped_ptr.h" | |
20 #include "base/message_loop/message_loop.h" | |
21 #include "base/strings/string_util.h" | |
22 #include "base/strings/stringprintf.h" | |
23 #include "base/synchronization/lock.h" | |
24 #include "base/version.h" | |
25 #include "base/win/windows_version.h" | |
26 #include "content/child/npapi/plugin_instance.h" | |
27 #include "content/child/npapi/plugin_lib.h" | |
28 #include "content/child/npapi/webplugin.h" | |
29 #include "content/common/cursors/webcursor.h" | |
30 #include "content/common/plugin_constants_win.h" | |
31 #include "content/public/common/content_constants.h" | |
32 #include "skia/ext/platform_canvas.h" | |
33 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
34 #include "ui/gfx/win/dpi.h" | |
35 #include "ui/gfx/win/hwnd_util.h" | |
36 | |
37 using blink::WebKeyboardEvent; | |
38 using blink::WebInputEvent; | |
39 using blink::WebMouseEvent; | |
40 | |
41 namespace content { | |
42 | |
43 namespace { | |
44 | |
45 // http://crbug.com/16114 | |
46 // Enforces providing a valid device context in NPWindow, so that NPP_SetWindow | |
47 // is never called with NPNWindoTypeDrawable and NPWindow set to NULL. | |
48 // Doing so allows removing NPP_SetWindow call during painting a windowless | |
49 // plugin, which otherwise could trigger layout change while painting by | |
50 // invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes. | |
51 // TODO(dglazkov): If this approach doesn't produce regressions, move class to | |
52 // webplugin_delegate_impl.h and implement for other platforms. | |
53 class DrawableContextEnforcer { | |
54 public: | |
55 explicit DrawableContextEnforcer(NPWindow* window) | |
56 : window_(window), | |
57 disposable_dc_(window && !window->window) { | |
58 // If NPWindow is NULL, create a device context with monochrome 1x1 surface | |
59 // and stuff it to NPWindow. | |
60 if (disposable_dc_) | |
61 window_->window = CreateCompatibleDC(NULL); | |
62 } | |
63 | |
64 ~DrawableContextEnforcer() { | |
65 if (!disposable_dc_) | |
66 return; | |
67 | |
68 DeleteDC(static_cast<HDC>(window_->window)); | |
69 window_->window = NULL; | |
70 } | |
71 | |
72 private: | |
73 NPWindow* window_; | |
74 bool disposable_dc_; | |
75 }; | |
76 | |
77 } // namespace | |
78 | |
79 WebPluginDelegateImpl::WebPluginDelegateImpl(WebPlugin* plugin, | |
80 PluginInstance* instance) | |
81 : plugin_(plugin), | |
82 instance_(instance), | |
83 quirks_(0), | |
84 handle_event_depth_(0), | |
85 first_set_window_call_(true), | |
86 plugin_has_focus_(false), | |
87 has_webkit_focus_(false), | |
88 containing_view_has_focus_(true), | |
89 creation_succeeded_(false), | |
90 user_gesture_msg_factory_(this) { | |
91 memset(&window_, 0, sizeof(window_)); | |
92 } | |
93 | |
94 WebPluginDelegateImpl::~WebPluginDelegateImpl() { | |
95 DestroyInstance(); | |
96 } | |
97 | |
98 bool WebPluginDelegateImpl::PlatformInitialize() { | |
99 return true; | |
100 } | |
101 | |
102 void WebPluginDelegateImpl::PlatformDestroyInstance() { | |
103 } | |
104 | |
105 void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) { | |
106 if (skia::SupportsPlatformPaint(canvas)) { | |
107 skia::ScopedPlatformPaint scoped_platform_paint(canvas); | |
108 HDC hdc = scoped_platform_paint.GetPlatformSurface(); | |
109 WindowlessPaint(hdc, rect); | |
110 } | |
111 } | |
112 | |
113 // Returns true if the message passed in corresponds to a user gesture. | |
114 static bool IsUserGestureMessage(unsigned int message) { | |
115 switch (message) { | |
116 case WM_LBUTTONDOWN: | |
117 case WM_LBUTTONUP: | |
118 case WM_RBUTTONDOWN: | |
119 case WM_RBUTTONUP: | |
120 case WM_MBUTTONDOWN: | |
121 case WM_MBUTTONUP: | |
122 case WM_KEYDOWN: | |
123 case WM_KEYUP: | |
124 return true; | |
125 | |
126 default: | |
127 break; | |
128 } | |
129 | |
130 return false; | |
131 } | |
132 | |
133 void WebPluginDelegateImpl::WindowlessUpdateGeometry( | |
134 const gfx::Rect& window_rect, | |
135 const gfx::Rect& clip_rect) { | |
136 bool window_rect_changed = (window_rect_ != window_rect); | |
137 // Only resend to the instance if the geometry has changed. | |
138 if (!window_rect_changed && clip_rect == clip_rect_) | |
139 return; | |
140 | |
141 clip_rect_ = clip_rect; | |
142 window_rect_ = window_rect; | |
143 | |
144 WindowlessSetWindow(); | |
145 | |
146 if (window_rect_changed) { | |
147 WINDOWPOS win_pos = {0}; | |
148 win_pos.x = window_rect_.x(); | |
149 win_pos.y = window_rect_.y(); | |
150 win_pos.cx = window_rect_.width(); | |
151 win_pos.cy = window_rect_.height(); | |
152 | |
153 NPEvent pos_changed_event; | |
154 pos_changed_event.event = WM_WINDOWPOSCHANGED; | |
155 pos_changed_event.wParam = 0; | |
156 pos_changed_event.lParam = reinterpret_cast<uintptr_t>(&win_pos); | |
157 | |
158 instance()->NPP_HandleEvent(&pos_changed_event); | |
159 } | |
160 } | |
161 | |
162 void WebPluginDelegateImpl::WindowlessPaint(HDC hdc, | |
163 const gfx::Rect& damage_rect) { | |
164 DCHECK(hdc); | |
165 | |
166 RECT damage_rect_win; | |
167 damage_rect_win.left = damage_rect.x(); // + window_rect_.x(); | |
168 damage_rect_win.top = damage_rect.y(); // + window_rect_.y(); | |
169 damage_rect_win.right = damage_rect_win.left + damage_rect.width(); | |
170 damage_rect_win.bottom = damage_rect_win.top + damage_rect.height(); | |
171 | |
172 // Save away the old HDC as this could be a nested invocation. | |
173 void* old_dc = window_.window; | |
174 window_.window = hdc; | |
175 | |
176 NPEvent paint_event; | |
177 paint_event.event = WM_PAINT; | |
178 paint_event.wParam = PtrToUlong(hdc); | |
179 paint_event.lParam = reinterpret_cast<uintptr_t>(&damage_rect_win); | |
180 instance()->NPP_HandleEvent(&paint_event); | |
181 window_.window = old_dc; | |
182 } | |
183 | |
184 void WebPluginDelegateImpl::WindowlessSetWindow() { | |
185 if (!instance()) | |
186 return; | |
187 | |
188 if (window_rect_.IsEmpty()) // wait for geometry to be set. | |
189 return; | |
190 | |
191 window_.clipRect.top = clip_rect_.y(); | |
192 window_.clipRect.left = clip_rect_.x(); | |
193 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); | |
194 window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); | |
195 window_.height = window_rect_.height(); | |
196 window_.width = window_rect_.width(); | |
197 window_.x = window_rect_.x(); | |
198 window_.y = window_rect_.y(); | |
199 window_.type = NPWindowTypeDrawable; | |
200 DrawableContextEnforcer enforcer(&window_); | |
201 | |
202 NPError err = instance()->NPP_SetWindow(&window_); | |
203 DCHECK(err == NPERR_NO_ERROR); | |
204 } | |
205 | |
206 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) { | |
207 NPEvent focus_event; | |
208 focus_event.event = focused ? WM_SETFOCUS : WM_KILLFOCUS; | |
209 focus_event.wParam = 0; | |
210 focus_event.lParam = 0; | |
211 | |
212 instance()->NPP_HandleEvent(&focus_event); | |
213 return true; | |
214 } | |
215 | |
216 static bool NPEventFromWebMouseEvent(const WebMouseEvent& event, | |
217 NPEvent* np_event) { | |
218 np_event->lParam = | |
219 static_cast<uint32_t>(MAKELPARAM(event.windowX, event.windowY)); | |
220 np_event->wParam = 0; | |
221 | |
222 if (event.modifiers & WebInputEvent::ControlKey) | |
223 np_event->wParam |= MK_CONTROL; | |
224 if (event.modifiers & WebInputEvent::ShiftKey) | |
225 np_event->wParam |= MK_SHIFT; | |
226 if (event.modifiers & WebInputEvent::LeftButtonDown) | |
227 np_event->wParam |= MK_LBUTTON; | |
228 if (event.modifiers & WebInputEvent::MiddleButtonDown) | |
229 np_event->wParam |= MK_MBUTTON; | |
230 if (event.modifiers & WebInputEvent::RightButtonDown) | |
231 np_event->wParam |= MK_RBUTTON; | |
232 | |
233 switch (event.type) { | |
234 case WebInputEvent::MouseMove: | |
235 case WebInputEvent::MouseLeave: | |
236 case WebInputEvent::MouseEnter: | |
237 np_event->event = WM_MOUSEMOVE; | |
238 return true; | |
239 case WebInputEvent::MouseDown: | |
240 switch (event.button) { | |
241 case WebMouseEvent::ButtonLeft: | |
242 np_event->event = WM_LBUTTONDOWN; | |
243 break; | |
244 case WebMouseEvent::ButtonMiddle: | |
245 np_event->event = WM_MBUTTONDOWN; | |
246 break; | |
247 case WebMouseEvent::ButtonRight: | |
248 np_event->event = WM_RBUTTONDOWN; | |
249 break; | |
250 case WebMouseEvent::ButtonNone: | |
251 break; | |
252 } | |
253 return true; | |
254 case WebInputEvent::MouseUp: | |
255 switch (event.button) { | |
256 case WebMouseEvent::ButtonLeft: | |
257 np_event->event = WM_LBUTTONUP; | |
258 break; | |
259 case WebMouseEvent::ButtonMiddle: | |
260 np_event->event = WM_MBUTTONUP; | |
261 break; | |
262 case WebMouseEvent::ButtonRight: | |
263 np_event->event = WM_RBUTTONUP; | |
264 break; | |
265 case WebMouseEvent::ButtonNone: | |
266 break; | |
267 } | |
268 return true; | |
269 default: | |
270 NOTREACHED(); | |
271 return false; | |
272 } | |
273 } | |
274 | |
275 static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event, | |
276 NPEvent* np_event) { | |
277 np_event->wParam = event.windowsKeyCode; | |
278 | |
279 switch (event.type) { | |
280 case WebInputEvent::KeyDown: | |
281 np_event->event = WM_KEYDOWN; | |
282 np_event->lParam = 0; | |
283 return true; | |
284 case WebInputEvent::Char: | |
285 np_event->event = WM_CHAR; | |
286 np_event->lParam = 0; | |
287 return true; | |
288 case WebInputEvent::KeyUp: | |
289 np_event->event = WM_KEYUP; | |
290 np_event->lParam = 0x8000; | |
291 return true; | |
292 default: | |
293 NOTREACHED(); | |
294 return false; | |
295 } | |
296 } | |
297 | |
298 static bool NPEventFromWebInputEvent(const WebInputEvent& event, | |
299 NPEvent* np_event) { | |
300 switch (event.type) { | |
301 case WebInputEvent::MouseMove: | |
302 case WebInputEvent::MouseLeave: | |
303 case WebInputEvent::MouseEnter: | |
304 case WebInputEvent::MouseDown: | |
305 case WebInputEvent::MouseUp: | |
306 if (event.size < sizeof(WebMouseEvent)) { | |
307 NOTREACHED(); | |
308 return false; | |
309 } | |
310 return NPEventFromWebMouseEvent( | |
311 *static_cast<const WebMouseEvent*>(&event), np_event); | |
312 case WebInputEvent::KeyDown: | |
313 case WebInputEvent::Char: | |
314 case WebInputEvent::KeyUp: | |
315 if (event.size < sizeof(WebKeyboardEvent)) { | |
316 NOTREACHED(); | |
317 return false; | |
318 } | |
319 return NPEventFromWebKeyboardEvent( | |
320 *static_cast<const WebKeyboardEvent*>(&event), np_event); | |
321 default: | |
322 return false; | |
323 } | |
324 } | |
325 | |
326 bool WebPluginDelegateImpl::PlatformHandleInputEvent( | |
327 const WebInputEvent& event, WebCursor::CursorInfo* cursor_info) { | |
328 DCHECK(cursor_info != NULL); | |
329 | |
330 NPEvent np_event; | |
331 if (!NPEventFromWebInputEvent(event, &np_event)) { | |
332 return false; | |
333 } | |
334 | |
335 handle_event_depth_++; | |
336 | |
337 bool popups_enabled = false; | |
338 | |
339 if (IsUserGestureMessage(np_event.event)) { | |
340 instance()->PushPopupsEnabledState(true); | |
341 popups_enabled = true; | |
342 } | |
343 | |
344 bool ret = instance()->NPP_HandleEvent(&np_event) != 0; | |
345 | |
346 if (popups_enabled) { | |
347 instance()->PopPopupsEnabledState(); | |
348 } | |
349 | |
350 // Flash and SilverLight always return false, even when they swallow the | |
351 // event. Flash does this because it passes the event to its window proc, | |
352 // which is supposed to return 0 if an event was handled. There are few | |
353 // exceptions, such as IME, where it sometimes returns true. | |
354 ret = true; | |
355 | |
356 if (np_event.event == WM_MOUSEMOVE) { | |
357 current_windowless_cursor_.InitFromExternalCursor(GetCursor()); | |
358 // Snag a reference to the current cursor ASAP in case the plugin modified | |
359 // it. There is a nasty race condition here with the multiprocess browser | |
360 // as someone might be setting the cursor in the main process as well. | |
361 current_windowless_cursor_.GetCursorInfo(cursor_info); | |
362 } | |
363 | |
364 handle_event_depth_--; | |
365 | |
366 return ret; | |
367 } | |
368 | |
369 } // namespace content | |
OLD | NEW |