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