OLD | NEW |
| (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(©_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(¤tRect); | |
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 } | |
OLD | NEW |