| 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 |