Home | History | Annotate | Download | only in renderer_host
      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/smooth_scroll_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/base/win/dpi.h"
     33 #include "ui/base/win/hwnd_util.h"
     34 #include "ui/gfx/gdi_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 ui::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   ui::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 = ui::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 = ui::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         ui::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_(ui::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 ui::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::GestureEventAck(int gesture_event_type,
    460                                                InputEventAckState ack_result) {}
    461 
    462 void RenderWidgetHostViewBase::SetPopupType(WebKit::WebPopupType popup_type) {
    463   popup_type_ = popup_type;
    464 }
    465 
    466 WebKit::WebPopupType RenderWidgetHostViewBase::GetPopupType() {
    467   return popup_type_;
    468 }
    469 
    470 BrowserAccessibilityManager*
    471     RenderWidgetHostViewBase::GetBrowserAccessibilityManager() const {
    472   return browser_accessibility_manager_.get();
    473 }
    474 
    475 void RenderWidgetHostViewBase::SetBrowserAccessibilityManager(
    476     BrowserAccessibilityManager* manager) {
    477   browser_accessibility_manager_.reset(manager);
    478 }
    479 
    480 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
    481   RenderWidgetHostImpl* impl = NULL;
    482   if (GetRenderWidgetHost())
    483     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
    484 
    485   if (impl)
    486     impl->SendScreenRects();
    487 
    488   if (HasDisplayPropertyChanged(view) && impl)
    489     impl->NotifyScreenInfoChanged();
    490 }
    491 
    492 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
    493   gfx::Display display =
    494       gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view);
    495   if (current_display_area_ == display.work_area() &&
    496       current_device_scale_factor_ == display.device_scale_factor()) {
    497     return false;
    498   }
    499   current_display_area_ = display.work_area();
    500   current_device_scale_factor_ = display.device_scale_factor();
    501   return true;
    502 }
    503 
    504 SmoothScrollGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture(
    505     bool scroll_down, int pixels_to_scroll, int mouse_event_x,
    506     int mouse_event_y) {
    507   return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll,
    508                                                 mouse_event_x, mouse_event_y);
    509 }
    510 
    511 void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
    512     const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
    513 }
    514 
    515 // Platform implementation should override this method to allow frame
    516 // subscription. Frame subscriber is set to RenderProcessHost, which is
    517 // platform independent. It should be set to the specific presenter on each
    518 // platform.
    519 bool RenderWidgetHostViewBase::CanSubscribeFrame() const {
    520   NOTIMPLEMENTED();
    521   return false;
    522 }
    523 
    524 // Base implementation for this method sets the subscriber to RenderProcessHost,
    525 // which is platform independent. Note: Implementation only support subscribing
    526 // to accelerated composited frames.
    527 void RenderWidgetHostViewBase::BeginFrameSubscription(
    528     scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) {
    529   RenderWidgetHostImpl* impl = NULL;
    530   if (GetRenderWidgetHost())
    531     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
    532   if (!impl)
    533     return;
    534   RenderProcessHostImpl* render_process_host =
    535       static_cast<RenderProcessHostImpl*>(impl->GetProcess());
    536   render_process_host->BeginFrameSubscription(impl->GetRoutingID(),
    537                                               subscriber.Pass());
    538 }
    539 
    540 void RenderWidgetHostViewBase::EndFrameSubscription() {
    541   RenderWidgetHostImpl* impl = NULL;
    542   if (GetRenderWidgetHost())
    543     impl = RenderWidgetHostImpl::From(GetRenderWidgetHost());
    544   if (!impl)
    545     return;
    546   RenderProcessHostImpl* render_process_host =
    547       static_cast<RenderProcessHostImpl*>(impl->GetProcess());
    548   render_process_host->EndFrameSubscription(impl->GetRoutingID());
    549 }
    550 
    551 void RenderWidgetHostViewBase::OnOverscrolled(
    552     gfx::Vector2dF accumulated_overscroll,
    553     gfx::Vector2dF current_fling_velocity) {
    554 }
    555 
    556 uint32 RenderWidgetHostViewBase::RendererFrameNumber() {
    557   return renderer_frame_number_;
    558 }
    559 
    560 void RenderWidgetHostViewBase::DidReceiveRendererFrame() {
    561   ++renderer_frame_number_;
    562 }
    563 
    564 }  // namespace content
    565