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