- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / render_widget_host_view_base.cc
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/basic_mouse_wheel_smooth_scroll_gesture.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 "content/port/browser/synthetic_gesture.h"
15 #include "third_party/WebKit/public/web/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"
20
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
36
37 #if defined(TOOLKIT_GTK)
38 #include <gdk/gdkx.h>
39 #include <gtk/gtk.h>
40
41 #include "content/browser/renderer_host/gtk_window_utils.h"
42 #endif
43
44 namespace content {
45
46 // static
47 RenderWidgetHostViewPort* RenderWidgetHostViewPort::FromRWHV(
48     RenderWidgetHostView* rwhv) {
49   return static_cast<RenderWidgetHostViewPort*>(rwhv);
50 }
51
52 // static
53 RenderWidgetHostViewPort* RenderWidgetHostViewPort::CreateViewForWidget(
54     RenderWidgetHost* widget) {
55   return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget));
56 }
57
58 #if defined(OS_WIN)
59
60 namespace {
61
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;
67
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;
74       continue;
75     }
76     if (base::GetProcId(iter.GetData().handle) == plugin_process_id) {
77       iter->AddWindow(parent);
78       return;
79     }
80   }
81
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
85     // delay.
86     if (tries > 0) {
87       base::MessageLoop::current()->PostDelayedTask(
88           FROM_HERE,
89           base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1),
90           base::TimeDelta::FromMilliseconds(kTryDelayMs));
91       return;
92     }
93   }
94
95   // The plugin process might have died in the time to execute the task, don't
96   // leak the HWND.
97   PostMessage(parent, WM_CLOSE, 0, 0);
98 }
99
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)) {
108       case WM_LBUTTONDOWN:
109       case WM_RBUTTONDOWN:
110       case WM_MBUTTONDOWN:
111         ::SendMessage(GetParent(window), message, wparam, lparam);
112         return 0;
113       default:
114         break;
115     }
116   }
117   return ::DefWindowProc(window, message, wparam, lparam);
118 }
119
120 bool IsPluginWrapperWindow(HWND window) {
121   return gfx::GetClassNameW(window) ==
122       string16(kWrapperNativeWindowClassName);
123 }
124
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;
129   if (!atom) {
130     WNDCLASSEX window_class;
131     base::win::InitializeWindowClass(
132         kWrapperNativeWindowClassName,
133         &base::win::WrappedWindowProc<PluginWrapperWindowProc>,
134         CS_DBLCLKS,
135         0,
136         0,
137         NULL,
138         // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1),
139         reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1),
140         NULL,
141         NULL,
142         NULL,
143         &window_class);
144     instance = window_class.hInstance;
145     atom = RegisterClassEx(&window_class);
146   }
147   DCHECK(atom);
148
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
157   // the HWND.
158   static const int kMaxTries = 5;
159   BrowserThread::PostTask(
160       BrowserThread::IO,
161       FROM_HERE,
162       base::Bind(&NotifyPluginProcessHostHelper, window, new_parent,
163                  kMaxTries));
164   return new_parent;
165 }
166
167 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) {
168   if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd))
169     return TRUE;
170
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());
176
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);
181
182   return TRUE;
183 }
184
185 // Windows callback for OnDestroy to detach the plugin windows.
186 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) {
187   RenderWidgetHostViewBase::DetachPluginWindowsCallback(window);
188   return TRUE;
189 }
190
191 }  // namespace
192
193 // static
194 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) {
195   if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) &&
196       !IsHungAppWindow(window)) {
197     ::ShowWindow(window, SW_HIDE);
198     SetParent(window, NULL);
199   }
200 }
201
202 // static
203 void RenderWidgetHostViewBase::MovePluginWindowsHelper(
204     HWND parent,
205     const std::vector<WebPluginGeometry>& moves) {
206   if (moves.empty())
207     return;
208
209   bool oop_plugins =
210     !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) &&
211     !CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessPlugins);
212
213   HDWP defer_window_pos_info =
214       ::BeginDeferWindowPos(static_cast<int>(moves.size()));
215
216   if (!defer_window_pos_info) {
217     NOTREACHED();
218     return;
219   }
220
221 #if defined(USE_AURA)
222   std::vector<RECT> invalidate_rects;
223 #endif
224
225   for (size_t i = 0; i < moves.size(); ++i) {
226     unsigned long flags = 0;
227     const WebPluginGeometry& move = moves[i];
228     HWND window = move.window;
229
230     // As the plugin parent window which lives on the browser UI thread is
231     // destroyed asynchronously, it is possible that we have a stale window
232     // sent in by the renderer for moving around.
233     // Note: get the parent before checking if the window is valid, to avoid a
234     // race condition where the window is destroyed after the check but before
235     // the GetParent call.
236     HWND cur_parent = ::GetParent(window);
237     if (!::IsWindow(window))
238       continue;
239
240     if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window)) {
241       // The renderer should only be trying to move plugin windows. However,
242       // this may happen as a result of a race condition (i.e. even after the
243       // check right above), so we ignore it.
244       continue;
245     }
246
247     if (oop_plugins) {
248       if (cur_parent == GetDesktopWindow()) {
249         // The plugin window hasn't been parented yet, add an intermediate
250         // window that lives on this thread to speed up scrolling. Note this
251         // only works with out of process plugins since we depend on
252         // PluginProcessHost to destroy the intermediate HWNDs.
253         cur_parent = ReparentWindow(window, parent);
254         ::ShowWindow(window, SW_SHOW);  // Window was created hidden.
255       } else if (!IsPluginWrapperWindow(cur_parent)) {
256         continue;  // Race if plugin process is shutting down.
257       }
258
259       // We move the intermediate parent window which doesn't result in cross-
260       // process synchronous Windows messages.
261       window = cur_parent;
262     } else {
263       if (cur_parent == GetDesktopWindow())
264         SetParent(window, parent);
265     }
266
267     if (move.visible)
268       flags |= SWP_SHOWWINDOW;
269     else
270       flags |= SWP_HIDEWINDOW;
271
272 #if defined(USE_AURA)
273     if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
274       // Without this flag, Windows repaints the parent area uncovered by this
275       // move. However when software compositing is used the clipping region is
276       // ignored. Since in Aura the browser chrome could be under the plugin, if
277       // if Windows tries to paint it synchronously inside EndDeferWindowsPos
278       // then it won't have the data and it will flash white. So instead we
279       // manually redraw the plugin.
280       // Why not do this for native Windows? Not sure if there are any
281       // performance issues with this.
282       flags |= SWP_NOREDRAW;
283     }
284 #endif
285
286     if (move.rects_valid) {
287       gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect);
288       HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(),
289                                   clip_rect_in_pixel.y(),
290                                   clip_rect_in_pixel.right(),
291                                   clip_rect_in_pixel.bottom());
292       gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects);
293
294       // Note: System will own the hrgn after we call SetWindowRgn,
295       // so we don't need to call DeleteObject(hrgn)
296       ::SetWindowRgn(window, hrgn, !move.clip_rect.IsEmpty());
297
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
301       // plugin.
302       if (clip_rect_in_pixel.IsEmpty() &&
303           !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {
304         RECT r;
305         GetClientRect(window, &r);
306         MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2);
307         invalidate_rects.push_back(r);
308       }
309 #endif
310     } else {
311       flags |= SWP_NOMOVE;
312       flags |= SWP_NOSIZE;
313     }
314
315     gfx::Rect window_rect_in_pixel =
316         gfx::win::DIPToScreenRect(move.window_rect);
317     defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info,
318                                              window, NULL,
319                                              window_rect_in_pixel.x(),
320                                              window_rect_in_pixel.y(),
321                                              window_rect_in_pixel.width(),
322                                              window_rect_in_pixel.height(),
323                                              flags);
324
325     if (!defer_window_pos_info) {
326       DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored.";
327       return;
328     }
329   }
330
331   ::EndDeferWindowPos(defer_window_pos_info);
332
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];
337       RECT r;
338       GetWindowRect(move.window, &r);
339       gfx::Rect gr(r);
340       PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr));
341     }
342   } else {
343       for (size_t i = 0; i < invalidate_rects.size(); ++i) {
344       ::RedrawWindow(
345           parent, &invalidate_rects[i], NULL,
346           // These flags are from WebPluginDelegateImpl::NativeWndProc.
347           RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW);
348     }
349   }
350 #endif
351 }
352
353 // static
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);
358 }
359
360 // static
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.
365   //
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.
370   //
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);
376 }
377
378 #endif  // OS_WIN
379
380 RenderWidgetHostViewBase::RenderWidgetHostViewBase()
381     : popup_type_(WebKit::WebPopupTypeNone),
382       mouse_locked_(false),
383       showing_context_menu_(false),
384       selection_text_offset_(0),
385       selection_range_(gfx::Range::InvalidRange()),
386       current_device_scale_factor_(0),
387       renderer_frame_number_(0) {
388 }
389
390 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
391   DCHECK(!mouse_locked_);
392 }
393
394 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){
395   return false;
396 }
397
398 void RenderWidgetHostViewBase::SetBackground(const SkBitmap& background) {
399   background_ = background;
400 }
401
402 const SkBitmap& RenderWidgetHostViewBase::GetBackground() {
403   return background_;
404 }
405
406 gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const {
407   gfx::NativeView view = GetNativeView();
408   gfx::Display display =
409       gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
410   return gfx::ToCeiledSize(gfx::ScaleSize(GetViewBounds().size(),
411                                           display.device_scale_factor()));
412 }
413
414 float RenderWidgetHostViewBase::GetOverdrawBottomHeight() const {
415   return 0.f;
416 }
417
418 void RenderWidgetHostViewBase::SelectionChanged(const string16& text,
419                                                 size_t offset,
420                                                 const gfx::Range& range) {
421   selection_text_ = text;
422   selection_text_offset_ = offset;
423   selection_range_.set_start(range.start());
424   selection_range_.set_end(range.end());
425 }
426
427 bool RenderWidgetHostViewBase::IsShowingContextMenu() const {
428   return showing_context_menu_;
429 }
430
431 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) {
432   DCHECK_NE(showing_context_menu_, showing);
433   showing_context_menu_ = showing;
434 }
435
436 string16 RenderWidgetHostViewBase::GetSelectedText() const {
437   if (!selection_range_.IsValid())
438     return string16();
439   return selection_text_.substr(
440       selection_range_.GetMin() - selection_text_offset_,
441       selection_range_.length());
442 }
443
444 bool RenderWidgetHostViewBase::IsMouseLocked() {
445   return mouse_locked_;
446 }
447
448 void RenderWidgetHostViewBase::UnhandledWheelEvent(
449     const WebKit::WebMouseWheelEvent& event) {
450   // Most implementations don't need to do anything here.
451 }
452
453 InputEventAckState RenderWidgetHostViewBase::FilterInputEvent(
454     const WebKit::WebInputEvent& input_event) {
455   // By default, input events are simply forwarded to the renderer.
456   return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
457 }
458
459 void RenderWidgetHostViewBase::OnDidFlushInput() {
460   // The notification can safely be ignored by most implementations.
461 }
462
463 void RenderWidgetHostViewBase::OnSetNeedsFlushInput() {
464   NOTIMPLEMENTED();
465 }
466
467 void RenderWidgetHostViewBase::GestureEventAck(int gesture_event_type,
468                                                InputEventAckState ack_result) {}
469
470 void RenderWidgetHostViewBase::SetPopupType(WebKit::WebPopupType popup_type) {
471   popup_type_ = popup_type;
472 }
473
474 WebKit::WebPopupType RenderWidgetHostViewBase::GetPopupType() {
475   return popup_type_;
476 }
477
478 BrowserAccessibilityManager*
479     RenderWidgetHostViewBase::GetBrowserAccessibilityManager() const {
480   return browser_accessibility_manager_.get();
481 }
482
483 void RenderWidgetHostViewBase::SetBrowserAccessibilityManager(
484     BrowserAccessibilityManager* manager) {
485   browser_accessibility_manager_.reset(manager);
486 }
487
488 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
489   RenderWidgetHostImpl* impl = NULL;
490   if (GetRenderWidgetHost())
491     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
492
493   if (impl)
494     impl->SendScreenRects();
495
496   if (HasDisplayPropertyChanged(view) && impl)
497     impl->NotifyScreenInfoChanged();
498 }
499
500 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
501   gfx::Display display =
502       gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
503   if (current_display_area_ == display.work_area() &&
504       current_device_scale_factor_ == display.device_scale_factor()) {
505     return false;
506   }
507   current_display_area_ = display.work_area();
508   current_device_scale_factor_ = display.device_scale_factor();
509   return true;
510 }
511
512 SyntheticGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture(
513     bool scroll_down, int pixels_to_scroll, int mouse_event_x,
514     int mouse_event_y) {
515   return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll,
516                                                 mouse_event_x, mouse_event_y);
517 }
518
519 SyntheticGesture* RenderWidgetHostViewBase::CreatePinchGesture(
520     bool zoom_in, int pixels_to_move, int anchor_x,
521     int anchor_y) {
522   // There is no generic implementation for pinch gestures.
523   NOTIMPLEMENTED();
524   return NULL;
525 }
526
527 void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
528     const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
529 }
530
531 // Platform implementation should override this method to allow frame
532 // subscription. Frame subscriber is set to RenderProcessHost, which is
533 // platform independent. It should be set to the specific presenter on each
534 // platform.
535 bool RenderWidgetHostViewBase::CanSubscribeFrame() const {
536   NOTIMPLEMENTED();
537   return false;
538 }
539
540 // Base implementation for this method sets the subscriber to RenderProcessHost,
541 // which is platform independent. Note: Implementation only support subscribing
542 // to accelerated composited frames.
543 void RenderWidgetHostViewBase::BeginFrameSubscription(
544     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
545   RenderWidgetHostImpl* impl = NULL;
546   if (GetRenderWidgetHost())
547     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
548   if (!impl)
549     return;
550   RenderProcessHostImpl* render_process_host =
551       static_cast<RenderProcessHostImpl*>(impl->GetProcess());
552   render_process_host->BeginFrameSubscription(impl->GetRoutingID(),
553                                               subscriber.Pass());
554 }
555
556 void RenderWidgetHostViewBase::EndFrameSubscription() {
557   RenderWidgetHostImpl* impl = NULL;
558   if (GetRenderWidgetHost())
559     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
560   if (!impl)
561     return;
562   RenderProcessHostImpl* render_process_host =
563       static_cast<RenderProcessHostImpl*>(impl->GetProcess());
564   render_process_host->EndFrameSubscription(impl->GetRoutingID());
565 }
566
567 void RenderWidgetHostViewBase::OnOverscrolled(
568     gfx::Vector2dF accumulated_overscroll,
569     gfx::Vector2dF current_fling_velocity) {
570 }
571
572 uint32 RenderWidgetHostViewBase::RendererFrameNumber() {
573   return renderer_frame_number_;
574 }
575
576 void RenderWidgetHostViewBase::DidReceiveRendererFrame() {
577   ++renderer_frame_number_;
578 }
579
580 }  // namespace content