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 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/render_widget_host_view_base.h" 6 7 #include "base/logging.h" 8 #include "content/browser/accessibility/browser_accessibility_manager.h" 9 #include "content/browser/gpu/gpu_data_manager_impl.h" 10 #include "content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h" 11 #include "content/browser/renderer_host/render_process_host_impl.h" 12 #include "content/browser/renderer_host/render_widget_host_impl.h" 13 #include "content/port/browser/render_widget_host_view_frame_subscriber.h" 14 #include "content/port/browser/smooth_scroll_gesture.h" 15 #include "third_party/WebKit/public/web/WebScreenInfo.h" 16 #include "ui/gfx/display.h" 17 #include "ui/gfx/screen.h" 18 #include "ui/gfx/size_conversions.h" 19 #include "ui/gfx/size_f.h" 20 21 #if defined(OS_WIN) 22 #include "base/command_line.h" 23 #include "base/message_loop/message_loop.h" 24 #include "base/win/wrapped_window_proc.h" 25 #include "content/browser/plugin_process_host.h" 26 #include "content/browser/plugin_service_impl.h" 27 #include "content/common/plugin_constants_win.h" 28 #include "content/common/webplugin_geometry.h" 29 #include "content/public/browser/browser_thread.h" 30 #include "content/public/browser/child_process_data.h" 31 #include "content/public/common/content_switches.h" 32 #include "ui/base/win/dpi.h" 33 #include "ui/base/win/hwnd_util.h" 34 #include "ui/gfx/gdi_util.h" 35 #endif 36 37 #if defined(TOOLKIT_GTK) 38 #include <gdk/gdkx.h> 39 #include <gtk/gtk.h> 40 41 #include "content/browser/renderer_host/gtk_window_utils.h" 42 #endif 43 44 namespace content { 45 46 // static 47 RenderWidgetHostViewPort* RenderWidgetHostViewPort::FromRWHV( 48 RenderWidgetHostView* rwhv) { 49 return static_cast<RenderWidgetHostViewPort*>(rwhv); 50 } 51 52 // static 53 RenderWidgetHostViewPort* RenderWidgetHostViewPort::CreateViewForWidget( 54 RenderWidgetHost* widget) { 55 return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget)); 56 } 57 58 #if defined(OS_WIN) 59 60 namespace { 61 62 // |window| is the plugin HWND, created and destroyed in the plugin process. 63 // |parent| is the parent HWND, created and destroyed on the browser UI thread. 64 void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) { 65 // How long to wait between each try. 66 static const int kTryDelayMs = 200; 67 68 DWORD plugin_process_id; 69 bool found_starting_plugin_process = false; 70 GetWindowThreadProcessId(window, &plugin_process_id); 71 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { 72 if (!iter.GetData().handle) { 73 found_starting_plugin_process = true; 74 continue; 75 } 76 if (base::GetProcId(iter.GetData().handle) == plugin_process_id) { 77 iter->AddWindow(parent); 78 return; 79 } 80 } 81 82 if (found_starting_plugin_process) { 83 // A plugin process has started but we don't have its handle yet. Since 84 // it's most likely the one for this plugin, try a few more times after a 85 // delay. 86 if (tries > 0) { 87 base::MessageLoop::current()->PostDelayedTask( 88 FROM_HERE, 89 base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1), 90 base::TimeDelta::FromMilliseconds(kTryDelayMs)); 91 return; 92 } 93 } 94 95 // The plugin process might have died in the time to execute the task, don't 96 // leak the HWND. 97 PostMessage(parent, WM_CLOSE, 0, 0); 98 } 99 100 // The plugin wrapper window which lives in the browser process has this proc 101 // as its window procedure. We only handle the WM_PARENTNOTIFY message sent by 102 // windowed plugins for mouse input. This is forwarded off to the wrappers 103 // parent which is typically the RVH window which turns on user gesture. 104 LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, 105 WPARAM wparam, LPARAM lparam) { 106 if (message == WM_PARENTNOTIFY) { 107 switch (LOWORD(wparam)) { 108 case WM_LBUTTONDOWN: 109 case WM_RBUTTONDOWN: 110 case WM_MBUTTONDOWN: 111 ::SendMessage(GetParent(window), message, wparam, lparam); 112 return 0; 113 default: 114 break; 115 } 116 } 117 return ::DefWindowProc(window, message, wparam, lparam); 118 } 119 120 bool IsPluginWrapperWindow(HWND window) { 121 return ui::GetClassNameW(window) == 122 string16(kWrapperNativeWindowClassName); 123 } 124 125 // Create an intermediate window between the given HWND and its parent. 126 HWND ReparentWindow(HWND window, HWND parent) { 127 static ATOM atom = 0; 128 static HMODULE instance = NULL; 129 if (!atom) { 130 WNDCLASSEX window_class; 131 base::win::InitializeWindowClass( 132 kWrapperNativeWindowClassName, 133 &base::win::WrappedWindowProc<PluginWrapperWindowProc>, 134 CS_DBLCLKS, 135 0, 136 0, 137 NULL, 138 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), 139 reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1), 140 NULL, 141 NULL, 142 NULL, 143 &window_class); 144 instance = window_class.hInstance; 145 atom = RegisterClassEx(&window_class); 146 } 147 DCHECK(atom); 148 149 HWND new_parent = CreateWindowEx( 150 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, 151 MAKEINTATOM(atom), 0, 152 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 153 0, 0, 0, 0, parent, 0, instance, 0); 154 ui::CheckWindowCreated(new_parent); 155 ::SetParent(window, new_parent); 156 // How many times we try to find a PluginProcessHost whose process matches 157 // the HWND. 158 static const int kMaxTries = 5; 159 BrowserThread::PostTask( 160 BrowserThread::IO, 161 FROM_HERE, 162 base::Bind(&NotifyPluginProcessHostHelper, window, new_parent, 163 kMaxTries)); 164 return new_parent; 165 } 166 167 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) { 168 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd)) 169 return TRUE; 170 171 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); 172 gfx::Rect rect_in_pixels = ui::win::DIPToScreenRect(*rect); 173 static UINT msg = RegisterWindowMessage(kPaintMessageName); 174 WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y()); 175 lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height()); 176 177 // SendMessage gets the message across much quicker than PostMessage, since it 178 // doesn't get queued. When the plugin thread calls PeekMessage or other 179 // Win32 APIs, sent messages are dispatched automatically. 180 SendNotifyMessage(hwnd, msg, wparam, lparam); 181 182 return TRUE; 183 } 184 185 // Windows callback for OnDestroy to detach the plugin windows. 186 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) { 187 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window); 188 return TRUE; 189 } 190 191 } // namespace 192 193 // static 194 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) { 195 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) && 196 !IsHungAppWindow(window)) { 197 ::ShowWindow(window, SW_HIDE); 198 SetParent(window, NULL); 199 } 200 } 201 202 // static 203 void RenderWidgetHostViewBase::MovePluginWindowsHelper( 204 HWND parent, 205 const std::vector<WebPluginGeometry>& moves) { 206 if (moves.empty()) 207 return; 208 209 bool oop_plugins = 210 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) && 211 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessPlugins); 212 213 HDWP defer_window_pos_info = 214 ::BeginDeferWindowPos(static_cast<int>(moves.size())); 215 216 if (!defer_window_pos_info) { 217 NOTREACHED(); 218 return; 219 } 220 221 #if defined(USE_AURA) 222 std::vector<RECT> invalidate_rects; 223 #endif 224 225 for (size_t i = 0; i < moves.size(); ++i) { 226 unsigned long flags = 0; 227 const WebPluginGeometry& move = moves[i]; 228 HWND window = move.window; 229 230 // As the plugin parent window which lives on the browser UI thread is 231 // destroyed asynchronously, it is possible that we have a stale window 232 // sent in by the renderer for moving around. 233 // Note: get the parent before checking if the window is valid, to avoid a 234 // race condition where the window is destroyed after the check but before 235 // the GetParent call. 236 HWND cur_parent = ::GetParent(window); 237 if (!::IsWindow(window)) 238 continue; 239 240 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window)) { 241 // The renderer should only be trying to move plugin windows. However, 242 // this may happen as a result of a race condition (i.e. even after the 243 // check right above), so we ignore it. 244 continue; 245 } 246 247 if (oop_plugins) { 248 if (cur_parent == GetDesktopWindow()) { 249 // The plugin window hasn't been parented yet, add an intermediate 250 // window that lives on this thread to speed up scrolling. Note this 251 // only works with out of process plugins since we depend on 252 // PluginProcessHost to destroy the intermediate HWNDs. 253 cur_parent = ReparentWindow(window, parent); 254 ::ShowWindow(window, SW_SHOW); // Window was created hidden. 255 } else if (!IsPluginWrapperWindow(cur_parent)) { 256 continue; // Race if plugin process is shutting down. 257 } 258 259 // We move the intermediate parent window which doesn't result in cross- 260 // process synchronous Windows messages. 261 window = cur_parent; 262 } else { 263 if (cur_parent == GetDesktopWindow()) 264 SetParent(window, parent); 265 } 266 267 if (move.visible) 268 flags |= SWP_SHOWWINDOW; 269 else 270 flags |= SWP_HIDEWINDOW; 271 272 #if defined(USE_AURA) 273 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 274 // Without this flag, Windows repaints the parent area uncovered by this 275 // move. However when software compositing is used the clipping region is 276 // ignored. Since in Aura the browser chrome could be under the plugin, if 277 // if Windows tries to paint it synchronously inside EndDeferWindowsPos 278 // then it won't have the data and it will flash white. So instead we 279 // manually redraw the plugin. 280 // Why not do this for native Windows? Not sure if there are any 281 // performance issues with this. 282 flags |= SWP_NOREDRAW; 283 } 284 #endif 285 286 if (move.rects_valid) { 287 gfx::Rect clip_rect_in_pixel = ui::win::DIPToScreenRect(move.clip_rect); 288 HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(), 289 clip_rect_in_pixel.y(), 290 clip_rect_in_pixel.right(), 291 clip_rect_in_pixel.bottom()); 292 gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); 293 294 // Note: System will own the hrgn after we call SetWindowRgn, 295 // so we don't need to call DeleteObject(hrgn) 296 ::SetWindowRgn(window, hrgn, !move.clip_rect.IsEmpty()); 297 298 #if defined(USE_AURA) 299 // When using the software compositor, if the clipping rectangle is empty 300 // then DeferWindowPos won't redraw the newly uncovered area under the 301 // plugin. 302 if (clip_rect_in_pixel.IsEmpty() && 303 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 304 RECT r; 305 GetClientRect(window, &r); 306 MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2); 307 invalidate_rects.push_back(r); 308 } 309 #endif 310 } else { 311 flags |= SWP_NOMOVE; 312 flags |= SWP_NOSIZE; 313 } 314 315 gfx::Rect window_rect_in_pixel = 316 ui::win::DIPToScreenRect(move.window_rect); 317 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, 318 window, NULL, 319 window_rect_in_pixel.x(), 320 window_rect_in_pixel.y(), 321 window_rect_in_pixel.width(), 322 window_rect_in_pixel.height(), 323 flags); 324 325 if (!defer_window_pos_info) { 326 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored."; 327 return; 328 } 329 } 330 331 ::EndDeferWindowPos(defer_window_pos_info); 332 333 #if defined(USE_AURA) 334 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 335 for (size_t i = 0; i < moves.size(); ++i) { 336 const WebPluginGeometry& move = moves[i]; 337 RECT r; 338 GetWindowRect(move.window, &r); 339 gfx::Rect gr(r); 340 PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr)); 341 } 342 } else { 343 for (size_t i = 0; i < invalidate_rects.size(); ++i) { 344 ::RedrawWindow( 345 parent, &invalidate_rects[i], NULL, 346 // These flags are from WebPluginDelegateImpl::NativeWndProc. 347 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW); 348 } 349 } 350 #endif 351 } 352 353 // static 354 void RenderWidgetHostViewBase::PaintPluginWindowsHelper( 355 HWND parent, const gfx::Rect& damaged_screen_rect) { 356 LPARAM lparam = reinterpret_cast<LPARAM>(&damaged_screen_rect); 357 EnumChildWindows(parent, PaintEnumChildProc, lparam); 358 } 359 360 // static 361 void RenderWidgetHostViewBase::DetachPluginsHelper(HWND parent) { 362 // When a tab is closed all its child plugin windows are destroyed 363 // automatically. This happens before plugins get any notification that its 364 // instances are tearing down. 365 // 366 // Plugins like Quicktime assume that their windows will remain valid as long 367 // as they have plugin instances active. Quicktime crashes in this case 368 // because its windowing code cleans up an internal data structure that the 369 // handler for NPP_DestroyStream relies on. 370 // 371 // The fix is to detach plugin windows from web contents when it is going 372 // away. This will prevent the plugin windows from getting destroyed 373 // automatically. The detached plugin windows will get cleaned up in proper 374 // sequence as part of the usual cleanup when the plugin instance goes away. 375 EnumChildWindows(parent, DetachPluginWindowsCallbackInternal, NULL); 376 } 377 378 #endif // OS_WIN 379 380 RenderWidgetHostViewBase::RenderWidgetHostViewBase() 381 : popup_type_(WebKit::WebPopupTypeNone), 382 mouse_locked_(false), 383 showing_context_menu_(false), 384 selection_text_offset_(0), 385 selection_range_(ui::Range::InvalidRange()), 386 current_device_scale_factor_(0), 387 renderer_frame_number_(0) { 388 } 389 390 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() { 391 DCHECK(!mouse_locked_); 392 } 393 394 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){ 395 return false; 396 } 397 398 void RenderWidgetHostViewBase::SetBackground(const SkBitmap& background) { 399 background_ = background; 400 } 401 402 const SkBitmap& RenderWidgetHostViewBase::GetBackground() { 403 return background_; 404 } 405 406 gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const { 407 gfx::NativeView view = GetNativeView(); 408 gfx::Display display = 409 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); 410 return gfx::ToCeiledSize(gfx::ScaleSize(GetViewBounds().size(), 411 display.device_scale_factor())); 412 } 413 414 float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const { 415 return 0.f; 416 } 417 418 void RenderWidgetHostViewBase::SelectionChanged(const string16& text, 419 size_t offset, 420 const ui::Range& range) { 421 selection_text_ = text; 422 selection_text_offset_ = offset; 423 selection_range_.set_start(range.start()); 424 selection_range_.set_end(range.end()); 425 } 426 427 bool RenderWidgetHostViewBase::IsShowingContextMenu() const { 428 return showing_context_menu_; 429 } 430 431 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) { 432 DCHECK_NE(showing_context_menu_, showing); 433 showing_context_menu_ = showing; 434 } 435 436 string16 RenderWidgetHostViewBase::GetSelectedText() const { 437 if (!selection_range_.IsValid()) 438 return string16(); 439 return selection_text_.substr( 440 selection_range_.GetMin() - selection_text_offset_, 441 selection_range_.length()); 442 } 443 444 bool RenderWidgetHostViewBase::IsMouseLocked() { 445 return mouse_locked_; 446 } 447 448 void RenderWidgetHostViewBase::UnhandledWheelEvent( 449 const WebKit::WebMouseWheelEvent& event) { 450 // Most implementations don't need to do anything here. 451 } 452 453 InputEventAckState RenderWidgetHostViewBase::FilterInputEvent( 454 const WebKit::WebInputEvent& input_event) { 455 // By default, input events are simply forwarded to the renderer. 456 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; 457 } 458 459 void RenderWidgetHostViewBase::GestureEventAck(int gesture_event_type, 460 InputEventAckState ack_result) {} 461 462 void RenderWidgetHostViewBase::SetPopupType(WebKit::WebPopupType popup_type) { 463 popup_type_ = popup_type; 464 } 465 466 WebKit::WebPopupType RenderWidgetHostViewBase::GetPopupType() { 467 return popup_type_; 468 } 469 470 BrowserAccessibilityManager* 471 RenderWidgetHostViewBase::GetBrowserAccessibilityManager() const { 472 return browser_accessibility_manager_.get(); 473 } 474 475 void RenderWidgetHostViewBase::SetBrowserAccessibilityManager( 476 BrowserAccessibilityManager* manager) { 477 browser_accessibility_manager_.reset(manager); 478 } 479 480 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) { 481 RenderWidgetHostImpl* impl = NULL; 482 if (GetRenderWidgetHost()) 483 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 484 485 if (impl) 486 impl->SendScreenRects(); 487 488 if (HasDisplayPropertyChanged(view) && impl) 489 impl->NotifyScreenInfoChanged(); 490 } 491 492 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) { 493 gfx::Display display = 494 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); 495 if (current_display_area_ == display.work_area() && 496 current_device_scale_factor_ == display.device_scale_factor()) { 497 return false; 498 } 499 current_display_area_ = display.work_area(); 500 current_device_scale_factor_ = display.device_scale_factor(); 501 return true; 502 } 503 504 SmoothScrollGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture( 505 bool scroll_down, int pixels_to_scroll, int mouse_event_x, 506 int mouse_event_y) { 507 return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll, 508 mouse_event_x, mouse_event_y); 509 } 510 511 void RenderWidgetHostViewBase::ProcessAckedTouchEvent( 512 const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) { 513 } 514 515 // Platform implementation should override this method to allow frame 516 // subscription. Frame subscriber is set to RenderProcessHost, which is 517 // platform independent. It should be set to the specific presenter on each 518 // platform. 519 bool RenderWidgetHostViewBase::CanSubscribeFrame() const { 520 NOTIMPLEMENTED(); 521 return false; 522 } 523 524 // Base implementation for this method sets the subscriber to RenderProcessHost, 525 // which is platform independent. Note: Implementation only support subscribing 526 // to accelerated composited frames. 527 void RenderWidgetHostViewBase::BeginFrameSubscription( 528 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { 529 RenderWidgetHostImpl* impl = NULL; 530 if (GetRenderWidgetHost()) 531 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 532 if (!impl) 533 return; 534 RenderProcessHostImpl* render_process_host = 535 static_cast<RenderProcessHostImpl*>(impl->GetProcess()); 536 render_process_host->BeginFrameSubscription(impl->GetRoutingID(), 537 subscriber.Pass()); 538 } 539 540 void RenderWidgetHostViewBase::EndFrameSubscription() { 541 RenderWidgetHostImpl* impl = NULL; 542 if (GetRenderWidgetHost()) 543 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 544 if (!impl) 545 return; 546 RenderProcessHostImpl* render_process_host = 547 static_cast<RenderProcessHostImpl*>(impl->GetProcess()); 548 render_process_host->EndFrameSubscription(impl->GetRoutingID()); 549 } 550 551 void RenderWidgetHostViewBase::OnOverscrolled( 552 gfx::Vector2dF accumulated_overscroll, 553 gfx::Vector2dF current_fling_velocity) { 554 } 555 556 uint32 RenderWidgetHostViewBase::RendererFrameNumber() { 557 return renderer_frame_number_; 558 } 559 560 void RenderWidgetHostViewBase::DidReceiveRendererFrame() { 561 ++renderer_frame_number_; 562 } 563 564 } // namespace content 565