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/common/content_switches_internal.h"
14 #include "content/port/browser/render_widget_host_view_frame_subscriber.h"
15 #include "third_party/WebKit/public/platform/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"
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/gfx/gdi_util.h"
33 #include "ui/gfx/win/dpi.h"
34 #include "ui/gfx/win/hwnd_util.h"
37 #if defined(TOOLKIT_GTK)
41 #include "content/browser/renderer_host/gtk_window_utils.h"
47 RenderWidgetHostViewPort* RenderWidgetHostViewPort::FromRWHV(
48 RenderWidgetHostView* rwhv) {
49 return static_cast<RenderWidgetHostViewPort*>(rwhv);
53 RenderWidgetHostViewPort* RenderWidgetHostViewPort::CreateViewForWidget(
54 RenderWidgetHost* widget) {
55 return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget));
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;
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;
76 if (base::GetProcId(iter.GetData().handle) == plugin_process_id) {
77 iter->AddWindow(parent);
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
87 base::MessageLoop::current()->PostDelayedTask(
89 base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1),
90 base::TimeDelta::FromMilliseconds(kTryDelayMs));
95 // The plugin process might have died in the time to execute the task, don't
97 PostMessage(parent, WM_CLOSE, 0, 0);
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)) {
111 ::SendMessage(GetParent(window), message, wparam, lparam);
117 return ::DefWindowProc(window, message, wparam, lparam);
120 bool IsPluginWrapperWindow(HWND window) {
121 return gfx::GetClassNameW(window) ==
122 base::string16(kWrapperNativeWindowClassName);
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;
130 WNDCLASSEX window_class;
131 base::win::InitializeWindowClass(
132 kWrapperNativeWindowClassName,
133 &base::win::WrappedWindowProc<PluginWrapperWindowProc>,
138 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1),
139 reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1),
144 instance = window_class.hInstance;
145 atom = RegisterClassEx(&window_class);
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 gfx::CheckWindowCreated(new_parent);
155 ::SetParent(window, new_parent);
156 // How many times we try to find a PluginProcessHost whose process matches
158 static const int kMaxTries = 5;
159 BrowserThread::PostTask(
162 base::Bind(&NotifyPluginProcessHostHelper, window, new_parent,
167 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) {
168 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd))
171 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam);
172 gfx::Rect rect_in_pixels = gfx::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());
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);
185 // Windows callback for OnDestroy to detach the plugin windows.
186 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) {
187 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window);
194 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) {
195 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) &&
196 !IsHungAppWindow(window)) {
197 ::ShowWindow(window, SW_HIDE);
198 SetParent(window, NULL);
203 void RenderWidgetHostViewBase::MovePluginWindowsHelper(
205 const std::vector<WebPluginGeometry>& moves) {
210 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
212 HDWP defer_window_pos_info =
213 ::BeginDeferWindowPos(static_cast<int>(moves.size()));
215 if (!defer_window_pos_info) {
220 #if defined(USE_AURA)
221 std::vector<RECT> invalidate_rects;
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;
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))
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.
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.
258 // We move the intermediate parent window which doesn't result in cross-
259 // process synchronous Windows messages.
262 if (cur_parent == GetDesktopWindow())
263 SetParent(window, parent);
267 flags |= SWP_SHOWWINDOW;
269 flags |= SWP_HIDEWINDOW;
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;
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);
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,
296 !move.clip_rect.IsEmpty() && (flags & SWP_NOREDRAW) == 0);
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
302 if (clip_rect_in_pixel.IsEmpty() &&
303 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
305 GetClientRect(window, &r);
306 MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2);
307 invalidate_rects.push_back(r);
315 gfx::Rect window_rect_in_pixel =
316 gfx::win::DIPToScreenRect(move.window_rect);
317 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info,
319 window_rect_in_pixel.x(),
320 window_rect_in_pixel.y(),
321 window_rect_in_pixel.width(),
322 window_rect_in_pixel.height(),
325 if (!defer_window_pos_info) {
326 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored.";
331 ::EndDeferWindowPos(defer_window_pos_info);
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];
338 GetWindowRect(move.window, &r);
340 PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr));
343 for (size_t i = 0; i < invalidate_rects.size(); ++i) {
345 parent, &invalidate_rects[i], NULL,
346 // These flags are from WebPluginDelegateImpl::NativeWndProc.
347 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW);
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);
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.
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.
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);
382 // How many microseconds apart input events should be flushed.
383 const int kFlushInputRateInUs = 16666;
387 RenderWidgetHostViewBase::RenderWidgetHostViewBase()
388 : popup_type_(blink::WebPopupTypeNone),
389 mouse_locked_(false),
390 showing_context_menu_(false),
391 selection_text_offset_(0),
392 selection_range_(gfx::Range::InvalidRange()),
393 current_device_scale_factor_(0),
394 pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
395 renderer_frame_number_(0) {
398 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
399 DCHECK(!mouse_locked_);
402 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){
406 void RenderWidgetHostViewBase::SetBackground(const SkBitmap& background) {
407 background_ = background;
410 const SkBitmap& RenderWidgetHostViewBase::GetBackground() {
414 gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const {
415 gfx::NativeView view = GetNativeView();
416 gfx::Display display =
417 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
418 return gfx::ToCeiledSize(gfx::ScaleSize(GetViewBounds().size(),
419 display.device_scale_factor()));
422 float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const {
426 void RenderWidgetHostViewBase::SelectionChanged(const base::string16& text,
428 const gfx::Range& range) {
429 selection_text_ = text;
430 selection_text_offset_ = offset;
431 selection_range_.set_start(range.start());
432 selection_range_.set_end(range.end());
435 bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
436 return showing_context_menu_;
439 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) {
440 DCHECK_NE(showing_context_menu_, showing);
441 showing_context_menu_ = showing;
444 base::string16 RenderWidgetHostViewBase::GetSelectedText() const {
445 if (!selection_range_.IsValid())
446 return base::string16();
447 return selection_text_.substr(
448 selection_range_.GetMin() - selection_text_offset_,
449 selection_range_.length());
452 bool RenderWidgetHostViewBase::IsMouseLocked() {
453 return mouse_locked_;
456 void RenderWidgetHostViewBase::UnhandledWheelEvent(
457 const blink::WebMouseWheelEvent& event) {
458 // Most implementations don't need to do anything here.
461 InputEventAckState RenderWidgetHostViewBase::FilterInputEvent(
462 const blink::WebInputEvent& input_event) {
463 // By default, input events are simply forwarded to the renderer.
464 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
467 void RenderWidgetHostViewBase::OnDidFlushInput() {
468 // The notification can safely be ignored by most implementations.
471 void RenderWidgetHostViewBase::OnSetNeedsFlushInput() {
472 if (flush_input_timer_.IsRunning())
475 flush_input_timer_.Start(
477 base::TimeDelta::FromMicroseconds(kFlushInputRateInUs),
479 &RenderWidgetHostViewBase::FlushInput);
482 void RenderWidgetHostViewBase::GestureEventAck(
483 const blink::WebGestureEvent& event,
484 InputEventAckState ack_result) {
487 void RenderWidgetHostViewBase::SetPopupType(blink::WebPopupType popup_type) {
488 popup_type_ = popup_type;
491 blink::WebPopupType RenderWidgetHostViewBase::GetPopupType() {
495 BrowserAccessibilityManager*
496 RenderWidgetHostViewBase::GetBrowserAccessibilityManager() const {
497 return browser_accessibility_manager_.get();
500 void RenderWidgetHostViewBase::CreateBrowserAccessibilityManagerIfNeeded() {
503 void RenderWidgetHostViewBase::SetBrowserAccessibilityManager(
504 BrowserAccessibilityManager* manager) {
505 browser_accessibility_manager_.reset(manager);
508 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
509 RenderWidgetHostImpl* impl = NULL;
510 if (GetRenderWidgetHost())
511 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
514 impl->SendScreenRects();
516 if (HasDisplayPropertyChanged(view) && impl)
517 impl->NotifyScreenInfoChanged();
520 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
521 gfx::Display display =
522 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
523 if (current_display_area_ == display.work_area() &&
524 current_device_scale_factor_ == display.device_scale_factor()) {
527 current_display_area_ = display.work_area();
528 current_device_scale_factor_ = display.device_scale_factor();
532 void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
533 const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
536 scoped_ptr<SyntheticGestureTarget>
537 RenderWidgetHostViewBase::CreateSyntheticGestureTarget() {
538 RenderWidgetHostImpl* host =
539 RenderWidgetHostImpl::From(GetRenderWidgetHost());
540 return scoped_ptr<SyntheticGestureTarget>(
541 new SyntheticGestureTargetBase(host));
544 void RenderWidgetHostViewBase::FocusedNodeChanged(bool is_editable_node) {
547 // Platform implementation should override this method to allow frame
548 // subscription. Frame subscriber is set to RenderProcessHost, which is
549 // platform independent. It should be set to the specific presenter on each
551 bool RenderWidgetHostViewBase::CanSubscribeFrame() const {
556 // Base implementation for this method sets the subscriber to RenderProcessHost,
557 // which is platform independent. Note: Implementation only support subscribing
558 // to accelerated composited frames.
559 void RenderWidgetHostViewBase::BeginFrameSubscription(
560 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
561 RenderWidgetHostImpl* impl = NULL;
562 if (GetRenderWidgetHost())
563 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
566 RenderProcessHostImpl* render_process_host =
567 static_cast<RenderProcessHostImpl*>(impl->GetProcess());
568 render_process_host->BeginFrameSubscription(impl->GetRoutingID(),
572 void RenderWidgetHostViewBase::EndFrameSubscription() {
573 RenderWidgetHostImpl* impl = NULL;
574 if (GetRenderWidgetHost())
575 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
578 RenderProcessHostImpl* render_process_host =
579 static_cast<RenderProcessHostImpl*>(impl->GetProcess());
580 render_process_host->EndFrameSubscription(impl->GetRoutingID());
583 void RenderWidgetHostViewBase::OnOverscrolled(
584 gfx::Vector2dF accumulated_overscroll,
585 gfx::Vector2dF current_fling_velocity) {
588 uint32 RenderWidgetHostViewBase::RendererFrameNumber() {
589 return renderer_frame_number_;
592 void RenderWidgetHostViewBase::DidReceiveRendererFrame() {
593 ++renderer_frame_number_;
596 void RenderWidgetHostViewBase::FlushInput() {
597 RenderWidgetHostImpl* impl = NULL;
598 if (GetRenderWidgetHost())
599 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
605 } // namespace content