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.
5 #include "content/browser/renderer_host/render_widget_host_view_base.h"
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"
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"
36 #if defined(TOOLKIT_GTK)
40 #include "content/browser/renderer_host/gtk_window_utils.h"
46 RenderWidgetHostViewPort
* RenderWidgetHostViewPort::FromRWHV(
47 RenderWidgetHostView
* rwhv
) {
48 return static_cast<RenderWidgetHostViewPort
*>(rwhv
);
52 RenderWidgetHostViewPort
* RenderWidgetHostViewPort::CreateViewForWidget(
53 RenderWidgetHost
* widget
) {
54 return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget
));
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;
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;
75 if (base::GetProcId(iter
.GetData().handle
) == plugin_process_id
) {
76 iter
->AddWindow(parent
);
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
86 base::MessageLoop::current()->PostDelayedTask(
88 base::Bind(&NotifyPluginProcessHostHelper
, window
, parent
, tries
- 1),
89 base::TimeDelta::FromMilliseconds(kTryDelayMs
));
94 // The plugin process might have died in the time to execute the task, don't
96 PostMessage(parent
, WM_CLOSE
, 0, 0);
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
)) {
110 ::SendMessage(GetParent(window
), message
, wparam
, lparam
);
116 return ::DefWindowProc(window
, message
, wparam
, lparam
);
119 bool IsPluginWrapperWindow(HWND window
) {
120 return gfx::GetClassNameW(window
) ==
121 base::string16(kWrapperNativeWindowClassName
);
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
;
129 WNDCLASSEX window_class
;
130 base::win::InitializeWindowClass(
131 kWrapperNativeWindowClassName
,
132 &base::win::WrappedWindowProc
<PluginWrapperWindowProc
>,
137 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1),
138 reinterpret_cast<HBRUSH
>(COLOR_GRAYTEXT
+1),
143 instance
= window_class
.hInstance
;
144 atom
= RegisterClassEx(&window_class
);
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
157 static const int kMaxTries
= 5;
158 BrowserThread::PostTask(
161 base::Bind(&NotifyPluginProcessHostHelper
, window
, new_parent
,
166 BOOL CALLBACK
PaintEnumChildProc(HWND hwnd
, LPARAM lparam
) {
167 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd
))
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());
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
);
184 // Windows callback for OnDestroy to detach the plugin windows.
185 BOOL CALLBACK
DetachPluginWindowsCallbackInternal(HWND window
, LPARAM param
) {
186 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window
);
193 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window
) {
194 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window
) &&
195 !IsHungAppWindow(window
)) {
196 ::ShowWindow(window
, SW_HIDE
);
197 SetParent(window
, NULL
);
202 void RenderWidgetHostViewBase::MovePluginWindowsHelper(
204 const std::vector
<WebPluginGeometry
>& moves
) {
209 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess
);
211 HDWP defer_window_pos_info
=
212 ::BeginDeferWindowPos(static_cast<int>(moves
.size()));
214 if (!defer_window_pos_info
) {
219 #if defined(USE_AURA)
220 std::vector
<RECT
> invalidate_rects
;
223 for (size_t i
= 0; i
< moves
.size(); ++i
) {
224 unsigned long flags
= 0;
225 const WebPluginGeometry
& move
= moves
[i
];
226 HWND window
= move
.window
;
228 // As the plugin parent window which lives on the browser UI thread is
229 // destroyed asynchronously, it is possible that we have a stale window
230 // sent in by the renderer for moving around.
231 // Note: get the parent before checking if the window is valid, to avoid a
232 // race condition where the window is destroyed after the check but before
233 // the GetParent call.
234 HWND cur_parent
= ::GetParent(window
);
235 if (!::IsWindow(window
))
238 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window
)) {
239 // The renderer should only be trying to move plugin windows. However,
240 // this may happen as a result of a race condition (i.e. even after the
241 // check right above), so we ignore it.
246 if (cur_parent
== GetDesktopWindow()) {
247 // The plugin window hasn't been parented yet, add an intermediate
248 // window that lives on this thread to speed up scrolling. Note this
249 // only works with out of process plugins since we depend on
250 // PluginProcessHost to destroy the intermediate HWNDs.
251 cur_parent
= ReparentWindow(window
, parent
);
252 ::ShowWindow(window
, SW_SHOW
); // Window was created hidden.
253 } else if (!IsPluginWrapperWindow(cur_parent
)) {
254 continue; // Race if plugin process is shutting down.
257 // We move the intermediate parent window which doesn't result in cross-
258 // process synchronous Windows messages.
261 if (cur_parent
== GetDesktopWindow())
262 SetParent(window
, parent
);
266 flags
|= SWP_SHOWWINDOW
;
268 flags
|= SWP_HIDEWINDOW
;
270 #if defined(USE_AURA)
271 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
272 // Without this flag, Windows repaints the parent area uncovered by this
273 // move. However when software compositing is used the clipping region is
274 // ignored. Since in Aura the browser chrome could be under the plugin, if
275 // if Windows tries to paint it synchronously inside EndDeferWindowsPos
276 // then it won't have the data and it will flash white. So instead we
277 // manually redraw the plugin.
278 // Why not do this for native Windows? Not sure if there are any
279 // performance issues with this.
280 flags
|= SWP_NOREDRAW
;
284 if (move
.rects_valid
) {
285 gfx::Rect clip_rect_in_pixel
= gfx::win::DIPToScreenRect(move
.clip_rect
);
286 HRGN hrgn
= ::CreateRectRgn(clip_rect_in_pixel
.x(),
287 clip_rect_in_pixel
.y(),
288 clip_rect_in_pixel
.right(),
289 clip_rect_in_pixel
.bottom());
290 gfx::SubtractRectanglesFromRegion(hrgn
, move
.cutout_rects
);
292 // Note: System will own the hrgn after we call SetWindowRgn,
293 // so we don't need to call DeleteObject(hrgn)
294 ::SetWindowRgn(window
, hrgn
,
295 !move
.clip_rect
.IsEmpty() && (flags
& SWP_NOREDRAW
) == 0);
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
301 if (clip_rect_in_pixel
.IsEmpty() &&
302 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
304 GetClientRect(window
, &r
);
305 MapWindowPoints(window
, parent
, reinterpret_cast<POINT
*>(&r
), 2);
306 invalidate_rects
.push_back(r
);
314 gfx::Rect window_rect_in_pixel
=
315 gfx::win::DIPToScreenRect(move
.window_rect
);
316 defer_window_pos_info
= ::DeferWindowPos(defer_window_pos_info
,
318 window_rect_in_pixel
.x(),
319 window_rect_in_pixel
.y(),
320 window_rect_in_pixel
.width(),
321 window_rect_in_pixel
.height(),
324 if (!defer_window_pos_info
) {
325 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored.";
330 ::EndDeferWindowPos(defer_window_pos_info
);
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
];
337 GetWindowRect(move
.window
, &r
);
339 PaintEnumChildProc(move
.window
, reinterpret_cast<LPARAM
>(&gr
));
342 for (size_t i
= 0; i
< invalidate_rects
.size(); ++i
) {
344 parent
, &invalidate_rects
[i
], NULL
,
345 // These flags are from WebPluginDelegateImpl::NativeWndProc.
346 RDW_INVALIDATE
| RDW_ALLCHILDREN
| RDW_FRAME
| RDW_UPDATENOW
);
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
);
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.
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.
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
);
381 // How many microseconds apart input events should be flushed.
382 const int kFlushInputRateInUs
= 16666;
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) {
396 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
397 DCHECK(!mouse_locked_
);
400 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message
& msg
){
404 void RenderWidgetHostViewBase::SetBackground(const SkBitmap
& background
) {
405 background_
= background
;
408 const SkBitmap
& RenderWidgetHostViewBase::GetBackground() {
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()));
420 float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const {
424 void RenderWidgetHostViewBase::SelectionChanged(const base::string16
& text
,
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());
433 bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
434 return showing_context_menu_
;
437 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing
) {
438 DCHECK_NE(showing_context_menu_
, showing
);
439 showing_context_menu_
= showing
;
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());
450 bool RenderWidgetHostViewBase::IsMouseLocked() {
451 return mouse_locked_
;
454 void RenderWidgetHostViewBase::UnhandledWheelEvent(
455 const blink::WebMouseWheelEvent
& event
) {
456 // Most implementations don't need to do anything here.
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
;
465 void RenderWidgetHostViewBase::OnDidFlushInput() {
466 // The notification can safely be ignored by most implementations.
469 void RenderWidgetHostViewBase::OnSetNeedsFlushInput() {
470 if (flush_input_timer_
.IsRunning())
473 flush_input_timer_
.Start(
475 base::TimeDelta::FromMicroseconds(kFlushInputRateInUs
),
477 &RenderWidgetHostViewBase::FlushInput
);
480 void RenderWidgetHostViewBase::GestureEventAck(int gesture_event_type
,
481 InputEventAckState ack_result
) {}
483 void RenderWidgetHostViewBase::SetPopupType(blink::WebPopupType popup_type
) {
484 popup_type_
= popup_type
;
487 blink::WebPopupType
RenderWidgetHostViewBase::GetPopupType() {
491 BrowserAccessibilityManager
*
492 RenderWidgetHostViewBase::GetBrowserAccessibilityManager() const {
493 return browser_accessibility_manager_
.get();
496 void RenderWidgetHostViewBase::CreateBrowserAccessibilityManagerIfNeeded() {
499 void RenderWidgetHostViewBase::SetBrowserAccessibilityManager(
500 BrowserAccessibilityManager
* manager
) {
501 browser_accessibility_manager_
.reset(manager
);
504 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view
) {
505 RenderWidgetHostImpl
* impl
= NULL
;
506 if (GetRenderWidgetHost())
507 impl
= RenderWidgetHostImpl::From(GetRenderWidgetHost());
510 impl
->SendScreenRects();
512 if (HasDisplayPropertyChanged(view
) && impl
)
513 impl
->NotifyScreenInfoChanged();
516 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view
) {
517 gfx::Display display
=
518 gfx::Screen::GetScreenFor(view
)->GetDisplayNearestWindow(view
);
519 if (current_display_area_
== display
.work_area() &&
520 current_device_scale_factor_
== display
.device_scale_factor()) {
523 current_display_area_
= display
.work_area();
524 current_device_scale_factor_
= display
.device_scale_factor();
528 void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
529 const TouchEventWithLatencyInfo
& touch
, InputEventAckState ack_result
) {
532 scoped_ptr
<SyntheticGestureTarget
>
533 RenderWidgetHostViewBase::CreateSyntheticGestureTarget() {
534 RenderWidgetHostImpl
* host
=
535 RenderWidgetHostImpl::From(GetRenderWidgetHost());
536 return scoped_ptr
<SyntheticGestureTarget
>(
537 new SyntheticGestureTargetBase(host
));
540 // Platform implementation should override this method to allow frame
541 // subscription. Frame subscriber is set to RenderProcessHost, which is
542 // platform independent. It should be set to the specific presenter on each
544 bool RenderWidgetHostViewBase::CanSubscribeFrame() const {
549 // Base implementation for this method sets the subscriber to RenderProcessHost,
550 // which is platform independent. Note: Implementation only support subscribing
551 // to accelerated composited frames.
552 void RenderWidgetHostViewBase::BeginFrameSubscription(
553 scoped_ptr
<RenderWidgetHostViewFrameSubscriber
> subscriber
) {
554 RenderWidgetHostImpl
* impl
= NULL
;
555 if (GetRenderWidgetHost())
556 impl
= RenderWidgetHostImpl::From(GetRenderWidgetHost());
559 RenderProcessHostImpl
* render_process_host
=
560 static_cast<RenderProcessHostImpl
*>(impl
->GetProcess());
561 render_process_host
->BeginFrameSubscription(impl
->GetRoutingID(),
565 void RenderWidgetHostViewBase::EndFrameSubscription() {
566 RenderWidgetHostImpl
* impl
= NULL
;
567 if (GetRenderWidgetHost())
568 impl
= RenderWidgetHostImpl::From(GetRenderWidgetHost());
571 RenderProcessHostImpl
* render_process_host
=
572 static_cast<RenderProcessHostImpl
*>(impl
->GetProcess());
573 render_process_host
->EndFrameSubscription(impl
->GetRoutingID());
576 void RenderWidgetHostViewBase::OnOverscrolled(
577 gfx::Vector2dF accumulated_overscroll
,
578 gfx::Vector2dF current_fling_velocity
) {
581 uint32
RenderWidgetHostViewBase::RendererFrameNumber() {
582 return renderer_frame_number_
;
585 void RenderWidgetHostViewBase::DidReceiveRendererFrame() {
586 ++renderer_frame_number_
;
589 void RenderWidgetHostViewBase::FlushInput() {
590 RenderWidgetHostImpl
* impl
= NULL
;
591 if (GetRenderWidgetHost())
592 impl
= RenderWidgetHostImpl::From(GetRenderWidgetHost());
598 } // namespace content