Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2014 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/legacy_render_widget_host_win.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/win/windows_version.h"
     10 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
     11 #include "content/browser/accessibility/browser_accessibility_win.h"
     12 #include "content/browser/renderer_host/render_widget_host_impl.h"
     13 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
     14 #include "content/public/browser/browser_accessibility_state.h"
     15 #include "content/public/common/content_switches.h"
     16 #include "ui/base/touch/touch_enabled.h"
     17 #include "ui/base/view_prop.h"
     18 #include "ui/base/win/internal_constants.h"
     19 #include "ui/base/win/window_event_target.h"
     20 #include "ui/gfx/geometry/rect.h"
     21 #include "ui/gfx/win/dpi.h"
     22 
     23 namespace content {
     24 
     25 // A custom MSAA object id used to determine if a screen reader or some
     26 // other client is listening on MSAA events - if so, we enable full web
     27 // accessibility support.
     28 const int kIdScreenReaderHoneyPot = 1;
     29 
     30 // static
     31 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
     32     HWND parent) {
     33   // content_unittests passes in the desktop window as the parent. We allow
     34   // the LegacyRenderWidgetHostHWND instance to be created in this case for
     35   // these tests to pass.
     36   if (CommandLine::ForCurrentProcess()->HasSwitch(
     37           switches::kDisableLegacyIntermediateWindow) ||
     38       (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
     39     return NULL;
     40 
     41   LegacyRenderWidgetHostHWND* legacy_window_instance =
     42       new LegacyRenderWidgetHostHWND(parent);
     43   // If we failed to create the child, or if the switch to disable the legacy
     44   // window is passed in, then return NULL.
     45   if (!::IsWindow(legacy_window_instance->hwnd())) {
     46     delete legacy_window_instance;
     47     return NULL;
     48   }
     49   legacy_window_instance->Init();
     50   return legacy_window_instance;
     51 }
     52 
     53 void LegacyRenderWidgetHostHWND::Destroy() {
     54   if (::IsWindow(hwnd()))
     55     ::DestroyWindow(hwnd());
     56 }
     57 
     58 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
     59   ::SetParent(hwnd(), parent);
     60   // If the new parent is the desktop Window, then we disable the child window
     61   // to ensure that it does not receive any input events. It should not because
     62   // of WS_EX_TRANSPARENT. This is only for safety.
     63   if (parent == ::GetDesktopWindow()) {
     64     ::EnableWindow(hwnd(), FALSE);
     65   } else {
     66     ::EnableWindow(hwnd(), TRUE);
     67   }
     68 }
     69 
     70 HWND LegacyRenderWidgetHostHWND::GetParent() {
     71   return ::GetParent(hwnd());
     72 }
     73 
     74 void LegacyRenderWidgetHostHWND::Show() {
     75   ::ShowWindow(hwnd(), SW_SHOW);
     76 }
     77 
     78 void LegacyRenderWidgetHostHWND::Hide() {
     79   ::ShowWindow(hwnd(), SW_HIDE);
     80 }
     81 
     82 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
     83   gfx::Rect bounds_in_pixel = gfx::win::DIPToScreenRect(bounds);
     84   ::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(),
     85                  bounds_in_pixel.width(), bounds_in_pixel.height(),
     86                  SWP_NOREDRAW);
     87 }
     88 
     89 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
     90   if (host_) {
     91     host_->OnLegacyWindowDestroyed();
     92     host_ = NULL;
     93   }
     94   delete this;
     95 }
     96 
     97 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
     98     : mouse_tracking_enabled_(false),
     99       host_(NULL) {
    100   RECT rect = {0};
    101   Base::Create(parent, rect, L"Chrome Legacy Window",
    102                WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
    103                WS_EX_TRANSPARENT);
    104 }
    105 
    106 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
    107   DCHECK(!::IsWindow(hwnd()));
    108 }
    109 
    110 bool LegacyRenderWidgetHostHWND::Init() {
    111   if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
    112       ui::AreTouchEventsEnabled())
    113     RegisterTouchWindow(hwnd(), TWF_WANTPALM);
    114 
    115   HRESULT hr = ::CreateStdAccessibleObject(
    116       hwnd(), OBJID_WINDOW, IID_IAccessible,
    117       reinterpret_cast<void **>(window_accessible_.Receive()));
    118   DCHECK(SUCCEEDED(hr));
    119 
    120   if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
    121     // Attempt to detect screen readers or other clients who want full
    122     // accessibility support, by seeing if they respond to this event.
    123     NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
    124                    CHILDID_SELF);
    125   }
    126 
    127   return !!SUCCEEDED(hr);
    128 }
    129 
    130 // static
    131 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
    132     HWND parent) {
    133   return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
    134       parent, ui::WindowEventTarget::kWin32InputEventTarget));
    135 }
    136 
    137 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
    138                                                  WPARAM w_param,
    139                                                  LPARAM l_param) {
    140   return 1;
    141 }
    142 
    143 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
    144                                                 WPARAM w_param,
    145                                                 LPARAM l_param) {
    146   // Only the lower 32 bits of l_param are valid when checking the object id
    147   // because it sometimes gets sign-extended incorrectly (but not always).
    148   DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
    149 
    150   if (kIdScreenReaderHoneyPot == obj_id) {
    151     // When an MSAA client has responded to our fake event on this id,
    152     // enable screen reader support.
    153     BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
    154     return static_cast<LRESULT>(0L);
    155   }
    156 
    157   if (OBJID_CLIENT != obj_id || !host_)
    158     return static_cast<LRESULT>(0L);
    159 
    160   RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(
    161       host_->GetRenderWidgetHost());
    162   if (!rwhi)
    163     return static_cast<LRESULT>(0L);
    164 
    165   BrowserAccessibilityManagerWin* manager =
    166       static_cast<BrowserAccessibilityManagerWin*>(
    167           rwhi->GetRootBrowserAccessibilityManager());
    168   if (!manager)
    169     return static_cast<LRESULT>(0L);
    170 
    171   base::win::ScopedComPtr<IAccessible> root(
    172       manager->GetRoot()->ToBrowserAccessibilityWin());
    173   return LresultFromObject(IID_IAccessible, w_param,
    174       static_cast<IAccessible*>(root.Detach()));
    175 }
    176 
    177 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
    178 // While this works, this has the side effect of converting input messages into
    179 // sent messages which changes their priority and could technically result
    180 // in these messages starving other messages in the queue. Additionally
    181 // keyboard/mouse hooks would not see these messages. The alternative approach
    182 // is to set and release capture as needed on the parent to ensure that it
    183 // receives all mouse events. However that was shelved due to possible issues
    184 // with capture changes.
    185 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
    186                                                     WPARAM w_param,
    187                                                     LPARAM l_param,
    188                                                     BOOL& handled) {
    189   LRESULT ret = 0;
    190   if (GetWindowEventTarget(GetParent())) {
    191     bool msg_handled = false;
    192     ret = GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
    193         message, w_param, l_param, &msg_handled);
    194     handled = msg_handled;
    195   }
    196   return ret;
    197 }
    198 
    199 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
    200                                                  WPARAM w_param,
    201                                                  LPARAM l_param,
    202                                                  BOOL& handled) {
    203   if (message == WM_MOUSEMOVE) {
    204     if (!mouse_tracking_enabled_) {
    205       mouse_tracking_enabled_ = true;
    206       TRACKMOUSEEVENT tme;
    207       tme.cbSize = sizeof(tme);
    208       tme.dwFlags = TME_LEAVE;
    209       tme.hwndTrack = hwnd();
    210       tme.dwHoverTime = 0;
    211       TrackMouseEvent(&tme);
    212     }
    213   }
    214   // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
    215   // in screen coordinates. We should not be converting them to parent
    216   // coordinates.
    217   if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
    218       (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
    219     POINT mouse_coords;
    220     mouse_coords.x = GET_X_LPARAM(l_param);
    221     mouse_coords.y = GET_Y_LPARAM(l_param);
    222     ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
    223     l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
    224   }
    225 
    226   LRESULT ret = 0;
    227 
    228   if (GetWindowEventTarget(GetParent())) {
    229     bool msg_handled = false;
    230     ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
    231         message, w_param, l_param, &msg_handled);
    232     handled = msg_handled;
    233     // If the parent did not handle non client mouse messages, we call
    234     // DefWindowProc on the message with the parent window handle. This
    235     // ensures that WM_SYSCOMMAND is generated for the parent and we are
    236     // out of the picture.
    237     if (!handled &&
    238          (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
    239       ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
    240       handled = TRUE;
    241     }
    242   }
    243   return ret;
    244 }
    245 
    246 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
    247                                                  WPARAM w_param,
    248                                                  LPARAM l_param) {
    249   mouse_tracking_enabled_ = false;
    250   LRESULT ret = 0;
    251   if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
    252     // We should send a WM_MOUSELEAVE to the parent window only if the mouse
    253     // has moved outside the bounds of the parent.
    254     POINT cursor_pos;
    255     ::GetCursorPos(&cursor_pos);
    256     if (::WindowFromPoint(cursor_pos) != GetParent()) {
    257       bool msg_handled = false;
    258       ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
    259           message, w_param, l_param, &msg_handled);
    260       SetMsgHandled(msg_handled);
    261     }
    262   }
    263   return ret;
    264 }
    265 
    266 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
    267                                                     WPARAM w_param,
    268                                                     LPARAM l_param) {
    269   // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
    270   // message going all the way to the parent which then messes up state
    271   // related to focused views, etc. This is because it treats this as if
    272   // it lost activation.
    273   // Our dummy window should not interfere with focus and activation in
    274   // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
    275   // is preserved. The only exception is if the parent was created with the
    276   // WS_EX_NOACTIVATE style.
    277   if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
    278     return MA_NOACTIVATE;
    279   // On Windows, if we select the menu item by touch and if the window at the
    280   // location is another window on the same thread, that window gets a
    281   // WM_MOUSEACTIVATE message and ends up activating itself, which is not
    282   // correct. We workaround this by setting a property on the window at the
    283   // current cursor location. We check for this property in our
    284   // WM_MOUSEACTIVATE handler and don't activate the window if the property is
    285   // set.
    286   if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
    287     ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
    288     return MA_NOACTIVATE;
    289   }
    290   return MA_ACTIVATE;
    291 }
    292 
    293 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
    294                                             WPARAM w_param,
    295                                             LPARAM l_param) {
    296   LRESULT ret = 0;
    297   if (GetWindowEventTarget(GetParent())) {
    298     bool msg_handled = false;
    299     ret = GetWindowEventTarget(GetParent())->HandleTouchMessage(
    300         message, w_param, l_param, &msg_handled);
    301     SetMsgHandled(msg_handled);
    302   }
    303   return ret;
    304 }
    305 
    306 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
    307                                              WPARAM w_param,
    308                                              LPARAM l_param) {
    309   LRESULT ret = 0;
    310   if (GetWindowEventTarget(GetParent())) {
    311     bool msg_handled = false;
    312     ret = GetWindowEventTarget(GetParent())->HandleScrollMessage(
    313         message, w_param, l_param, &msg_handled);
    314     SetMsgHandled(msg_handled);
    315   }
    316   return ret;
    317 }
    318 
    319 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
    320                                                 WPARAM w_param,
    321                                                 LPARAM l_param) {
    322   if (GetWindowEventTarget(GetParent())) {
    323     bool msg_handled = false;
    324     LRESULT hit_test = GetWindowEventTarget(
    325         GetParent())->HandleNcHitTestMessage(message, w_param, l_param,
    326                                              &msg_handled);
    327     // If the parent returns HTNOWHERE which can happen for popup windows, etc
    328     // we return HTCLIENT.
    329     if (hit_test == HTNOWHERE)
    330       hit_test = HTCLIENT;
    331     return hit_test;
    332   }
    333   return HTNOWHERE;
    334 }
    335 
    336 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
    337                                               WPARAM w_param,
    338                                               LPARAM l_param) {
    339   return 0;
    340 }
    341 
    342 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
    343                                             WPARAM w_param,
    344                                             LPARAM l_param) {
    345   PAINTSTRUCT ps = {0};
    346   ::BeginPaint(hwnd(), &ps);
    347   ::EndPaint(hwnd(), &ps);
    348   return 0;
    349 }
    350 
    351 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
    352                                                 WPARAM w_param,
    353                                                 LPARAM l_param) {
    354   return 0;
    355 }
    356 
    357 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
    358                                                  WPARAM w_param,
    359                                                  LPARAM l_param) {
    360   // Prevent scrollbars, etc from drawing.
    361   return 0;
    362 }
    363 
    364 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
    365                                            WPARAM w_param,
    366                                            LPARAM l_param) {
    367   // Certain trackpad drivers on Windows have bugs where in they don't generate
    368   // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
    369   // unless there is an entry for Chrome with the class name of the Window.
    370   // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
    371   // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
    372   // We add these styles to ensure that trackpad/trackpoint scrolling
    373   // work.
    374   long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
    375   ::SetWindowLong(hwnd(), GWL_STYLE,
    376                   current_style | WS_VSCROLL | WS_HSCROLL);
    377   return 0;
    378 }
    379 
    380 }  // namespace content
    381