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/public/browser/render_widget_host_view_frame_subscriber.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"
42 // |window| is the plugin HWND, created and destroyed in the plugin process.
43 // |parent| is the parent HWND, created and destroyed on the browser UI thread.
44 void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) {
45 // How long to wait between each try.
46 static const int kTryDelayMs = 200;
48 DWORD plugin_process_id;
49 bool found_starting_plugin_process = false;
50 GetWindowThreadProcessId(window, &plugin_process_id);
51 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
52 if (!iter.GetData().handle) {
53 found_starting_plugin_process = true;
56 if (base::GetProcId(iter.GetData().handle) == plugin_process_id) {
57 iter->AddWindow(parent);
62 if (found_starting_plugin_process) {
63 // A plugin process has started but we don't have its handle yet. Since
64 // it's most likely the one for this plugin, try a few more times after a
67 base::MessageLoop::current()->PostDelayedTask(
69 base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1),
70 base::TimeDelta::FromMilliseconds(kTryDelayMs));
75 // The plugin process might have died in the time to execute the task, don't
77 PostMessage(parent, WM_CLOSE, 0, 0);
80 // The plugin wrapper window which lives in the browser process has this proc
81 // as its window procedure. We only handle the WM_PARENTNOTIFY message sent by
82 // windowed plugins for mouse input. This is forwarded off to the wrappers
83 // parent which is typically the RVH window which turns on user gesture.
84 LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message,
85 WPARAM wparam, LPARAM lparam) {
86 if (message == WM_PARENTNOTIFY) {
87 switch (LOWORD(wparam)) {
91 ::SendMessage(GetParent(window), message, wparam, lparam);
97 return ::DefWindowProc(window, message, wparam, lparam);
100 bool IsPluginWrapperWindow(HWND window) {
101 return gfx::GetClassNameW(window) ==
102 base::string16(kWrapperNativeWindowClassName);
105 // Create an intermediate window between the given HWND and its parent.
106 HWND ReparentWindow(HWND window, HWND parent) {
107 static ATOM atom = 0;
108 static HMODULE instance = NULL;
110 WNDCLASSEX window_class;
111 base::win::InitializeWindowClass(
112 kWrapperNativeWindowClassName,
113 &base::win::WrappedWindowProc<PluginWrapperWindowProc>,
118 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1),
119 reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1),
124 instance = window_class.hInstance;
125 atom = RegisterClassEx(&window_class);
129 HWND new_parent = CreateWindowEx(
130 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
131 MAKEINTATOM(atom), 0,
132 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
133 0, 0, 0, 0, parent, 0, instance, 0);
134 gfx::CheckWindowCreated(new_parent);
135 ::SetParent(window, new_parent);
136 // How many times we try to find a PluginProcessHost whose process matches
138 static const int kMaxTries = 5;
139 BrowserThread::PostTask(
142 base::Bind(&NotifyPluginProcessHostHelper, window, new_parent,
147 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) {
148 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd))
151 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam);
152 gfx::Rect rect_in_pixels = gfx::win::DIPToScreenRect(*rect);
153 static UINT msg = RegisterWindowMessage(kPaintMessageName);
154 WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y());
155 lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height());
157 // SendMessage gets the message across much quicker than PostMessage, since it
158 // doesn't get queued. When the plugin thread calls PeekMessage or other
159 // Win32 APIs, sent messages are dispatched automatically.
160 SendNotifyMessage(hwnd, msg, wparam, lparam);
165 // Windows callback for OnDestroy to detach the plugin windows.
166 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) {
167 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window);
174 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) {
175 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) &&
176 !IsHungAppWindow(window)) {
177 ::ShowWindow(window, SW_HIDE);
178 SetParent(window, NULL);
183 void RenderWidgetHostViewBase::MovePluginWindowsHelper(
185 const std::vector<WebPluginGeometry>& moves) {
190 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
192 HDWP defer_window_pos_info =
193 ::BeginDeferWindowPos(static_cast<int>(moves.size()));
195 if (!defer_window_pos_info) {
200 #if defined(USE_AURA)
201 std::vector<RECT> invalidate_rects;
204 for (size_t i = 0; i < moves.size(); ++i) {
205 unsigned long flags = 0;
206 const WebPluginGeometry& move = moves[i];
207 HWND window = move.window;
209 // As the plugin parent window which lives on the browser UI thread is
210 // destroyed asynchronously, it is possible that we have a stale window
211 // sent in by the renderer for moving around.
212 // Note: get the parent before checking if the window is valid, to avoid a
213 // race condition where the window is destroyed after the check but before
214 // the GetParent call.
215 HWND cur_parent = ::GetParent(window);
216 if (!::IsWindow(window))
219 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window)) {
220 // The renderer should only be trying to move plugin windows. However,
221 // this may happen as a result of a race condition (i.e. even after the
222 // check right above), so we ignore it.
227 if (cur_parent == GetDesktopWindow()) {
228 // The plugin window hasn't been parented yet, add an intermediate
229 // window that lives on this thread to speed up scrolling. Note this
230 // only works with out of process plugins since we depend on
231 // PluginProcessHost to destroy the intermediate HWNDs.
232 cur_parent = ReparentWindow(window, parent);
233 ::ShowWindow(window, SW_SHOW); // Window was created hidden.
234 } else if (!IsPluginWrapperWindow(cur_parent)) {
235 continue; // Race if plugin process is shutting down.
238 // We move the intermediate parent window which doesn't result in cross-
239 // process synchronous Windows messages.
242 if (cur_parent == GetDesktopWindow())
243 SetParent(window, parent);
247 flags |= SWP_SHOWWINDOW;
249 flags |= SWP_HIDEWINDOW;
251 #if defined(USE_AURA)
252 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
253 // Without this flag, Windows repaints the parent area uncovered by this
254 // move. However when software compositing is used the clipping region is
255 // ignored. Since in Aura the browser chrome could be under the plugin, if
256 // if Windows tries to paint it synchronously inside EndDeferWindowsPos
257 // then it won't have the data and it will flash white. So instead we
258 // manually redraw the plugin.
259 // Why not do this for native Windows? Not sure if there are any
260 // performance issues with this.
261 flags |= SWP_NOREDRAW;
265 if (move.rects_valid) {
266 gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect);
267 HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(),
268 clip_rect_in_pixel.y(),
269 clip_rect_in_pixel.right(),
270 clip_rect_in_pixel.bottom());
271 gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects);
273 // Note: System will own the hrgn after we call SetWindowRgn,
274 // so we don't need to call DeleteObject(hrgn)
275 ::SetWindowRgn(window, hrgn,
276 !move.clip_rect.IsEmpty() && (flags & SWP_NOREDRAW) == 0);
278 #if defined(USE_AURA)
279 // When using the software compositor, if the clipping rectangle is empty
280 // then DeferWindowPos won't redraw the newly uncovered area under the
282 if (clip_rect_in_pixel.IsEmpty() &&
283 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
285 GetClientRect(window, &r);
286 MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2);
287 invalidate_rects.push_back(r);
295 gfx::Rect window_rect_in_pixel =
296 gfx::win::DIPToScreenRect(move.window_rect);
297 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info,
299 window_rect_in_pixel.x(),
300 window_rect_in_pixel.y(),
301 window_rect_in_pixel.width(),
302 window_rect_in_pixel.height(),
305 if (!defer_window_pos_info) {
306 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored.";
311 ::EndDeferWindowPos(defer_window_pos_info);
313 #if defined(USE_AURA)
314 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
315 for (size_t i = 0; i < moves.size(); ++i) {
316 const WebPluginGeometry& move = moves[i];
318 GetWindowRect(move.window, &r);
320 PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr));
323 for (size_t i = 0; i < invalidate_rects.size(); ++i) {
325 parent, &invalidate_rects[i], NULL,
326 // These flags are from WebPluginDelegateImpl::NativeWndProc.
327 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW);
334 void RenderWidgetHostViewBase::PaintPluginWindowsHelper(
335 HWND parent, const gfx::Rect& damaged_screen_rect) {
336 LPARAM lparam = reinterpret_cast<LPARAM>(&damaged_screen_rect);
337 EnumChildWindows(parent, PaintEnumChildProc, lparam);
341 void RenderWidgetHostViewBase::DetachPluginsHelper(HWND parent) {
342 // When a tab is closed all its child plugin windows are destroyed
343 // automatically. This happens before plugins get any notification that its
344 // instances are tearing down.
346 // Plugins like Quicktime assume that their windows will remain valid as long
347 // as they have plugin instances active. Quicktime crashes in this case
348 // because its windowing code cleans up an internal data structure that the
349 // handler for NPP_DestroyStream relies on.
351 // The fix is to detach plugin windows from web contents when it is going
352 // away. This will prevent the plugin windows from getting destroyed
353 // automatically. The detached plugin windows will get cleaned up in proper
354 // sequence as part of the usual cleanup when the plugin instance goes away.
355 EnumChildWindows(parent, DetachPluginWindowsCallbackInternal, NULL);
362 // How many microseconds apart input events should be flushed.
363 const int kFlushInputRateInUs = 16666;
367 RenderWidgetHostViewBase::RenderWidgetHostViewBase()
368 : popup_type_(blink::WebPopupTypeNone),
369 background_color_(SK_ColorWHITE),
370 mouse_locked_(false),
371 showing_context_menu_(false),
372 selection_text_offset_(0),
373 selection_range_(gfx::Range::InvalidRange()),
374 current_device_scale_factor_(0),
375 current_display_rotation_(gfx::Display::ROTATE_0),
376 pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
377 renderer_frame_number_(0),
378 weak_factory_(this) {
381 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
382 DCHECK(!mouse_locked_);
385 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){
389 void RenderWidgetHostViewBase::SetBackgroundColor(SkColor color) {
390 background_color_ = color;
393 void RenderWidgetHostViewBase::SetBackgroundColorToDefault() {
394 SetBackgroundColor(SK_ColorWHITE);
397 bool RenderWidgetHostViewBase::GetBackgroundOpaque() {
398 return SkColorGetA(background_color_) == SK_AlphaOPAQUE;
401 gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const {
402 gfx::NativeView view = GetNativeView();
403 gfx::Display display =
404 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
405 return gfx::ToCeiledSize(gfx::ScaleSize(GetRequestedRendererSize(),
406 display.device_scale_factor()));
409 float RenderWidgetHostViewBase::GetTopControlsLayoutHeight() const {
413 void RenderWidgetHostViewBase::SelectionChanged(const base::string16& text,
415 const gfx::Range& range) {
416 selection_text_ = text;
417 selection_text_offset_ = offset;
418 selection_range_.set_start(range.start());
419 selection_range_.set_end(range.end());
422 gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSize() const {
423 return GetViewBounds().size();
426 ui::TextInputClient* RenderWidgetHostViewBase::GetTextInputClient() {
431 bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
432 return showing_context_menu_;
435 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) {
436 DCHECK_NE(showing_context_menu_, showing);
437 showing_context_menu_ = showing;
440 base::string16 RenderWidgetHostViewBase::GetSelectedText() const {
441 if (!selection_range_.IsValid())
442 return base::string16();
443 return selection_text_.substr(
444 selection_range_.GetMin() - selection_text_offset_,
445 selection_range_.length());
448 bool RenderWidgetHostViewBase::IsMouseLocked() {
449 return mouse_locked_;
452 InputEventAckState RenderWidgetHostViewBase::FilterInputEvent(
453 const blink::WebInputEvent& input_event) {
454 // By default, input events are simply forwarded to the renderer.
455 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
458 void RenderWidgetHostViewBase::OnDidFlushInput() {
459 // The notification can safely be ignored by most implementations.
462 void RenderWidgetHostViewBase::OnSetNeedsFlushInput() {
463 if (flush_input_timer_.IsRunning())
466 flush_input_timer_.Start(
468 base::TimeDelta::FromMicroseconds(kFlushInputRateInUs),
470 &RenderWidgetHostViewBase::FlushInput);
473 void RenderWidgetHostViewBase::WheelEventAck(
474 const blink::WebMouseWheelEvent& event,
475 InputEventAckState ack_result) {
478 void RenderWidgetHostViewBase::GestureEventAck(
479 const blink::WebGestureEvent& event,
480 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::CreateBrowserAccessibilityManager(
493 BrowserAccessibilityDelegate* delegate) {
498 void RenderWidgetHostViewBase::AccessibilityShowMenu(const gfx::Point& point) {
501 gfx::Point RenderWidgetHostViewBase::AccessibilityOriginInScreen(
502 const gfx::Rect& bounds) {
503 return bounds.origin();
506 gfx::AcceleratedWidget
507 RenderWidgetHostViewBase::AccessibilityGetAcceleratedWidget() {
508 return gfx::kNullAcceleratedWidget;
511 gfx::NativeViewAccessible
512 RenderWidgetHostViewBase::AccessibilityGetNativeViewAccessible() {
516 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
517 RenderWidgetHostImpl* impl = NULL;
518 if (GetRenderWidgetHost())
519 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
522 impl->SendScreenRects();
524 if (HasDisplayPropertyChanged(view) && impl)
525 impl->NotifyScreenInfoChanged();
528 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
529 gfx::Display display =
530 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
531 if (current_display_area_ == display.work_area() &&
532 current_device_scale_factor_ == display.device_scale_factor() &&
533 current_display_rotation_ == display.rotation()) {
537 current_display_area_ = display.work_area();
538 current_device_scale_factor_ = display.device_scale_factor();
539 current_display_rotation_ = display.rotation();
543 base::WeakPtr<RenderWidgetHostViewBase> RenderWidgetHostViewBase::GetWeakPtr() {
544 return weak_factory_.GetWeakPtr();
547 scoped_ptr<SyntheticGestureTarget>
548 RenderWidgetHostViewBase::CreateSyntheticGestureTarget() {
549 RenderWidgetHostImpl* host =
550 RenderWidgetHostImpl::From(GetRenderWidgetHost());
551 return scoped_ptr<SyntheticGestureTarget>(
552 new SyntheticGestureTargetBase(host));
555 // Platform implementation should override this method to allow frame
556 // subscription. Frame subscriber is set to RenderProcessHost, which is
557 // platform independent. It should be set to the specific presenter on each
559 bool RenderWidgetHostViewBase::CanSubscribeFrame() const {
564 // Base implementation for this method sets the subscriber to RenderProcessHost,
565 // which is platform independent. Note: Implementation only support subscribing
566 // to accelerated composited frames.
567 void RenderWidgetHostViewBase::BeginFrameSubscription(
568 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
569 RenderWidgetHostImpl* impl = NULL;
570 if (GetRenderWidgetHost())
571 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
574 RenderProcessHostImpl* render_process_host =
575 static_cast<RenderProcessHostImpl*>(impl->GetProcess());
576 render_process_host->BeginFrameSubscription(impl->GetRoutingID(),
580 void RenderWidgetHostViewBase::EndFrameSubscription() {
581 RenderWidgetHostImpl* impl = NULL;
582 if (GetRenderWidgetHost())
583 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
586 RenderProcessHostImpl* render_process_host =
587 static_cast<RenderProcessHostImpl*>(impl->GetProcess());
588 render_process_host->EndFrameSubscription(impl->GetRoutingID());
591 uint32 RenderWidgetHostViewBase::RendererFrameNumber() {
592 return renderer_frame_number_;
595 void RenderWidgetHostViewBase::DidReceiveRendererFrame() {
596 ++renderer_frame_number_;
599 void RenderWidgetHostViewBase::FlushInput() {
600 RenderWidgetHostImpl* impl = NULL;
601 if (GetRenderWidgetHost())
602 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
608 SkColorType RenderWidgetHostViewBase::PreferredReadbackFormat() {
609 return kN32_SkColorType;
612 void RenderWidgetHostViewBase::OnTextSurroundingSelectionResponse(
613 const base::string16& content,
619 void RenderWidgetHostViewBase::ShowDisambiguationPopup(
620 const gfx::Rect& rect_pixels,
621 const SkBitmap& zoomed_bitmap) {
625 gfx::Size RenderWidgetHostViewBase::GetVisibleViewportSize() const {
626 return GetViewBounds().size();
629 void RenderWidgetHostViewBase::SetInsets(const gfx::Insets& insets) {
634 blink::WebScreenOrientationType
635 RenderWidgetHostViewBase::GetOrientationTypeForMobile(
636 const gfx::Display& display) {
637 int angle = display.RotationAsDegree();
638 const gfx::Rect& bounds = display.bounds();
640 // Whether the device's natural orientation is portrait.
641 bool natural_portrait = false;
642 if (angle == 0 || angle == 180) // The device is in its natural orientation.
643 natural_portrait = bounds.height() >= bounds.width();
645 natural_portrait = bounds.height() <= bounds.width();
649 return natural_portrait ? blink::WebScreenOrientationPortraitPrimary
650 : blink::WebScreenOrientationLandscapePrimary;
652 return natural_portrait ? blink::WebScreenOrientationLandscapePrimary
653 : blink::WebScreenOrientationPortraitSecondary;
655 return natural_portrait ? blink::WebScreenOrientationPortraitSecondary
656 : blink::WebScreenOrientationLandscapeSecondary;
658 return natural_portrait ? blink::WebScreenOrientationLandscapeSecondary
659 : blink::WebScreenOrientationPortraitPrimary;
662 return blink::WebScreenOrientationPortraitPrimary;
667 blink::WebScreenOrientationType
668 RenderWidgetHostViewBase::GetOrientationTypeForDesktop(
669 const gfx::Display& display) {
670 static int primary_landscape_angle = -1;
671 static int primary_portrait_angle = -1;
673 int angle = display.RotationAsDegree();
674 const gfx::Rect& bounds = display.bounds();
675 bool is_portrait = bounds.height() >= bounds.width();
677 if (is_portrait && primary_portrait_angle == -1)
678 primary_portrait_angle = angle;
680 if (!is_portrait && primary_landscape_angle == -1)
681 primary_landscape_angle = angle;
684 return primary_portrait_angle == angle
685 ? blink::WebScreenOrientationPortraitPrimary
686 : blink::WebScreenOrientationPortraitSecondary;
689 return primary_landscape_angle == angle
690 ? blink::WebScreenOrientationLandscapePrimary
691 : blink::WebScreenOrientationLandscapeSecondary;
694 } // namespace content