Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(582)

Side by Side Diff: chrome/browser/renderer_host/render_widget_host_view_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698