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
DismissOwnedPopups(HWND window,LPARAM arg)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:
NotifyPluginProcessHostTask(HWND window,HWND parent)106 NotifyPluginProcessHostTask(HWND window, HWND parent)
107 : window_(window), parent_(parent), tries_(kMaxTries) { }
108
109 private:
Run()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.
DetachPluginWindowsCallback(HWND window,LPARAM param)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.
DrawDeemphasized(const SkColor & color,const gfx::Rect & paint_rect,HDC backing_store_dc,HDC paint_dc)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.
PluginWrapperWindowProc(HWND window,unsigned int message,WPARAM wparam,LPARAM lparam)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
CreateViewForWidget(RenderWidgetHost * widget)213 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
214 RenderWidgetHost* widget) {
215 return new RenderWidgetHostViewWin(widget);
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219 // RenderWidgetHostViewWin, public:
220
RenderWidgetHostViewWin(RenderWidgetHost * widget)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
~RenderWidgetHostViewWin()245 RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
246 ResetTooltip();
247 }
248
CreateWnd(HWND parent)249 void RenderWidgetHostViewWin::CreateWnd(HWND parent) {
250 Create(parent); // ATL function to create the window.
251 }
252
253 ///////////////////////////////////////////////////////////////////////////////
254 // RenderWidgetHostViewWin, RenderWidgetHostView implementation:
255
InitAsPopup(RenderWidgetHostView * parent_host_view,const gfx::Rect & pos)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
InitAsFullscreen()266 void RenderWidgetHostViewWin::InitAsFullscreen() {
267 NOTIMPLEMENTED() << "Fullscreen not implemented on Win";
268 }
269
GetRenderWidgetHost() const270 RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const {
271 return render_widget_host_;
272 }
273
DidBecomeSelected()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
WasHidden()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
SetSize(const gfx::Size & size)306 void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) {
307 SetBounds(gfx::Rect(GetViewBounds().origin(), size));
308 }
309
SetBounds(const gfx::Rect & rect)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
GetNativeView()330 gfx::NativeView RenderWidgetHostViewWin::GetNativeView() {
331 return m_hWnd;
332 }
333
MovePluginWindows(const std::vector<WebPluginGeometry> & plugin_window_moves)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
ReparentWindow(HWND window)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
AddChildWindowToVector(HWND hwnd,LPARAM lparam)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
CleanupCompositorWindow()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
IsActivatable() const481 bool RenderWidgetHostViewWin::IsActivatable() const {
482 // Popups should not be activated.
483 return popup_type_ == WebKit::WebPopupTypeNone;
484 }
485
Focus()486 void RenderWidgetHostViewWin::Focus() {
487 if (IsWindow())
488 SetFocus();
489 }
490
Blur()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
HasFocus()499 bool RenderWidgetHostViewWin::HasFocus() {
500 return ::GetFocus() == m_hWnd;
501 }
502
Show()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
Hide()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
IsShowing()539 bool RenderWidgetHostViewWin::IsShowing() {
540 return !!IsWindowVisible();
541 }
542
GetViewBounds() const543 gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const {
544 CRect window_rect;
545 GetWindowRect(&window_rect);
546 return gfx::Rect(window_rect);
547 }
548
UpdateCursor(const WebCursor & cursor)549 void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) {
550 current_cursor_ = cursor;
551 UpdateCursorIfOverSelf();
552 }
553
UpdateCursorIfOverSelf()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
SetIsLoading(bool is_loading)582 void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) {
583 is_loading_ = is_loading;
584 UpdateCursorIfOverSelf();
585 }
586
ImeUpdateTextInputState(WebKit::WebTextInputType type,const gfx::Rect & caret_rect)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
ImeCancelComposition()603 void RenderWidgetHostViewWin::ImeCancelComposition() {
604 ime_input_.CancelIME(m_hWnd);
605 }
606
EnumChildProc(HWND hwnd,LPARAM lparam)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
Redraw()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
DidUpdateBackingStore(const gfx::Rect & scroll_rect,int scroll_dx,int scroll_dy,const std::vector<gfx::Rect> & copy_rects)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
RenderViewGone(base::TerminationStatus status,int error_code)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
WillWmDestroy()685 void RenderWidgetHostViewWin::WillWmDestroy() {
686 CleanupCompositorWindow();
687 }
688
WillDestroyRenderWidget(RenderWidgetHost * rwh)689 void RenderWidgetHostViewWin::WillDestroyRenderWidget(RenderWidgetHost* rwh) {
690 if (rwh == render_widget_host_)
691 render_widget_host_ = NULL;
692 }
693
Destroy()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
SetTooltipText(const std::wstring & tooltip_text)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
AllocBackingStore(const gfx::Size & size)734 BackingStore* RenderWidgetHostViewWin::AllocBackingStore(
735 const gfx::Size& size) {
736 return new BackingStoreWin(render_widget_host_, size);
737 }
738
SetBackground(const SkBitmap & background)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
ContainsNativeView(gfx::NativeView native_view) const745 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
SetVisuallyDeemphasized(const SkColor * color,bool animate)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
OnCreate(CREATESTRUCT * create_struct)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
OnActivate(UINT action,BOOL minimized,HWND window)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
OnDestroy()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
OnPaint(HDC unused_dc)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
DrawBackground(const RECT & dirty_rect,CPaintDC * dc)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
OnNCPaint(HRGN update_region)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
OnEraseBkgnd(HDC dc)977 LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) {
978 return 1;
979 }
980
OnSetCursor(HWND window,UINT hittest_code,UINT mouse_message_id)981 LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code,
982 UINT mouse_message_id) {
983 UpdateCursorIfOverSelf();
984 return 0;
985 }
986
OnSetFocus(HWND window)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
OnKillFocus(HWND window)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
OnCaptureChanged(HWND window)1004 void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) {
1005 if (render_widget_host_)
1006 render_widget_host_->LostCapture();
1007 }
1008
OnCancelMode()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
OnInputLangChange(DWORD character_set,HKL input_language_id)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
OnThemeChanged()1067 void RenderWidgetHostViewWin::OnThemeChanged() {
1068 if (render_widget_host_)
1069 render_widget_host_->SystemThemeChanged();
1070 }
1071
OnNotify(int w_param,NMHDR * header)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
OnImeSetContext(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnImeStartComposition(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnImeComposition(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnImeEndComposition(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnMouseEvent(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnKeyEvent(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnWheelEvent(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnMouseActivate(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnAccessibilityNotifications(const std::vector<ViewHostMsg_AccessibilityNotification_Params> & params)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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
PaintCompositorHostWindow(HWND hWnd)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.
CompositorHostWindowProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)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.
GetCompositingSurface()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
ShowCompositorHostWindow(bool show)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
SetAccessibilityFocus(int acc_obj_id)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
AccessibilityDoDefaultAction(int acc_obj_id)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
OnGetObject(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnParentNotify(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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
OnFinalMessage(HWND window)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
TrackMouseLeave(bool track)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
Send(IPC::Message * message)1700 bool RenderWidgetHostViewWin::Send(IPC::Message* message) {
1701 if (!render_widget_host_)
1702 return false;
1703 return render_widget_host_->Send(message);
1704 }
1705
EnsureTooltip()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
ResetTooltip()1729 void RenderWidgetHostViewWin::ResetTooltip() {
1730 if (::IsWindow(tooltip_hwnd_))
1731 ::DestroyWindow(tooltip_hwnd_);
1732 tooltip_hwnd_ = NULL;
1733 }
1734
ForwardMouseEventToRenderer(UINT message,WPARAM wparam,LPARAM lparam)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
ShutdownHost()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*
GetRenderWidgetHostViewFromNativeView(gfx::NativeView native_view)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