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