| 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 |