OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 5 #include "content/browser/renderer_host/render_widget_host_view_base.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "build/build_config.h" | 8 #include "build/build_config.h" |
9 #include "content/browser/accessibility/browser_accessibility_manager.h" | 9 #include "content/browser/accessibility/browser_accessibility_manager.h" |
10 #include "content/browser/gpu/gpu_data_manager_impl.h" | 10 #include "content/browser/gpu/gpu_data_manager_impl.h" |
11 #include "content/browser/renderer_host/input/synthetic_gesture_target_base.h" | 11 #include "content/browser/renderer_host/input/synthetic_gesture_target_base.h" |
12 #include "content/browser/renderer_host/render_process_host_impl.h" | 12 #include "content/browser/renderer_host/render_process_host_impl.h" |
13 #include "content/browser/renderer_host/render_widget_host_delegate.h" | 13 #include "content/browser/renderer_host/render_widget_host_delegate.h" |
14 #include "content/browser/renderer_host/render_widget_host_impl.h" | 14 #include "content/browser/renderer_host/render_widget_host_impl.h" |
15 #include "content/browser/renderer_host/render_widget_host_view_base_observer.h" | 15 #include "content/browser/renderer_host/render_widget_host_view_base_observer.h" |
16 #include "content/common/content_switches_internal.h" | 16 #include "content/common/content_switches_internal.h" |
17 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 17 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
18 #include "ui/gfx/display.h" | 18 #include "ui/gfx/display.h" |
19 #include "ui/gfx/geometry/point_conversions.h" | 19 #include "ui/gfx/geometry/point_conversions.h" |
20 #include "ui/gfx/geometry/size_conversions.h" | 20 #include "ui/gfx/geometry/size_conversions.h" |
21 #include "ui/gfx/geometry/size_f.h" | 21 #include "ui/gfx/geometry/size_f.h" |
22 #include "ui/gfx/screen.h" | 22 #include "ui/gfx/screen.h" |
23 | 23 |
24 #if defined(OS_WIN) | |
25 #include "base/command_line.h" | |
26 #include "base/message_loop/message_loop.h" | |
27 #include "base/win/wrapped_window_proc.h" | |
28 #include "content/browser/plugin_process_host.h" | |
29 #include "content/browser/plugin_service_impl.h" | |
30 #include "content/common/plugin_constants_win.h" | |
31 #include "content/common/webplugin_geometry.h" | |
32 #include "content/public/browser/browser_thread.h" | |
33 #include "content/public/browser/child_process_data.h" | |
34 #include "content/public/common/content_switches.h" | |
35 #include "ui/gfx/gdi_util.h" | |
36 #include "ui/gfx/win/dpi.h" | |
37 #include "ui/gfx/win/hwnd_util.h" | |
38 #endif | |
39 | |
40 namespace content { | 24 namespace content { |
41 | 25 |
42 #if defined(OS_WIN) | |
43 | |
44 namespace { | |
45 | |
46 // |window| is the plugin HWND, created and destroyed in the plugin process. | |
47 // |parent| is the parent HWND, created and destroyed on the browser UI thread. | |
48 void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) { | |
49 // How long to wait between each try. | |
50 static const int kTryDelayMs = 200; | |
51 | |
52 DWORD plugin_process_id; | |
53 bool found_starting_plugin_process = false; | |
54 GetWindowThreadProcessId(window, &plugin_process_id); | |
55 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { | |
56 if (!iter.GetData().handle) { | |
57 found_starting_plugin_process = true; | |
58 continue; | |
59 } | |
60 if (base::GetProcId(iter.GetData().handle) == plugin_process_id) { | |
61 iter->AddWindow(parent); | |
62 return; | |
63 } | |
64 } | |
65 | |
66 if (found_starting_plugin_process) { | |
67 // A plugin process has started but we don't have its handle yet. Since | |
68 // it's most likely the one for this plugin, try a few more times after a | |
69 // delay. | |
70 if (tries > 0) { | |
71 base::MessageLoop::current()->PostDelayedTask( | |
72 FROM_HERE, | |
73 base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1), | |
74 base::TimeDelta::FromMilliseconds(kTryDelayMs)); | |
75 return; | |
76 } | |
77 } | |
78 | |
79 // The plugin process might have died in the time to execute the task, don't | |
80 // leak the HWND. | |
81 PostMessage(parent, WM_CLOSE, 0, 0); | |
82 } | |
83 | |
84 // The plugin wrapper window which lives in the browser process has this proc | |
85 // as its window procedure. We only handle the WM_PARENTNOTIFY message sent by | |
86 // windowed plugins for mouse input. This is forwarded off to the wrappers | |
87 // parent which is typically the RVH window which turns on user gesture. | |
88 LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, | |
89 WPARAM wparam, LPARAM lparam) { | |
90 if (message == WM_PARENTNOTIFY) { | |
91 switch (LOWORD(wparam)) { | |
92 case WM_LBUTTONDOWN: | |
93 case WM_RBUTTONDOWN: | |
94 case WM_MBUTTONDOWN: | |
95 ::SendMessage(GetParent(window), message, wparam, lparam); | |
96 return 0; | |
97 default: | |
98 break; | |
99 } | |
100 } | |
101 return ::DefWindowProc(window, message, wparam, lparam); | |
102 } | |
103 | |
104 bool IsPluginWrapperWindow(HWND window) { | |
105 return gfx::GetClassNameW(window) == | |
106 base::string16(kWrapperNativeWindowClassName); | |
107 } | |
108 | |
109 // Create an intermediate window between the given HWND and its parent. | |
110 HWND ReparentWindow(HWND window, HWND parent) { | |
111 static ATOM atom = 0; | |
112 static HMODULE instance = NULL; | |
113 if (!atom) { | |
114 WNDCLASSEX window_class; | |
115 base::win::InitializeWindowClass( | |
116 kWrapperNativeWindowClassName, | |
117 &base::win::WrappedWindowProc<PluginWrapperWindowProc>, | |
118 CS_DBLCLKS, | |
119 0, | |
120 0, | |
121 NULL, | |
122 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), | |
123 reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1), | |
124 NULL, | |
125 NULL, | |
126 NULL, | |
127 &window_class); | |
128 instance = window_class.hInstance; | |
129 atom = RegisterClassEx(&window_class); | |
130 } | |
131 DCHECK(atom); | |
132 | |
133 HWND new_parent = CreateWindowEx( | |
134 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, | |
135 MAKEINTATOM(atom), 0, | |
136 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, | |
137 0, 0, 0, 0, parent, 0, instance, 0); | |
138 gfx::CheckWindowCreated(new_parent); | |
139 ::SetParent(window, new_parent); | |
140 // How many times we try to find a PluginProcessHost whose process matches | |
141 // the HWND. | |
142 static const int kMaxTries = 5; | |
143 BrowserThread::PostTask( | |
144 BrowserThread::IO, | |
145 FROM_HERE, | |
146 base::Bind(&NotifyPluginProcessHostHelper, window, new_parent, | |
147 kMaxTries)); | |
148 return new_parent; | |
149 } | |
150 | |
151 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) { | |
152 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd)) | |
153 return TRUE; | |
154 | |
155 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); | |
156 gfx::Rect rect_in_pixels = gfx::win::DIPToScreenRect(*rect); | |
157 static UINT msg = RegisterWindowMessage(kPaintMessageName); | |
158 WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y()); | |
159 lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height()); | |
160 | |
161 // SendMessage gets the message across much quicker than PostMessage, since it | |
162 // doesn't get queued. When the plugin thread calls PeekMessage or other | |
163 // Win32 APIs, sent messages are dispatched automatically. | |
164 SendNotifyMessage(hwnd, msg, wparam, lparam); | |
165 | |
166 return TRUE; | |
167 } | |
168 | |
169 // Windows callback for OnDestroy to detach the plugin windows. | |
170 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) { | |
171 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window); | |
172 return TRUE; | |
173 } | |
174 | |
175 } // namespace | |
176 | |
177 // static | |
178 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) { | |
179 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) && | |
180 !IsHungAppWindow(window)) { | |
181 ::ShowWindow(window, SW_HIDE); | |
182 SetParent(window, NULL); | |
183 } | |
184 } | |
185 | |
186 // static | |
187 void RenderWidgetHostViewBase::MovePluginWindowsHelper( | |
188 HWND parent, | |
189 const std::vector<WebPluginGeometry>& moves) { | |
190 if (moves.empty()) | |
191 return; | |
192 | |
193 bool oop_plugins = !base::CommandLine::ForCurrentProcess()->HasSwitch( | |
194 switches::kSingleProcess); | |
195 | |
196 HDWP defer_window_pos_info = | |
197 ::BeginDeferWindowPos(static_cast<int>(moves.size())); | |
198 | |
199 if (!defer_window_pos_info) { | |
200 NOTREACHED(); | |
201 return; | |
202 } | |
203 | |
204 #if defined(USE_AURA) | |
205 std::vector<RECT> invalidate_rects; | |
206 #endif | |
207 | |
208 for (size_t i = 0; i < moves.size(); ++i) { | |
209 unsigned long flags = 0; | |
210 const WebPluginGeometry& move = moves[i]; | |
211 HWND window = move.window; | |
212 | |
213 // As the plugin parent window which lives on the browser UI thread is | |
214 // destroyed asynchronously, it is possible that we have a stale window | |
215 // sent in by the renderer for moving around. | |
216 // Note: get the parent before checking if the window is valid, to avoid a | |
217 // race condition where the window is destroyed after the check but before | |
218 // the GetParent call. | |
219 HWND cur_parent = ::GetParent(window); | |
220 if (!::IsWindow(window)) | |
221 continue; | |
222 | |
223 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window)) { | |
224 // The renderer should only be trying to move plugin windows. However, | |
225 // this may happen as a result of a race condition (i.e. even after the | |
226 // check right above), so we ignore it. | |
227 continue; | |
228 } | |
229 | |
230 if (oop_plugins) { | |
231 if (cur_parent == GetDesktopWindow()) { | |
232 // The plugin window hasn't been parented yet, add an intermediate | |
233 // window that lives on this thread to speed up scrolling. Note this | |
234 // only works with out of process plugins since we depend on | |
235 // PluginProcessHost to destroy the intermediate HWNDs. | |
236 cur_parent = ReparentWindow(window, parent); | |
237 ::ShowWindow(window, SW_SHOW); // Window was created hidden. | |
238 } else if (!IsPluginWrapperWindow(cur_parent)) { | |
239 continue; // Race if plugin process is shutting down. | |
240 } | |
241 | |
242 // We move the intermediate parent window which doesn't result in cross- | |
243 // process synchronous Windows messages. | |
244 window = cur_parent; | |
245 } else { | |
246 if (cur_parent == GetDesktopWindow()) | |
247 SetParent(window, parent); | |
248 } | |
249 | |
250 if (move.visible) | |
251 flags |= SWP_SHOWWINDOW; | |
252 else | |
253 flags |= SWP_HIDEWINDOW; | |
254 | |
255 #if defined(USE_AURA) | |
256 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { | |
257 // Without this flag, Windows repaints the parent area uncovered by this | |
258 // move. However when software compositing is used the clipping region is | |
259 // ignored. Since in Aura the browser chrome could be under the plugin, if | |
260 // if Windows tries to paint it synchronously inside EndDeferWindowsPos | |
261 // then it won't have the data and it will flash white. So instead we | |
262 // manually redraw the plugin. | |
263 // Why not do this for native Windows? Not sure if there are any | |
264 // performance issues with this. | |
265 flags |= SWP_NOREDRAW; | |
266 } | |
267 #endif | |
268 | |
269 if (move.rects_valid) { | |
270 gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect); | |
271 HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(), | |
272 clip_rect_in_pixel.y(), | |
273 clip_rect_in_pixel.right(), | |
274 clip_rect_in_pixel.bottom()); | |
275 gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); | |
276 | |
277 // Note: System will own the hrgn after we call SetWindowRgn, | |
278 // so we don't need to call DeleteObject(hrgn) | |
279 ::SetWindowRgn(window, hrgn, | |
280 !move.clip_rect.IsEmpty() && (flags & SWP_NOREDRAW) == 0); | |
281 | |
282 #if defined(USE_AURA) | |
283 // When using the software compositor, if the clipping rectangle is empty | |
284 // then DeferWindowPos won't redraw the newly uncovered area under the | |
285 // plugin. | |
286 if (clip_rect_in_pixel.IsEmpty() && | |
287 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { | |
288 RECT r; | |
289 GetClientRect(window, &r); | |
290 MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2); | |
291 invalidate_rects.push_back(r); | |
292 } | |
293 #endif | |
294 } else { | |
295 flags |= SWP_NOMOVE; | |
296 flags |= SWP_NOSIZE; | |
297 } | |
298 | |
299 gfx::Rect window_rect_in_pixel = | |
300 gfx::win::DIPToScreenRect(move.window_rect); | |
301 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, | |
302 window, NULL, | |
303 window_rect_in_pixel.x(), | |
304 window_rect_in_pixel.y(), | |
305 window_rect_in_pixel.width(), | |
306 window_rect_in_pixel.height(), | |
307 flags); | |
308 | |
309 if (!defer_window_pos_info) { | |
310 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored."; | |
311 return; | |
312 } | |
313 } | |
314 | |
315 ::EndDeferWindowPos(defer_window_pos_info); | |
316 | |
317 #if defined(USE_AURA) | |
318 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { | |
319 for (size_t i = 0; i < moves.size(); ++i) { | |
320 const WebPluginGeometry& move = moves[i]; | |
321 RECT r; | |
322 GetWindowRect(move.window, &r); | |
323 gfx::Rect gr(r); | |
324 PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr)); | |
325 } | |
326 } else { | |
327 for (size_t i = 0; i < invalidate_rects.size(); ++i) { | |
328 ::RedrawWindow( | |
329 parent, &invalidate_rects[i], NULL, | |
330 // These flags are from WebPluginDelegateImpl::NativeWndProc. | |
331 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW); | |
332 } | |
333 } | |
334 #endif | |
335 } | |
336 | |
337 // static | |
338 void RenderWidgetHostViewBase::PaintPluginWindowsHelper( | |
339 HWND parent, const gfx::Rect& damaged_screen_rect) { | |
340 LPARAM lparam = reinterpret_cast<LPARAM>(&damaged_screen_rect); | |
341 EnumChildWindows(parent, PaintEnumChildProc, lparam); | |
342 } | |
343 | |
344 // static | |
345 void RenderWidgetHostViewBase::DetachPluginsHelper(HWND parent) { | |
346 // When a tab is closed all its child plugin windows are destroyed | |
347 // automatically. This happens before plugins get any notification that its | |
348 // instances are tearing down. | |
349 // | |
350 // Plugins like Quicktime assume that their windows will remain valid as long | |
351 // as they have plugin instances active. Quicktime crashes in this case | |
352 // because its windowing code cleans up an internal data structure that the | |
353 // handler for NPP_DestroyStream relies on. | |
354 // | |
355 // The fix is to detach plugin windows from web contents when it is going | |
356 // away. This will prevent the plugin windows from getting destroyed | |
357 // automatically. The detached plugin windows will get cleaned up in proper | |
358 // sequence as part of the usual cleanup when the plugin instance goes away. | |
359 EnumChildWindows(parent, DetachPluginWindowsCallbackInternal, NULL); | |
360 } | |
361 | |
362 #endif // OS_WIN | |
363 | |
364 namespace { | 26 namespace { |
365 | 27 |
366 // How many microseconds apart input events should be flushed. | 28 // How many microseconds apart input events should be flushed. |
367 const int kFlushInputRateInUs = 16666; | 29 const int kFlushInputRateInUs = 16666; |
368 | 30 |
369 } | 31 } |
370 | 32 |
371 RenderWidgetHostViewBase::RenderWidgetHostViewBase() | 33 RenderWidgetHostViewBase::RenderWidgetHostViewBase() |
372 : popup_type_(blink::WebPopupTypeNone), | 34 : popup_type_(blink::WebPopupTypeNone), |
373 background_color_(SK_ColorWHITE), | 35 background_color_(SK_ColorWHITE), |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
728 void RenderWidgetHostViewBase::RemoveObserver( | 390 void RenderWidgetHostViewBase::RemoveObserver( |
729 RenderWidgetHostViewBaseObserver* observer) { | 391 RenderWidgetHostViewBaseObserver* observer) { |
730 observers_.RemoveObserver(observer); | 392 observers_.RemoveObserver(observer); |
731 } | 393 } |
732 | 394 |
733 cc::SurfaceId RenderWidgetHostViewBase::SurfaceIdForTesting() const { | 395 cc::SurfaceId RenderWidgetHostViewBase::SurfaceIdForTesting() const { |
734 return cc::SurfaceId(); | 396 return cc::SurfaceId(); |
735 } | 397 } |
736 | 398 |
737 } // namespace content | 399 } // namespace content |
OLD | NEW |