1 // Copyright (c) 2011 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 "chrome/browser/renderer_host/render_widget_host_view_win.h" 6 7 #include <algorithm> 8 9 #include "base/command_line.h" 10 #include "base/i18n/rtl.h" 11 #include "base/metrics/histogram.h" 12 #include "base/process_util.h" 13 #include "base/threading/thread.h" 14 #include "base/win/scoped_comptr.h" 15 #include "base/win/scoped_gdi_object.h" 16 #include "base/win/wrapped_window_proc.h" 17 #include "chrome/browser/accessibility/browser_accessibility_manager.h" 18 #include "chrome/browser/accessibility/browser_accessibility_state.h" 19 #include "chrome/browser/accessibility/browser_accessibility_win.h" 20 #include "chrome/browser/browser_trial.h" 21 #include "chrome/common/chrome_constants.h" 22 #include "chrome/common/chrome_switches.h" 23 #include "chrome/common/render_messages.h" 24 #include "content/browser/browser_thread.h" 25 #include "content/browser/plugin_process_host.h" 26 #include "content/browser/renderer_host/backing_store.h" 27 #include "content/browser/renderer_host/backing_store_win.h" 28 #include "content/browser/renderer_host/render_process_host.h" 29 #include "content/browser/renderer_host/render_widget_host.h" 30 #include "content/common/native_web_keyboard_event.h" 31 #include "content/common/notification_service.h" 32 #include "content/common/plugin_messages.h" 33 #include "content/common/view_messages.h" 34 #include "grit/webkit_resources.h" 35 #include "skia/ext/skia_utils_win.h" 36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" 37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" 38 #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h" 39 #include "ui/base/ime/composition_text.h" 40 #include "ui/base/l10n/l10n_util.h" 41 #include "ui/base/l10n/l10n_util_win.h" 42 #include "ui/base/resource/resource_bundle.h" 43 #include "ui/base/view_prop.h" 44 #include "ui/base/win/hwnd_util.h" 45 #include "ui/gfx/canvas.h" 46 #include "ui/gfx/canvas_skia.h" 47 #include "ui/gfx/gdi_util.h" 48 #include "ui/gfx/rect.h" 49 #include "views/accessibility/native_view_accessibility_win.h" 50 #include "views/focus/focus_manager.h" 51 #include "views/focus/focus_util_win.h" 52 // Included for views::kReflectedMessage - TODO(beng): move this to win_util.h! 53 #include "views/widget/widget_win.h" 54 #include "webkit/glue/webaccessibility.h" 55 #include "webkit/glue/webcursor.h" 56 #include "webkit/plugins/npapi/plugin_constants_win.h" 57 #include "webkit/plugins/npapi/webplugin.h" 58 #include "webkit/plugins/npapi/webplugin_delegate_impl.h" 59 60 using base::TimeDelta; 61 using base::TimeTicks; 62 using ui::ViewProp; 63 using WebKit::WebInputEvent; 64 using WebKit::WebInputEventFactory; 65 using WebKit::WebMouseEvent; 66 using WebKit::WebTextDirection; 67 using webkit::npapi::WebPluginGeometry; 68 69 const wchar_t kRenderWidgetHostHWNDClass[] = L"Chrome_RenderWidgetHostHWND"; 70 71 namespace { 72 73 // Tooltips will wrap after this width. Yes, wrap. Imagine that! 74 const int kTooltipMaxWidthPixels = 300; 75 76 // Maximum number of characters we allow in a tooltip. 77 const int kMaxTooltipLength = 1024; 78 79 // A custom MSAA object id used to determine if a screen reader is actively 80 // listening for MSAA events. 81 const int kIdCustom = 1; 82 83 // The delay before the compositor host window is destroyed. This gives the GPU 84 // process a grace period to stop referencing it. 85 const int kDestroyCompositorHostWindowDelay = 10000; 86 87 const char* const kRenderWidgetHostViewKey = "__RENDER_WIDGET_HOST_VIEW__"; 88 89 // A callback function for EnumThreadWindows to enumerate and dismiss 90 // any owned popop windows 91 BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) { 92 const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg); 93 94 if (::IsWindowVisible(window)) { 95 const HWND owner = ::GetWindow(window, GW_OWNER); 96 if (toplevel_hwnd == owner) { 97 ::PostMessage(window, WM_CANCELMODE, 0, 0); 98 } 99 } 100 101 return TRUE; 102 } 103 104 class NotifyPluginProcessHostTask : public Task { 105 public: 106 NotifyPluginProcessHostTask(HWND window, HWND parent) 107 : window_(window), parent_(parent), tries_(kMaxTries) { } 108 109 private: 110 void Run() { 111 DWORD plugin_process_id; 112 bool found_starting_plugin_process = false; 113 GetWindowThreadProcessId(window_, &plugin_process_id); 114 for (BrowserChildProcessHost::Iterator iter( 115 ChildProcessInfo::PLUGIN_PROCESS); 116 !iter.Done(); ++iter) { 117 PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter); 118 if (!plugin->handle()) { 119 found_starting_plugin_process = true; 120 continue; 121 } 122 if (base::GetProcId(plugin->handle()) == plugin_process_id) { 123 plugin->AddWindow(parent_); 124 return; 125 } 126 } 127 128 if (found_starting_plugin_process) { 129 // A plugin process has started but we don't have its handle yet. Since 130 // it's most likely the one for this plugin, try a few more times after a 131 // delay. 132 if (tries_--) { 133 MessageLoop::current()->PostDelayedTask(FROM_HERE, this, kTryDelayMs); 134 return; 135 } 136 } 137 138 // The plugin process might have died in the time to execute the task, don't 139 // leak the HWND. 140 PostMessage(parent_, WM_CLOSE, 0, 0); 141 } 142 143 HWND window_; // Plugin HWND, created and destroyed in the plugin process. 144 HWND parent_; // Parent HWND, created and destroyed on the browser UI thread. 145 146 int tries_; 147 148 // How many times we try to find a PluginProcessHost whose process matches 149 // the HWND. 150 static const int kMaxTries = 5; 151 // How long to wait between each try. 152 static const int kTryDelayMs = 200; 153 }; 154 155 // Windows callback for OnDestroy to detach the plugin windows. 156 BOOL CALLBACK DetachPluginWindowsCallback(HWND window, LPARAM param) { 157 if (webkit::npapi::WebPluginDelegateImpl::IsPluginDelegateWindow(window) && 158 !IsHungAppWindow(window)) { 159 ::ShowWindow(window, SW_HIDE); 160 SetParent(window, NULL); 161 } 162 return TRUE; 163 } 164 165 // Draw the contents of |backing_store_dc| onto |paint_rect| with a 70% grey 166 // filter. 167 void DrawDeemphasized(const SkColor& color, 168 const gfx::Rect& paint_rect, 169 HDC backing_store_dc, 170 HDC paint_dc) { 171 gfx::CanvasSkia canvas(paint_rect.width(), paint_rect.height(), true); 172 HDC dc = canvas.beginPlatformPaint(); 173 BitBlt(dc, 174 0, 175 0, 176 paint_rect.width(), 177 paint_rect.height(), 178 backing_store_dc, 179 paint_rect.x(), 180 paint_rect.y(), 181 SRCCOPY); 182 canvas.endPlatformPaint(); 183 canvas.FillRectInt(color, 0, 0, paint_rect.width(), paint_rect.height()); 184 canvas.getTopPlatformDevice().drawToHDC(paint_dc, paint_rect.x(), 185 paint_rect.y(), NULL); 186 } 187 188 // The plugin wrapper window which lives in the browser process has this proc 189 // as its window procedure. We only handle the WM_PARENTNOTIFY message sent by 190 // windowed plugins for mouse input. This is forwarded off to the wrappers 191 // parent which is typically the RVH window which turns on user gesture. 192 LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, 193 WPARAM wparam, LPARAM lparam) { 194 if (message == WM_PARENTNOTIFY) { 195 switch (LOWORD(wparam)) { 196 case WM_LBUTTONDOWN: 197 case WM_RBUTTONDOWN: 198 case WM_MBUTTONDOWN: 199 ::SendMessage(GetParent(window), message, wparam, lparam); 200 return 0; 201 default: 202 break; 203 } 204 } 205 return ::DefWindowProc(window, message, wparam, lparam); 206 } 207 208 } // namespace 209 210 // RenderWidgetHostView -------------------------------------------------------- 211 212 // static 213 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( 214 RenderWidgetHost* widget) { 215 return new RenderWidgetHostViewWin(widget); 216 } 217 218 /////////////////////////////////////////////////////////////////////////////// 219 // RenderWidgetHostViewWin, public: 220 221 RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) 222 : render_widget_host_(widget), 223 compositor_host_window_(NULL), 224 hide_compositor_window_at_next_paint_(false), 225 track_mouse_leave_(false), 226 ime_notification_(false), 227 capture_enter_key_(false), 228 is_hidden_(false), 229 about_to_validate_and_paint_(false), 230 close_on_deactivate_(false), 231 being_destroyed_(false), 232 tooltip_hwnd_(NULL), 233 tooltip_showing_(false), 234 shutdown_factory_(this), 235 parent_hwnd_(NULL), 236 is_loading_(false), 237 overlay_color_(0), 238 text_input_type_(WebKit::WebTextInputTypeNone) { 239 render_widget_host_->set_view(this); 240 registrar_.Add(this, 241 NotificationType::RENDERER_PROCESS_TERMINATED, 242 NotificationService::AllSources()); 243 } 244 245 RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { 246 ResetTooltip(); 247 } 248 249 void RenderWidgetHostViewWin::CreateWnd(HWND parent) { 250 Create(parent); // ATL function to create the window. 251 } 252 253 /////////////////////////////////////////////////////////////////////////////// 254 // RenderWidgetHostViewWin, RenderWidgetHostView implementation: 255 256 void RenderWidgetHostViewWin::InitAsPopup( 257 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { 258 parent_hwnd_ = parent_host_view->GetNativeView(); 259 close_on_deactivate_ = true; 260 Create(parent_hwnd_, NULL, NULL, WS_POPUP, WS_EX_TOOLWINDOW); 261 MoveWindow(pos.x(), pos.y(), pos.width(), pos.height(), TRUE); 262 // Popups are not activated. 263 ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); 264 } 265 266 void RenderWidgetHostViewWin::InitAsFullscreen() { 267 NOTIMPLEMENTED() << "Fullscreen not implemented on Win"; 268 } 269 270 RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { 271 return render_widget_host_; 272 } 273 274 void RenderWidgetHostViewWin::DidBecomeSelected() { 275 if (!is_hidden_) 276 return; 277 278 if (tab_switch_paint_time_.is_null()) 279 tab_switch_paint_time_ = TimeTicks::Now(); 280 is_hidden_ = false; 281 EnsureTooltip(); 282 render_widget_host_->WasRestored(); 283 } 284 285 void RenderWidgetHostViewWin::WasHidden() { 286 if (is_hidden_) 287 return; 288 289 // If we receive any more paint messages while we are hidden, we want to 290 // ignore them so we don't re-allocate the backing store. We will paint 291 // everything again when we become selected again. 292 is_hidden_ = true; 293 294 ResetTooltip(); 295 296 // If we have a renderer, then inform it that we are being hidden so it can 297 // reduce its resource utilization. 298 render_widget_host_->WasHidden(); 299 300 // TODO(darin): what about constrained windows? it doesn't look like they 301 // see a message when their parent is hidden. maybe there is something more 302 // generic we can do at the TabContents API level instead of relying on 303 // Windows messages. 304 } 305 306 void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { 307 SetBounds(gfx::Rect(GetViewBounds().origin(), size)); 308 } 309 310 void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) { 311 if (is_hidden_) 312 return; 313 314 // No SWP_NOREDRAW as autofill popups can move and the underneath window 315 // should redraw in that case. 316 UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS | 317 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE; 318 319 // If the style is not popup, you have to convert the point to client 320 // coordinate. 321 POINT point = { rect.x(), rect.y() }; 322 if (GetStyle() & WS_CHILD) 323 ScreenToClient(&point); 324 325 SetWindowPos(NULL, point.x, point.y, rect.width(), rect.height(), swp_flags); 326 render_widget_host_->WasResized(); 327 EnsureTooltip(); 328 } 329 330 gfx::NativeView RenderWidgetHostViewWin::GetNativeView() { 331 return m_hWnd; 332 } 333 334 void RenderWidgetHostViewWin::MovePluginWindows( 335 const std::vector<WebPluginGeometry>& plugin_window_moves) { 336 if (plugin_window_moves.empty()) 337 return; 338 339 bool oop_plugins = 340 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) && 341 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessPlugins); 342 343 HDWP defer_window_pos_info = 344 ::BeginDeferWindowPos(static_cast<int>(plugin_window_moves.size())); 345 346 if (!defer_window_pos_info) { 347 NOTREACHED(); 348 return; 349 } 350 351 for (size_t i = 0; i < plugin_window_moves.size(); ++i) { 352 unsigned long flags = 0; 353 const WebPluginGeometry& move = plugin_window_moves[i]; 354 HWND window = move.window; 355 356 // As the plugin parent window which lives on the browser UI thread is 357 // destroyed asynchronously, it is possible that we have a stale window 358 // sent in by the renderer for moving around. 359 // Note: get the parent before checking if the window is valid, to avoid a 360 // race condition where the window is destroyed after the check but before 361 // the GetParent call. 362 HWND parent = ::GetParent(window); 363 if (!::IsWindow(window)) 364 continue; 365 366 if (oop_plugins) { 367 if (parent == m_hWnd) { 368 // The plugin window is a direct child of this window, add an 369 // intermediate window that lives on this thread to speed up scrolling. 370 // Note this only works with out of process plugins since we depend on 371 // PluginProcessHost to destroy the intermediate HWNDs. 372 parent = ReparentWindow(window); 373 ::ShowWindow(window, SW_SHOW); // Window was created hidden. 374 } else if (::GetParent(parent) != m_hWnd) { 375 // The renderer should only be trying to move windows that are children 376 // of its render widget window. However, this may happen as a result of 377 // a race condition, so we ignore it and not kill the plugin process. 378 continue; 379 } 380 381 // We move the intermediate parent window which doesn't result in cross- 382 // process synchronous Windows messages. 383 window = parent; 384 } 385 386 if (move.visible) 387 flags |= SWP_SHOWWINDOW; 388 else 389 flags |= SWP_HIDEWINDOW; 390 391 if (move.rects_valid) { 392 HRGN hrgn = ::CreateRectRgn(move.clip_rect.x(), 393 move.clip_rect.y(), 394 move.clip_rect.right(), 395 move.clip_rect.bottom()); 396 gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); 397 398 // Note: System will own the hrgn after we call SetWindowRgn, 399 // so we don't need to call DeleteObject(hrgn) 400 ::SetWindowRgn(window, hrgn, !move.clip_rect.IsEmpty()); 401 } else { 402 flags |= SWP_NOMOVE; 403 flags |= SWP_NOSIZE; 404 } 405 406 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, 407 window, NULL, 408 move.window_rect.x(), 409 move.window_rect.y(), 410 move.window_rect.width(), 411 move.window_rect.height(), flags); 412 if (!defer_window_pos_info) { 413 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored."; 414 return; 415 } 416 } 417 418 ::EndDeferWindowPos(defer_window_pos_info); 419 } 420 421 HWND RenderWidgetHostViewWin::ReparentWindow(HWND window) { 422 static ATOM window_class = 0; 423 if (!window_class) { 424 WNDCLASSEX wcex; 425 wcex.cbSize = sizeof(WNDCLASSEX); 426 wcex.style = CS_DBLCLKS; 427 wcex.lpfnWndProc = base::win::WrappedWindowProc<PluginWrapperWindowProc>; 428 wcex.cbClsExtra = 0; 429 wcex.cbWndExtra = 0; 430 wcex.hInstance = GetModuleHandle(NULL); 431 wcex.hIcon = 0; 432 wcex.hCursor = 0; 433 wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); 434 wcex.lpszMenuName = 0; 435 wcex.lpszClassName = webkit::npapi::kWrapperNativeWindowClassName; 436 wcex.hIconSm = 0; 437 window_class = RegisterClassEx(&wcex); 438 } 439 DCHECK(window_class); 440 441 HWND parent = CreateWindowEx( 442 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, 443 MAKEINTATOM(window_class), 0, 444 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 445 0, 0, 0, 0, ::GetParent(window), 0, GetModuleHandle(NULL), 0); 446 ui::CheckWindowCreated(parent); 447 ::SetParent(window, parent); 448 BrowserThread::PostTask( 449 BrowserThread::IO, FROM_HERE, 450 new NotifyPluginProcessHostTask(window, parent)); 451 return parent; 452 } 453 454 static BOOL CALLBACK AddChildWindowToVector(HWND hwnd, LPARAM lparam) { 455 std::vector<HWND>* vector = reinterpret_cast<std::vector<HWND>*>(lparam); 456 vector->push_back(hwnd); 457 return TRUE; 458 } 459 460 void RenderWidgetHostViewWin::CleanupCompositorWindow() { 461 if (!compositor_host_window_) 462 return; 463 464 // Hide the compositor and parent it to the desktop rather than destroying 465 // it immediately. The GPU process has a grace period to stop accessing the 466 // window. TODO(apatrick): the GPU process should acknowledge that it has 467 // finished with the window handle and the browser process should destroy it 468 // at that point. 469 ::ShowWindow(compositor_host_window_, SW_HIDE); 470 ::SetParent(compositor_host_window_, NULL); 471 472 BrowserThread::PostDelayedTask( 473 BrowserThread::UI, 474 FROM_HERE, 475 NewRunnableFunction(::DestroyWindow, compositor_host_window_), 476 kDestroyCompositorHostWindowDelay); 477 478 compositor_host_window_ = NULL; 479 } 480 481 bool RenderWidgetHostViewWin::IsActivatable() const { 482 // Popups should not be activated. 483 return popup_type_ == WebKit::WebPopupTypeNone; 484 } 485 486 void RenderWidgetHostViewWin::Focus() { 487 if (IsWindow()) 488 SetFocus(); 489 } 490 491 void RenderWidgetHostViewWin::Blur() { 492 views::FocusManager* focus_manager = 493 views::FocusManager::GetFocusManagerForNativeView(m_hWnd); 494 // We don't have a FocusManager if we are hidden. 495 if (focus_manager) 496 focus_manager->ClearFocus(); 497 } 498 499 bool RenderWidgetHostViewWin::HasFocus() { 500 return ::GetFocus() == m_hWnd; 501 } 502 503 void RenderWidgetHostViewWin::Show() { 504 DCHECK(parent_hwnd_); 505 DCHECK(parent_hwnd_ != GetDesktopWindow()); 506 SetParent(parent_hwnd_); 507 ShowWindow(SW_SHOW); 508 509 // Save away our HWND in the parent window as a property so that the 510 // accessibility code can find it. 511 accessibility_prop_.reset(new ViewProp( 512 GetParent(), 513 views::kViewsNativeHostPropForAccessibility, 514 m_hWnd)); 515 516 DidBecomeSelected(); 517 } 518 519 void RenderWidgetHostViewWin::Hide() { 520 if (GetParent() == GetDesktopWindow()) { 521 LOG(WARNING) << "Hide() called twice in a row: " << this << ":" << 522 parent_hwnd_ << ":" << GetParent(); 523 return; 524 } 525 526 accessibility_prop_.reset(); 527 528 if (::GetFocus() == m_hWnd) 529 ::SetFocus(NULL); 530 ShowWindow(SW_HIDE); 531 532 // Cache the old parent, then orphan the window so we stop receiving messages 533 parent_hwnd_ = GetParent(); 534 SetParent(NULL); 535 536 WasHidden(); 537 } 538 539 bool RenderWidgetHostViewWin::IsShowing() { 540 return !!IsWindowVisible(); 541 } 542 543 gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { 544 CRect window_rect; 545 GetWindowRect(&window_rect); 546 return gfx::Rect(window_rect); 547 } 548 549 void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) { 550 current_cursor_ = cursor; 551 UpdateCursorIfOverSelf(); 552 } 553 554 void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() { 555 static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW); 556 static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING); 557 static HINSTANCE module_handle = 558 GetModuleHandle(chrome::kBrowserResourcesDll); 559 560 // If the mouse is over our HWND, then update the cursor state immediately. 561 CPoint pt; 562 GetCursorPos(&pt); 563 if (WindowFromPoint(pt) == m_hWnd) { 564 BOOL result = ::ScreenToClient(m_hWnd, &pt); 565 DCHECK(result); 566 // We cannot pass in NULL as the module handle as this would only work for 567 // standard win32 cursors. We can also receive cursor types which are 568 // defined as webkit resources. We need to specify the module handle of 569 // chrome.dll while loading these cursors. 570 HCURSOR display_cursor = current_cursor_.GetCursor(module_handle); 571 572 // If a page is in the loading state, we want to show the Arrow+Hourglass 573 // cursor only when the current cursor is the ARROW cursor. In all other 574 // cases we should continue to display the current cursor. 575 if (is_loading_ && display_cursor == kCursorArrow) 576 display_cursor = kCursorAppStarting; 577 578 SetCursor(display_cursor); 579 } 580 } 581 582 void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { 583 is_loading_ = is_loading; 584 UpdateCursorIfOverSelf(); 585 } 586 587 void RenderWidgetHostViewWin::ImeUpdateTextInputState( 588 WebKit::WebTextInputType type, 589 const gfx::Rect& caret_rect) { 590 if (text_input_type_ != type) { 591 text_input_type_ = type; 592 if (type == WebKit::WebTextInputTypeText) 593 ime_input_.EnableIME(m_hWnd); 594 else 595 ime_input_.DisableIME(m_hWnd); 596 } 597 598 // Only update caret position if the input method is enabled. 599 if (type == WebKit::WebTextInputTypeText) 600 ime_input_.UpdateCaretRect(m_hWnd, caret_rect); 601 } 602 603 void RenderWidgetHostViewWin::ImeCancelComposition() { 604 ime_input_.CancelIME(m_hWnd); 605 } 606 607 BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lparam) { 608 if (!webkit::npapi::WebPluginDelegateImpl::IsPluginDelegateWindow(hwnd)) 609 return TRUE; 610 611 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); 612 static UINT msg = RegisterWindowMessage(webkit::npapi::kPaintMessageName); 613 WPARAM wparam = rect->x() << 16 | rect->y(); 614 lparam = rect->width() << 16 | rect->height(); 615 616 // SendMessage gets the message across much quicker than PostMessage, since it 617 // doesn't get queued. When the plugin thread calls PeekMessage or other 618 // Win32 APIs, sent messages are dispatched automatically. 619 SendNotifyMessage(hwnd, msg, wparam, lparam); 620 621 return TRUE; 622 } 623 624 void RenderWidgetHostViewWin::Redraw() { 625 RECT damage_bounds; 626 GetUpdateRect(&damage_bounds, FALSE); 627 628 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); 629 GetUpdateRgn(damage_region, FALSE); 630 631 // Paint the invalid region synchronously. Our caller will not paint again 632 // until we return, so by painting to the screen here, we ensure effective 633 // rate-limiting of backing store updates. This helps a lot on pages that 634 // have animations or fairly expensive layout (e.g., google maps). 635 // 636 // We paint this window synchronously, however child windows (i.e. plugins) 637 // are painted asynchronously. By avoiding synchronous cross-process window 638 // message dispatching we allow scrolling to be smooth, and also avoid the 639 // browser process locking up if the plugin process is hung. 640 // 641 RedrawWindow(NULL, damage_region, RDW_UPDATENOW | RDW_NOCHILDREN); 642 643 // Send the invalid rect in screen coordinates. 644 gfx::Rect screen_rect = GetViewBounds(); 645 gfx::Rect invalid_screen_rect(damage_bounds); 646 invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y()); 647 648 LPARAM lparam = reinterpret_cast<LPARAM>(&invalid_screen_rect); 649 EnumChildWindows(m_hWnd, EnumChildProc, lparam); 650 } 651 652 void RenderWidgetHostViewWin::DidUpdateBackingStore( 653 const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, 654 const std::vector<gfx::Rect>& copy_rects) { 655 if (is_hidden_) 656 return; 657 658 // Schedule invalidations first so that the ScrollWindowEx call is closer to 659 // Redraw. That minimizes chances of "flicker" resulting if the screen 660 // refreshes before we have a chance to paint the exposed area. Somewhat 661 // surprisingly, this ordering matters. 662 663 for (size_t i = 0; i < copy_rects.size(); ++i) 664 InvalidateRect(©_rects[i].ToRECT(), false); 665 666 if (!scroll_rect.IsEmpty()) { 667 RECT clip_rect = scroll_rect.ToRECT(); 668 ScrollWindowEx(scroll_dx, scroll_dy, NULL, &clip_rect, NULL, NULL, 669 SW_INVALIDATE); 670 } 671 672 if (!about_to_validate_and_paint_) 673 Redraw(); 674 } 675 676 void RenderWidgetHostViewWin::RenderViewGone(base::TerminationStatus status, 677 int error_code) { 678 // TODO(darin): keep this around, and draw sad-tab into it. 679 UpdateCursorIfOverSelf(); 680 being_destroyed_ = true; 681 CleanupCompositorWindow(); 682 DestroyWindow(); 683 } 684 685 void RenderWidgetHostViewWin::WillWmDestroy() { 686 CleanupCompositorWindow(); 687 } 688 689 void RenderWidgetHostViewWin::WillDestroyRenderWidget(RenderWidgetHost* rwh) { 690 if (rwh == render_widget_host_) 691 render_widget_host_ = NULL; 692 } 693 694 void RenderWidgetHostViewWin::Destroy() { 695 // We've been told to destroy. 696 // By clearing close_on_deactivate_, we prevent further deactivations 697 // (caused by windows messages resulting from the DestroyWindow) from 698 // triggering further destructions. The deletion of this is handled by 699 // OnFinalMessage(); 700 close_on_deactivate_ = false; 701 being_destroyed_ = true; 702 CleanupCompositorWindow(); 703 DestroyWindow(); 704 } 705 706 void RenderWidgetHostViewWin::SetTooltipText(const std::wstring& tooltip_text) { 707 // Clamp the tooltip length to kMaxTooltipLength so that we don't 708 // accidentally DOS the user with a mega tooltip (since Windows doesn't seem 709 // to do this itself). 710 const std::wstring& new_tooltip_text = 711 l10n_util::TruncateString(tooltip_text, kMaxTooltipLength); 712 713 if (new_tooltip_text != tooltip_text_) { 714 tooltip_text_ = new_tooltip_text; 715 716 // Need to check if the tooltip is already showing so that we don't 717 // immediately show the tooltip with no delay when we move the mouse from 718 // a region with no tooltip to a region with a tooltip. 719 if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { 720 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); 721 ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); 722 } 723 } else { 724 // Make sure the tooltip gets closed after TTN_POP gets sent. For some 725 // reason this doesn't happen automatically, so moving the mouse around 726 // within the same link/image/etc doesn't cause the tooltip to re-appear. 727 if (!tooltip_showing_) { 728 if (::IsWindow(tooltip_hwnd_)) 729 ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); 730 } 731 } 732 } 733 734 BackingStore* RenderWidgetHostViewWin::AllocBackingStore( 735 const gfx::Size& size) { 736 return new BackingStoreWin(render_widget_host_, size); 737 } 738 739 void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) { 740 RenderWidgetHostView::SetBackground(background); 741 Send(new ViewMsg_SetBackground(render_widget_host_->routing_id(), 742 background)); 743 } 744 745 bool RenderWidgetHostViewWin::ContainsNativeView( 746 gfx::NativeView native_view) const { 747 if (m_hWnd == native_view) 748 return true; 749 750 // Traverse the set of parents of the given view to determine if native_view 751 // is a descendant of this window. 752 HWND parent_window = ::GetParent(native_view); 753 while (parent_window) { 754 if (parent_window == m_hWnd) 755 return true; 756 parent_window = ::GetParent(parent_window); 757 } 758 759 return false; 760 } 761 762 void RenderWidgetHostViewWin::SetVisuallyDeemphasized(const SkColor* color, 763 bool animate) { 764 // |animate| is not yet implemented, and currently isn't used. 765 CHECK(!animate); 766 767 SkColor overlay_color = color ? *color : 0; 768 if (overlay_color_ == overlay_color) 769 return; 770 overlay_color_ = overlay_color; 771 772 InvalidateRect(NULL, FALSE); 773 } 774 775 /////////////////////////////////////////////////////////////////////////////// 776 // RenderWidgetHostViewWin, private: 777 778 LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) { 779 // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale 780 // of a browser process. 781 OnInputLangChange(0, 0); 782 // Marks that window as supporting mouse-wheel messages rerouting so it is 783 // scrolled when under the mouse pointer even if inactive. 784 props_.push_back(views::SetWindowSupportsRerouteMouseWheel(m_hWnd)); 785 props_.push_back(new ViewProp(m_hWnd, kRenderWidgetHostViewKey, 786 static_cast<RenderWidgetHostView*>(this))); 787 // Save away our HWND in the parent window as a property so that the 788 // accessibility code can find it. 789 accessibility_prop_.reset(new ViewProp( 790 GetParent(), 791 views::kViewsNativeHostPropForAccessibility, 792 m_hWnd)); 793 794 return 0; 795 } 796 797 void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized, 798 HWND window) { 799 // If the container is a popup, clicking elsewhere on screen should close the 800 // popup. 801 if (close_on_deactivate_ && action == WA_INACTIVE) { 802 // Send a windows message so that any derived classes 803 // will get a change to override the default handling 804 SendMessage(WM_CANCELMODE); 805 } 806 } 807 808 void RenderWidgetHostViewWin::OnDestroy() { 809 // When a tab is closed all its child plugin windows are destroyed 810 // automatically. This happens before plugins get any notification that its 811 // instances are tearing down. 812 // 813 // Plugins like Quicktime assume that their windows will remain valid as long 814 // as they have plugin instances active. Quicktime crashes in this case 815 // because its windowing code cleans up an internal data structure that the 816 // handler for NPP_DestroyStream relies on. 817 // 818 // The fix is to detach plugin windows from web contents when it is going 819 // away. This will prevent the plugin windows from getting destroyed 820 // automatically. The detached plugin windows will get cleaned up in proper 821 // sequence as part of the usual cleanup when the plugin instance goes away. 822 EnumChildWindows(m_hWnd, DetachPluginWindowsCallback, NULL); 823 824 props_.reset(); 825 826 CleanupCompositorWindow(); 827 828 ResetTooltip(); 829 TrackMouseLeave(false); 830 } 831 832 void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { 833 DCHECK(render_widget_host_->process()->HasConnection()); 834 835 // If the GPU process is rendering directly into the View, 836 // call the compositor directly. 837 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(); 838 if (render_widget_host->is_accelerated_compositing_active()) { 839 // We initialize paint_dc here so that BeginPaint()/EndPaint() 840 // get called to validate the region. 841 CPaintDC paint_dc(m_hWnd); 842 render_widget_host_->ScheduleComposite(); 843 return; 844 } 845 846 about_to_validate_and_paint_ = true; 847 BackingStoreWin* backing_store = static_cast<BackingStoreWin*>( 848 render_widget_host_->GetBackingStore(true)); 849 850 // We initialize |paint_dc| (and thus call BeginPaint()) after calling 851 // GetBackingStore(), so that if it updates the invalid rect we'll catch the 852 // changes and repaint them. 853 about_to_validate_and_paint_ = false; 854 855 // Grab the region to paint before creation of paint_dc since it clears the 856 // damage region. 857 base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); 858 GetUpdateRgn(damage_region, FALSE); 859 860 if (hide_compositor_window_at_next_paint_) { 861 ::ShowWindow(compositor_host_window_, SW_HIDE); 862 hide_compositor_window_at_next_paint_ = false; 863 } 864 865 CPaintDC paint_dc(m_hWnd); 866 867 gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint); 868 if (damaged_rect.IsEmpty()) 869 return; 870 871 if (backing_store) { 872 gfx::Rect bitmap_rect(gfx::Point(), backing_store->size()); 873 874 bool manage_colors = BackingStoreWin::ColorManagementEnabled(); 875 if (manage_colors) 876 SetICMMode(paint_dc.m_hDC, ICM_ON); 877 878 // Blit only the damaged regions from the backing store. 879 DWORD data_size = GetRegionData(damage_region, 0, NULL); 880 scoped_array<char> region_data_buf(new char[data_size]); 881 RGNDATA* region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get()); 882 GetRegionData(damage_region, data_size, region_data); 883 884 RECT* region_rects = reinterpret_cast<RECT*>(region_data->Buffer); 885 for (DWORD i = 0; i < region_data->rdh.nCount; ++i) { 886 gfx::Rect paint_rect = bitmap_rect.Intersect(gfx::Rect(region_rects[i])); 887 if (!paint_rect.IsEmpty()) { 888 if (SkColorGetA(overlay_color_) > 0) { 889 DrawDeemphasized(overlay_color_, 890 paint_rect, 891 backing_store->hdc(), 892 paint_dc.m_hDC); 893 } else { 894 BitBlt(paint_dc.m_hDC, 895 paint_rect.x(), 896 paint_rect.y(), 897 paint_rect.width(), 898 paint_rect.height(), 899 backing_store->hdc(), 900 paint_rect.x(), 901 paint_rect.y(), 902 SRCCOPY); 903 } 904 } 905 } 906 907 if (manage_colors) 908 SetICMMode(paint_dc.m_hDC, ICM_OFF); 909 910 // Fill the remaining portion of the damaged_rect with the background 911 if (damaged_rect.right() > bitmap_rect.right()) { 912 RECT r; 913 r.left = std::max(bitmap_rect.right(), damaged_rect.x()); 914 r.right = damaged_rect.right(); 915 r.top = damaged_rect.y(); 916 r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom()); 917 DrawBackground(r, &paint_dc); 918 } 919 if (damaged_rect.bottom() > bitmap_rect.bottom()) { 920 RECT r; 921 r.left = damaged_rect.x(); 922 r.right = damaged_rect.right(); 923 r.top = std::max(bitmap_rect.bottom(), damaged_rect.y()); 924 r.bottom = damaged_rect.bottom(); 925 DrawBackground(r, &paint_dc); 926 } 927 if (!whiteout_start_time_.is_null()) { 928 TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_; 929 UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); 930 931 // Reset the start time to 0 so that we start recording again the next 932 // time the backing store is NULL... 933 whiteout_start_time_ = TimeTicks(); 934 } 935 if (!tab_switch_paint_time_.is_null()) { 936 TimeDelta tab_switch_paint_duration = TimeTicks::Now() - 937 tab_switch_paint_time_; 938 UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", 939 tab_switch_paint_duration); 940 // Reset tab_switch_paint_time_ to 0 so future tab selections are 941 // recorded. 942 tab_switch_paint_time_ = TimeTicks(); 943 } 944 } else { 945 DrawBackground(paint_dc.m_ps.rcPaint, &paint_dc); 946 if (whiteout_start_time_.is_null()) 947 whiteout_start_time_ = TimeTicks::Now(); 948 } 949 } 950 951 void RenderWidgetHostViewWin::DrawBackground(const RECT& dirty_rect, 952 CPaintDC* dc) { 953 if (!background_.empty()) { 954 gfx::CanvasSkia canvas(dirty_rect.right - dirty_rect.left, 955 dirty_rect.bottom - dirty_rect.top, 956 true); // opaque 957 canvas.TranslateInt(-dirty_rect.left, -dirty_rect.top); 958 959 const RECT& dc_rect = dc->m_ps.rcPaint; 960 canvas.TileImageInt(background_, 0, 0, 961 dc_rect.right - dc_rect.left, 962 dc_rect.bottom - dc_rect.top); 963 964 canvas.getTopPlatformDevice().drawToHDC(*dc, dirty_rect.left, 965 dirty_rect.top, NULL); 966 } else { 967 HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); 968 dc->FillRect(&dirty_rect, white_brush); 969 } 970 } 971 972 void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) { 973 // Do nothing. This suppresses the resize corner that Windows would 974 // otherwise draw for us. 975 } 976 977 LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) { 978 return 1; 979 } 980 981 LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code, 982 UINT mouse_message_id) { 983 UpdateCursorIfOverSelf(); 984 return 0; 985 } 986 987 void RenderWidgetHostViewWin::OnSetFocus(HWND window) { 988 views::FocusManager::GetWidgetFocusManager()->OnWidgetFocusEvent(window, 989 m_hWnd); 990 if (browser_accessibility_manager_.get()) 991 browser_accessibility_manager_->GotFocus(); 992 if (render_widget_host_) 993 render_widget_host_->GotFocus(); 994 } 995 996 void RenderWidgetHostViewWin::OnKillFocus(HWND window) { 997 views::FocusManager::GetWidgetFocusManager()->OnWidgetFocusEvent(m_hWnd, 998 window); 999 1000 if (render_widget_host_) 1001 render_widget_host_->Blur(); 1002 } 1003 1004 void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) { 1005 if (render_widget_host_) 1006 render_widget_host_->LostCapture(); 1007 } 1008 1009 void RenderWidgetHostViewWin::OnCancelMode() { 1010 if (render_widget_host_) 1011 render_widget_host_->LostCapture(); 1012 1013 if (close_on_deactivate_ && shutdown_factory_.empty()) { 1014 // Dismiss popups and menus. We do this asynchronously to avoid changing 1015 // activation within this callstack, which may interfere with another window 1016 // being activated. We can synchronously hide the window, but we need to 1017 // not change activation while doing so. 1018 SetWindowPos(NULL, 0, 0, 0, 0, 1019 SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | 1020 SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); 1021 MessageLoop::current()->PostTask(FROM_HERE, 1022 shutdown_factory_.NewRunnableMethod( 1023 &RenderWidgetHostViewWin::ShutdownHost)); 1024 } 1025 } 1026 1027 void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set, 1028 HKL input_language_id) { 1029 // Send the given Locale ID to the ImeInput object and retrieves whether 1030 // or not the current input context has IMEs. 1031 // If the current input context has IMEs, a browser process has to send a 1032 // request to a renderer process that it needs status messages about 1033 // the focused edit control from the renderer process. 1034 // On the other hand, if the current input context does not have IMEs, the 1035 // browser process also has to send a request to the renderer process that 1036 // it does not need the status messages any longer. 1037 // To minimize the number of this notification request, we should check if 1038 // the browser process is actually retrieving the status messages (this 1039 // state is stored in ime_notification_) and send a request only if the 1040 // browser process has to update this status, its details are listed below: 1041 // * If a browser process is not retrieving the status messages, 1042 // (i.e. ime_notification_ == false), 1043 // send this request only if the input context does have IMEs, 1044 // (i.e. ime_status == true); 1045 // When it successfully sends the request, toggle its notification status, 1046 // (i.e.ime_notification_ = !ime_notification_ = true). 1047 // * If a browser process is retrieving the status messages 1048 // (i.e. ime_notification_ == true), 1049 // send this request only if the input context does not have IMEs, 1050 // (i.e. ime_status == false). 1051 // When it successfully sends the request, toggle its notification status, 1052 // (i.e.ime_notification_ = !ime_notification_ = false). 1053 // To analyze the above actions, we can optimize them into the ones 1054 // listed below: 1055 // 1 Sending a request only if ime_status_ != ime_notification_, and; 1056 // 2 Copying ime_status to ime_notification_ if it sends the request 1057 // successfully (because Action 1 shows ime_status = !ime_notification_.) 1058 bool ime_status = ime_input_.SetInputLanguage(); 1059 if (ime_status != ime_notification_) { 1060 if (render_widget_host_) { 1061 render_widget_host_->SetInputMethodActive(ime_status); 1062 ime_notification_ = ime_status; 1063 } 1064 } 1065 } 1066 1067 void RenderWidgetHostViewWin::OnThemeChanged() { 1068 if (render_widget_host_) 1069 render_widget_host_->SystemThemeChanged(); 1070 } 1071 1072 LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) { 1073 if (tooltip_hwnd_ == NULL) 1074 return 0; 1075 1076 switch (header->code) { 1077 case TTN_GETDISPINFO: { 1078 NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header); 1079 tooltip_info->szText[0] = L'\0'; 1080 tooltip_info->lpszText = const_cast<wchar_t*>(tooltip_text_.c_str()); 1081 ::SendMessage( 1082 tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels); 1083 SetMsgHandled(TRUE); 1084 break; 1085 } 1086 case TTN_POP: 1087 tooltip_showing_ = false; 1088 SetMsgHandled(TRUE); 1089 break; 1090 case TTN_SHOW: 1091 tooltip_showing_ = true; 1092 SetMsgHandled(TRUE); 1093 break; 1094 } 1095 return 0; 1096 } 1097 1098 LRESULT RenderWidgetHostViewWin::OnImeSetContext( 1099 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 1100 if (!render_widget_host_) 1101 return 0; 1102 1103 // We need status messages about the focused input control from a 1104 // renderer process when: 1105 // * the current input context has IMEs, and; 1106 // * an application is activated. 1107 // This seems to tell we should also check if the current input context has 1108 // IMEs before sending a request, however, this WM_IME_SETCONTEXT is 1109 // fortunately sent to an application only while the input context has IMEs. 1110 // Therefore, we just start/stop status messages according to the activation 1111 // status of this application without checks. 1112 bool activated = (wparam == TRUE); 1113 if (render_widget_host_) { 1114 render_widget_host_->SetInputMethodActive(activated); 1115 ime_notification_ = activated; 1116 } 1117 1118 if (ime_notification_) 1119 ime_input_.CreateImeWindow(m_hWnd); 1120 1121 ime_input_.CleanupComposition(m_hWnd); 1122 return ime_input_.SetImeWindowStyle( 1123 m_hWnd, message, wparam, lparam, &handled); 1124 } 1125 1126 LRESULT RenderWidgetHostViewWin::OnImeStartComposition( 1127 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 1128 if (!render_widget_host_) 1129 return 0; 1130 1131 // Reset the composition status and create IME windows. 1132 ime_input_.CreateImeWindow(m_hWnd); 1133 ime_input_.ResetComposition(m_hWnd); 1134 // We have to prevent WTL from calling ::DefWindowProc() because the function 1135 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to 1136 // over-write the position of IME windows. 1137 handled = TRUE; 1138 return 0; 1139 } 1140 1141 LRESULT RenderWidgetHostViewWin::OnImeComposition( 1142 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 1143 if (!render_widget_host_) 1144 return 0; 1145 1146 // At first, update the position of the IME window. 1147 ime_input_.UpdateImeWindow(m_hWnd); 1148 1149 // ui::CompositionUnderline should be identical to 1150 // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely. 1151 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) == 1152 sizeof(WebKit::WebCompositionUnderline), 1153 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); 1154 1155 // Retrieve the result string and its attributes of the ongoing composition 1156 // and send it to a renderer process. 1157 ui::CompositionText composition; 1158 if (ime_input_.GetResult(m_hWnd, lparam, &composition.text)) { 1159 render_widget_host_->ImeConfirmComposition(composition.text); 1160 ime_input_.ResetComposition(m_hWnd); 1161 // Fall though and try reading the composition string. 1162 // Japanese IMEs send a message containing both GCS_RESULTSTR and 1163 // GCS_COMPSTR, which means an ongoing composition has been finished 1164 // by the start of another composition. 1165 } 1166 // Retrieve the composition string and its attributes of the ongoing 1167 // composition and send it to a renderer process. 1168 if (ime_input_.GetComposition(m_hWnd, lparam, &composition)) { 1169 // TODO(suzhe): due to a bug of webkit, we can't use selection range with 1170 // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 1171 composition.selection = ui::Range(composition.selection.end()); 1172 1173 // TODO(suzhe): convert both renderer_host and renderer to use 1174 // ui::CompositionText. 1175 const std::vector<WebKit::WebCompositionUnderline>& underlines = 1176 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( 1177 composition.underlines); 1178 render_widget_host_->ImeSetComposition( 1179 composition.text, underlines, 1180 composition.selection.start(), composition.selection.end()); 1181 } 1182 // We have to prevent WTL from calling ::DefWindowProc() because we do not 1183 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. 1184 handled = TRUE; 1185 return 0; 1186 } 1187 1188 LRESULT RenderWidgetHostViewWin::OnImeEndComposition( 1189 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 1190 if (!render_widget_host_) 1191 return 0; 1192 1193 if (ime_input_.is_composing()) { 1194 // A composition has been ended while there is an ongoing composition, 1195 // i.e. the ongoing composition has been canceled. 1196 // We need to reset the composition status both of the ImeInput object and 1197 // of the renderer process. 1198 render_widget_host_->ImeCancelComposition(); 1199 ime_input_.ResetComposition(m_hWnd); 1200 } 1201 ime_input_.DestroyImeWindow(m_hWnd); 1202 // Let WTL call ::DefWindowProc() and release its resources. 1203 handled = FALSE; 1204 return 0; 1205 } 1206 1207 LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam, 1208 LPARAM lparam, BOOL& handled) { 1209 handled = TRUE; 1210 1211 if (::IsWindow(tooltip_hwnd_)) { 1212 // Forward mouse events through to the tooltip window 1213 MSG msg; 1214 msg.hwnd = m_hWnd; 1215 msg.message = message; 1216 msg.wParam = wparam; 1217 msg.lParam = lparam; 1218 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL, 1219 reinterpret_cast<LPARAM>(&msg)); 1220 } 1221 1222 // TODO(jcampan): I am not sure if we should forward the message to the 1223 // TabContents first in the case of popups. If we do, we would need to 1224 // convert the click from the popup window coordinates to the TabContents' 1225 // window coordinates. For now we don't forward the message in that case to 1226 // address bug #907474. 1227 // Note: GetParent() on popup windows returns the top window and not the 1228 // parent the window was created with (the parent and the owner of the popup 1229 // is the first non-child view of the view that was specified to the create 1230 // call). So the TabContents window would have to be specified to the 1231 // RenderViewHostHWND as there is no way to retrieve it from the HWND. 1232 if (!close_on_deactivate_) { // Don't forward if the container is a popup. 1233 switch (message) { 1234 case WM_LBUTTONDOWN: 1235 case WM_MBUTTONDOWN: 1236 case WM_RBUTTONDOWN: 1237 // Finish the ongoing composition whenever a mouse click happens. 1238 // It matches IE's behavior. 1239 ime_input_.CleanupComposition(m_hWnd); 1240 // Fall through. 1241 case WM_MOUSEMOVE: 1242 case WM_MOUSELEAVE: { 1243 // Give the TabContents first crack at the message. It may want to 1244 // prevent forwarding to the renderer if some higher level browser 1245 // functionality is invoked. 1246 LPARAM parent_msg_lparam = lparam; 1247 if (message != WM_MOUSELEAVE) { 1248 // For the messages except WM_MOUSELEAVE, before forwarding them to 1249 // parent window, we should adjust cursor position from client 1250 // coordinates in current window to client coordinates in its parent 1251 // window. 1252 CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); 1253 ClientToScreen(&cursor_pos); 1254 GetParent().ScreenToClient(&cursor_pos); 1255 parent_msg_lparam = MAKELPARAM(cursor_pos.x, cursor_pos.y); 1256 } 1257 if (SendMessage(GetParent(), message, wparam, parent_msg_lparam) != 0) 1258 return 1; 1259 } 1260 } 1261 } 1262 1263 ForwardMouseEventToRenderer(message, wparam, lparam); 1264 return 0; 1265 } 1266 1267 LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam, 1268 LPARAM lparam, BOOL& handled) { 1269 handled = TRUE; 1270 1271 // If we are a pop-up, forward tab related messages to our parent HWND, so 1272 // that we are dismissed appropriately and so that the focus advance in our 1273 // parent. 1274 // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the 1275 // FocusManager. 1276 if (close_on_deactivate_ && 1277 (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) || 1278 (message == WM_CHAR && wparam == L'\t'))) { 1279 DCHECK(parent_hwnd_); 1280 // First close the pop-up. 1281 SendMessage(WM_CANCELMODE); 1282 // Then move the focus by forwarding the tab key to the parent. 1283 return ::SendMessage(parent_hwnd_, message, wparam, lparam); 1284 } 1285 1286 if (!render_widget_host_) 1287 return 0; 1288 1289 // Bug 1845: we need to update the text direction when a user releases 1290 // either a right-shift key or a right-control key after pressing both of 1291 // them. So, we just update the text direction while a user is pressing the 1292 // keys, and we notify the text direction when a user releases either of them. 1293 // Bug 9718: http://crbug.com/9718 To investigate IE and notepad, this 1294 // shortcut is enabled only on a PC having RTL keyboard layouts installed. 1295 // We should emulate them. 1296 if (ui::ImeInput::IsRTLKeyboardLayoutInstalled()) { 1297 if (message == WM_KEYDOWN) { 1298 if (wparam == VK_SHIFT) { 1299 base::i18n::TextDirection dir; 1300 if (ui::ImeInput::IsCtrlShiftPressed(&dir)) { 1301 render_widget_host_->UpdateTextDirection( 1302 dir == base::i18n::RIGHT_TO_LEFT ? 1303 WebKit::WebTextDirectionRightToLeft : 1304 WebKit::WebTextDirectionLeftToRight); 1305 } 1306 } else if (wparam != VK_CONTROL) { 1307 // Bug 9762: http://crbug.com/9762 A user pressed a key except shift 1308 // and control keys. 1309 // When a user presses a key while he/she holds control and shift keys, 1310 // we cancel sending an IPC message in NotifyTextDirection() below and 1311 // ignore succeeding UpdateTextDirection() calls while we call 1312 // NotifyTextDirection(). 1313 // To cancel it, this call set a flag that prevents sending an IPC 1314 // message in NotifyTextDirection() only if we are going to send it. 1315 // It is harmless to call this function if we aren't going to send it. 1316 render_widget_host_->CancelUpdateTextDirection(); 1317 } 1318 } else if (message == WM_KEYUP && 1319 (wparam == VK_SHIFT || wparam == VK_CONTROL)) { 1320 // We send an IPC message only if we need to update the text direction. 1321 render_widget_host_->NotifyTextDirection(); 1322 } 1323 } 1324 1325 // Special processing for enter key: When user hits enter in omnibox 1326 // we change focus to render host after the navigation, so repeat WM_KEYDOWNs 1327 // and WM_KEYUP are going to render host, despite being initiated in other 1328 // window. This code filters out these messages. 1329 bool ignore_keyboard_event = false; 1330 if (wparam == VK_RETURN) { 1331 if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) { 1332 if (KF_REPEAT & HIWORD(lparam)) { 1333 // this is a repeated key 1334 if (!capture_enter_key_) 1335 ignore_keyboard_event = true; 1336 } else { 1337 capture_enter_key_ = true; 1338 } 1339 } else if (message == WM_KEYUP || message == WM_SYSKEYUP) { 1340 if (!capture_enter_key_) 1341 ignore_keyboard_event = true; 1342 capture_enter_key_ = false; 1343 } else { 1344 // Ignore all other keyboard events for the enter key if not captured. 1345 if (!capture_enter_key_) 1346 ignore_keyboard_event = true; 1347 } 1348 } 1349 1350 if (render_widget_host_ && !ignore_keyboard_event) { 1351 render_widget_host_->ForwardKeyboardEvent( 1352 NativeWebKeyboardEvent(m_hWnd, message, wparam, lparam)); 1353 } 1354 return 0; 1355 } 1356 1357 LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, 1358 LPARAM lparam, BOOL& handled) { 1359 // Forward the mouse-wheel message to the window under the mouse if it belongs 1360 // to us. 1361 if (message == WM_MOUSEWHEEL && 1362 views::RerouteMouseWheel(m_hWnd, wparam, lparam)) { 1363 handled = TRUE; 1364 return 0; 1365 } 1366 1367 // Workaround for Thinkpad mousewheel driver. We get mouse wheel/scroll 1368 // messages even if we are not in the foreground. So here we check if 1369 // we have any owned popup windows in the foreground and dismiss them. 1370 if (m_hWnd != GetForegroundWindow()) { 1371 HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT); 1372 EnumThreadWindows( 1373 GetCurrentThreadId(), 1374 DismissOwnedPopups, 1375 reinterpret_cast<LPARAM>(toplevel_hwnd)); 1376 } 1377 1378 // This is a bit of a hack, but will work for now since we don't want to 1379 // pollute this object with TabContents-specific functionality... 1380 bool handled_by_TabContents = false; 1381 if (GetParent()) { 1382 // Use a special reflected message to break recursion. If we send 1383 // WM_MOUSEWHEEL, the focus manager subclass of web contents will 1384 // route it back here. 1385 MSG new_message = {0}; 1386 new_message.hwnd = m_hWnd; 1387 new_message.message = message; 1388 new_message.wParam = wparam; 1389 new_message.lParam = lparam; 1390 1391 handled_by_TabContents = 1392 !!::SendMessage(GetParent(), views::kReflectedMessage, 0, 1393 reinterpret_cast<LPARAM>(&new_message)); 1394 } 1395 1396 if (!handled_by_TabContents && render_widget_host_) { 1397 render_widget_host_->ForwardWheelEvent( 1398 WebInputEventFactory::mouseWheelEvent(m_hWnd, message, wparam, 1399 lparam)); 1400 } 1401 handled = TRUE; 1402 return 0; 1403 } 1404 1405 LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message, 1406 WPARAM wparam, 1407 LPARAM lparam, 1408 BOOL& handled) { 1409 if (!IsActivatable()) 1410 return MA_NOACTIVATE; 1411 1412 HWND focus_window = GetFocus(); 1413 if (!::IsWindow(focus_window) || !IsChild(focus_window)) { 1414 // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin 1415 // child window. This is to ensure that keyboard events are received 1416 // by the plugin. The correct way to fix this would be send over 1417 // an event to the renderer which would then eventually send over 1418 // a setFocus call to the plugin widget. This would ensure that 1419 // the renderer (webkit) knows about the plugin widget receiving 1420 // focus. 1421 // TODO(iyengar) Do the right thing as per the above comment. 1422 POINT cursor_pos = {0}; 1423 ::GetCursorPos(&cursor_pos); 1424 ::ScreenToClient(m_hWnd, &cursor_pos); 1425 HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); 1426 if (::IsWindow(child_window) && child_window != m_hWnd) { 1427 if (ui::GetClassName(child_window) == 1428 webkit::npapi::kWrapperNativeWindowClassName) 1429 child_window = ::GetWindow(child_window, GW_CHILD); 1430 1431 ::SetFocus(child_window); 1432 return MA_NOACTIVATE; 1433 } 1434 } 1435 handled = FALSE; 1436 render_widget_host_->OnMouseActivate(); 1437 return MA_ACTIVATE; 1438 } 1439 1440 void RenderWidgetHostViewWin::OnAccessibilityNotifications( 1441 const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { 1442 if (!browser_accessibility_manager_.get()) { 1443 // Use empty document to process notifications 1444 webkit_glue::WebAccessibility empty_document; 1445 empty_document.role = WebAccessibility::ROLE_DOCUMENT; 1446 empty_document.state = 0; 1447 browser_accessibility_manager_.reset( 1448 BrowserAccessibilityManager::Create(m_hWnd, empty_document, this)); 1449 } 1450 1451 browser_accessibility_manager_->OnAccessibilityNotifications(params); 1452 } 1453 1454 void RenderWidgetHostViewWin::Observe(NotificationType type, 1455 const NotificationSource& source, 1456 const NotificationDetails& details) { 1457 DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED); 1458 1459 // Get the RenderProcessHost that posted this notification, and exit 1460 // if it's not the one associated with this host view. 1461 RenderProcessHost* render_process_host = 1462 Source<RenderProcessHost>(source).ptr(); 1463 DCHECK(render_process_host); 1464 if (!render_widget_host_ || 1465 render_process_host != render_widget_host_->process()) 1466 return; 1467 1468 // If it was our RenderProcessHost that posted the notification, 1469 // clear the BrowserAccessibilityManager, because the renderer is 1470 // dead and any accessibility information we have is now stale. 1471 browser_accessibility_manager_.reset(NULL); 1472 } 1473 1474 static void PaintCompositorHostWindow(HWND hWnd) { 1475 PAINTSTRUCT paint; 1476 BeginPaint(hWnd, &paint); 1477 1478 EndPaint(hWnd, &paint); 1479 } 1480 1481 // WndProc for the compositor host window. We use this instead of Default so 1482 // we can drop WM_PAINT and WM_ERASEBKGD messages on the floor. 1483 static LRESULT CALLBACK CompositorHostWindowProc(HWND hWnd, UINT message, 1484 WPARAM wParam, LPARAM lParam) { 1485 switch (message) { 1486 case WM_ERASEBKGND: 1487 return 0; 1488 case WM_DESTROY: 1489 return 0; 1490 case WM_PAINT: 1491 PaintCompositorHostWindow(hWnd); 1492 return 0; 1493 default: 1494 return DefWindowProc(hWnd, message, wParam, lParam); 1495 } 1496 } 1497 1498 // Creates a HWND within the RenderWidgetHostView that will serve as a host 1499 // for a HWND that the GPU process will create. The host window is used 1500 // to Z-position the GPU's window relative to other plugin windows. 1501 gfx::PluginWindowHandle RenderWidgetHostViewWin::GetCompositingSurface() { 1502 // If the window has been created, don't recreate it a second time 1503 if (compositor_host_window_) 1504 return compositor_host_window_; 1505 1506 static ATOM window_class = 0; 1507 if (!window_class) { 1508 WNDCLASSEX wcex; 1509 wcex.cbSize = sizeof(WNDCLASSEX); 1510 wcex.style = 0; 1511 wcex.lpfnWndProc = 1512 base::win::WrappedWindowProc<CompositorHostWindowProc>; 1513 wcex.cbClsExtra = 0; 1514 wcex.cbWndExtra = 0; 1515 wcex.hInstance = GetModuleHandle(NULL); 1516 wcex.hIcon = 0; 1517 wcex.hCursor = 0; 1518 wcex.hbrBackground = NULL; 1519 wcex.lpszMenuName = 0; 1520 wcex.lpszClassName = L"CompositorHostWindowClass"; 1521 wcex.hIconSm = 0; 1522 window_class = RegisterClassEx(&wcex); 1523 DCHECK(window_class); 1524 } 1525 1526 RECT currentRect; 1527 GetClientRect(¤tRect); 1528 int width = currentRect.right - currentRect.left; 1529 int height = currentRect.bottom - currentRect.top; 1530 1531 compositor_host_window_ = CreateWindowEx( 1532 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, 1533 MAKEINTATOM(window_class), 0, 1534 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED, 1535 0, 0, width, height, m_hWnd, 0, GetModuleHandle(NULL), 0); 1536 ui::CheckWindowCreated(compositor_host_window_); 1537 1538 return static_cast<gfx::PluginWindowHandle>(compositor_host_window_); 1539 } 1540 1541 void RenderWidgetHostViewWin::ShowCompositorHostWindow(bool show) { 1542 // When we first create the compositor, we will get a show request from 1543 // the renderer before we have gotten the create request from the GPU. In this 1544 // case, simply ignore the show request. 1545 if (compositor_host_window_ == NULL) 1546 return; 1547 1548 if (show) { 1549 ::ShowWindow(compositor_host_window_, SW_SHOW); 1550 1551 // Get all the child windows of this view, including the compositor window. 1552 std::vector<HWND> all_child_windows; 1553 ::EnumChildWindows(m_hWnd, AddChildWindowToVector, 1554 reinterpret_cast<LPARAM>(&all_child_windows)); 1555 1556 // Build a list of just the plugin window handles 1557 std::vector<HWND> plugin_windows; 1558 bool compositor_host_window_found = false; 1559 for (size_t i = 0; i < all_child_windows.size(); ++i) { 1560 if (all_child_windows[i] != compositor_host_window_) 1561 plugin_windows.push_back(all_child_windows[i]); 1562 else 1563 compositor_host_window_found = true; 1564 } 1565 DCHECK(compositor_host_window_found); 1566 1567 // Set all the plugin windows to be "after" the compositor window. 1568 // When the compositor window is created, gets placed above plugins. 1569 for (size_t i = 0; i < plugin_windows.size(); ++i) { 1570 HWND next; 1571 if (i + 1 < plugin_windows.size()) 1572 next = plugin_windows[i+1]; 1573 else 1574 next = compositor_host_window_; 1575 ::SetWindowPos(plugin_windows[i], next, 0, 0, 0, 0, 1576 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 1577 } 1578 } else { 1579 hide_compositor_window_at_next_paint_ = true; 1580 } 1581 } 1582 1583 void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) { 1584 if (!browser_accessibility_manager_.get() || 1585 !render_widget_host_ || 1586 !render_widget_host_->process() || 1587 !render_widget_host_->process()->HasConnection()) { 1588 return; 1589 } 1590 1591 render_widget_host_->SetAccessibilityFocus(acc_obj_id); 1592 } 1593 1594 void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) { 1595 if (!browser_accessibility_manager_.get() || 1596 !render_widget_host_ || 1597 !render_widget_host_->process() || 1598 !render_widget_host_->process()->HasConnection()) { 1599 return; 1600 } 1601 1602 render_widget_host_->AccessibilityDoDefaultAction(acc_obj_id); 1603 } 1604 1605 LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam, 1606 LPARAM lparam, BOOL& handled) { 1607 if (kIdCustom == lparam) { 1608 // An MSAA client requestes our custom id. Assume that we have detected an 1609 // active windows screen reader. 1610 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected(); 1611 render_widget_host_->EnableRendererAccessibility(); 1612 1613 // Return with failure. 1614 return static_cast<LRESULT>(0L); 1615 } 1616 1617 if (lparam != OBJID_CLIENT) { 1618 handled = false; 1619 return static_cast<LRESULT>(0L); 1620 } 1621 1622 if (render_widget_host_ && !render_widget_host_->renderer_accessible()) { 1623 // Attempt to detect screen readers by sending an event with our custom id. 1624 NotifyWinEvent(EVENT_SYSTEM_ALERT, m_hWnd, kIdCustom, CHILDID_SELF); 1625 } 1626 1627 if (!browser_accessibility_manager_.get()) { 1628 // Return busy document tree while renderer accessibility tree loads. 1629 webkit_glue::WebAccessibility loading_tree; 1630 loading_tree.role = WebAccessibility::ROLE_DOCUMENT; 1631 loading_tree.state = (1 << WebAccessibility::STATE_BUSY); 1632 browser_accessibility_manager_.reset( 1633 BrowserAccessibilityManager::Create(m_hWnd, loading_tree, this)); 1634 } 1635 1636 base::win::ScopedComPtr<IAccessible> root( 1637 browser_accessibility_manager_->GetRoot()->toBrowserAccessibilityWin()); 1638 if (root.get()) 1639 return LresultFromObject(IID_IAccessible, wparam, root.Detach()); 1640 1641 handled = false; 1642 return static_cast<LRESULT>(0L); 1643 } 1644 1645 LRESULT RenderWidgetHostViewWin::OnParentNotify(UINT message, WPARAM wparam, 1646 LPARAM lparam, BOOL& handled) { 1647 handled = FALSE; 1648 1649 if (!render_widget_host_) 1650 return 0; 1651 1652 switch (LOWORD(wparam)) { 1653 case WM_LBUTTONDOWN: 1654 case WM_RBUTTONDOWN: 1655 case WM_MBUTTONDOWN: 1656 render_widget_host_->StartUserGesture(); 1657 break; 1658 default: 1659 break; 1660 } 1661 return 0; 1662 } 1663 1664 void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { 1665 // When the render widget host is being destroyed, it ends up calling 1666 // WillDestroyRenderWidget (through the RENDER_WIDGET_HOST_DESTROYED 1667 // notification) which NULLs render_widget_host_. 1668 // Note: the following bug http://crbug.com/24248 seems to report that 1669 // OnFinalMessage is called with a deleted |render_widget_host_|. It is not 1670 // clear how this could happen, hence the NULLing of render_widget_host_ 1671 // above. 1672 if (!render_widget_host_ && !being_destroyed_) { 1673 // If you hit this NOTREACHED, please add a comment to report it on 1674 // http://crbug.com/24248, including what you did when it happened and if 1675 // you can repro. 1676 NOTREACHED(); 1677 } 1678 if (render_widget_host_) 1679 render_widget_host_->ViewDestroyed(); 1680 delete this; 1681 } 1682 1683 void RenderWidgetHostViewWin::TrackMouseLeave(bool track) { 1684 if (track == track_mouse_leave_) 1685 return; 1686 track_mouse_leave_ = track; 1687 1688 DCHECK(m_hWnd); 1689 1690 TRACKMOUSEEVENT tme; 1691 tme.cbSize = sizeof(TRACKMOUSEEVENT); 1692 tme.dwFlags = TME_LEAVE; 1693 if (!track_mouse_leave_) 1694 tme.dwFlags |= TME_CANCEL; 1695 tme.hwndTrack = m_hWnd; 1696 1697 TrackMouseEvent(&tme); 1698 } 1699 1700 bool RenderWidgetHostViewWin::Send(IPC::Message* message) { 1701 if (!render_widget_host_) 1702 return false; 1703 return render_widget_host_->Send(message); 1704 } 1705 1706 void RenderWidgetHostViewWin::EnsureTooltip() { 1707 UINT message = TTM_NEWTOOLRECT; 1708 1709 TOOLINFO ti; 1710 ti.cbSize = sizeof(ti); 1711 ti.hwnd = m_hWnd; 1712 ti.uId = 0; 1713 if (!::IsWindow(tooltip_hwnd_)) { 1714 message = TTM_ADDTOOL; 1715 tooltip_hwnd_ = CreateWindowEx( 1716 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), 1717 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL, 1718 NULL, NULL); 1719 ui::CheckWindowCreated(tooltip_hwnd_); 1720 ti.uFlags = TTF_TRANSPARENT; 1721 ti.lpszText = LPSTR_TEXTCALLBACK; 1722 } 1723 1724 CRect cr; 1725 GetClientRect(&ti.rect); 1726 SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti)); 1727 } 1728 1729 void RenderWidgetHostViewWin::ResetTooltip() { 1730 if (::IsWindow(tooltip_hwnd_)) 1731 ::DestroyWindow(tooltip_hwnd_); 1732 tooltip_hwnd_ = NULL; 1733 } 1734 1735 void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, 1736 WPARAM wparam, 1737 LPARAM lparam) { 1738 if (!render_widget_host_) 1739 return; 1740 1741 WebMouseEvent event( 1742 WebInputEventFactory::mouseEvent(m_hWnd, message, wparam, lparam)); 1743 1744 // Send the event to the renderer before changing mouse capture, so that the 1745 // capturelost event arrives after mouseup. 1746 render_widget_host_->ForwardMouseEvent(event); 1747 1748 switch (event.type) { 1749 case WebInputEvent::MouseMove: 1750 TrackMouseLeave(true); 1751 break; 1752 case WebInputEvent::MouseLeave: 1753 TrackMouseLeave(false); 1754 break; 1755 case WebInputEvent::MouseDown: 1756 SetCapture(); 1757 break; 1758 case WebInputEvent::MouseUp: 1759 if (GetCapture() == m_hWnd) 1760 ReleaseCapture(); 1761 break; 1762 } 1763 1764 if (IsActivatable() && event.type == WebInputEvent::MouseDown) { 1765 // This is a temporary workaround for bug 765011 to get focus when the 1766 // mouse is clicked. This happens after the mouse down event is sent to 1767 // the renderer because normally Windows does a WM_SETFOCUS after 1768 // WM_LBUTTONDOWN. 1769 SetFocus(); 1770 } 1771 } 1772 1773 void RenderWidgetHostViewWin::ShutdownHost() { 1774 shutdown_factory_.RevokeAll(); 1775 if (render_widget_host_) 1776 render_widget_host_->Shutdown(); 1777 // Do not touch any members at this point, |this| has been deleted. 1778 } 1779 1780 // static 1781 RenderWidgetHostView* 1782 RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( 1783 gfx::NativeView native_view) { 1784 return ::IsWindow(native_view) ? 1785 reinterpret_cast<RenderWidgetHostView*>( 1786 ViewProp::GetValue(native_view, kRenderWidgetHostViewKey)) : NULL; 1787 } 1788