Home | History | Annotate | Download | only in web_contents
      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/web_contents/web_contents_view_win.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_vector.h"
      9 #include "content/browser/renderer_host/render_view_host_factory.h"
     10 #include "content/browser/renderer_host/render_view_host_impl.h"
     11 #include "content/browser/renderer_host/render_widget_host_view_win.h"
     12 #include "content/browser/web_contents/interstitial_page_impl.h"
     13 #include "content/browser/web_contents/web_contents_drag_win.h"
     14 #include "content/browser/web_contents/web_contents_impl.h"
     15 #include "content/browser/web_contents/web_drag_dest_win.h"
     16 #include "content/public/browser/web_contents_delegate.h"
     17 #include "content/public/browser/web_contents_view_delegate.h"
     18 #include "ui/base/win/hidden_window.h"
     19 #include "ui/base/win/hwnd_subclass.h"
     20 #include "ui/gfx/screen.h"
     21 
     22 namespace content {
     23 WebContentsViewPort* CreateWebContentsView(
     24     WebContentsImpl* web_contents,
     25     WebContentsViewDelegate* delegate,
     26     RenderViewHostDelegateView** render_view_host_delegate_view) {
     27   WebContentsViewWin* rv = new WebContentsViewWin(web_contents, delegate);
     28   *render_view_host_delegate_view = rv;
     29   return rv;
     30 }
     31 
     32 namespace {
     33 
     34 typedef std::map<HWND, WebContentsViewWin*> HwndToWcvMap;
     35 HwndToWcvMap hwnd_to_wcv_map;
     36 
     37 void RemoveHwndToWcvMapEntry(WebContentsViewWin* wcv) {
     38   HwndToWcvMap::iterator it;
     39   for (it = hwnd_to_wcv_map.begin(); it != hwnd_to_wcv_map.end();) {
     40     if (it->second == wcv)
     41       hwnd_to_wcv_map.erase(it++);
     42     else
     43       ++it;
     44   }
     45 }
     46 
     47 BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) {
     48   HwndToWcvMap::iterator it = hwnd_to_wcv_map.find(hwnd);
     49   if (it == hwnd_to_wcv_map.end())
     50     return TRUE;  // must return TRUE to continue enumeration.
     51   WebContentsViewWin* wcv = it->second;
     52   RenderWidgetHostViewWin* rwhv = static_cast<RenderWidgetHostViewWin*>(
     53       wcv->web_contents()->GetRenderWidgetHostView());
     54   if (rwhv)
     55     rwhv->UpdateScreenInfo(rwhv->GetNativeView());
     56 
     57   return TRUE;  // must return TRUE to continue enumeration.
     58 }
     59 
     60 class PositionChangedMessageFilter : public ui::HWNDMessageFilter {
     61  public:
     62   PositionChangedMessageFilter() {}
     63 
     64  private:
     65   // Overridden from ui::HWNDMessageFilter:
     66   virtual bool FilterMessage(HWND hwnd,
     67                              UINT message,
     68                              WPARAM w_param,
     69                              LPARAM l_param,
     70                              LRESULT* l_result) OVERRIDE {
     71     if (message == WM_WINDOWPOSCHANGED || message == WM_SETTINGCHANGE)
     72       EnumChildWindows(hwnd, EnumChildProc, 0);
     73 
     74     return false;
     75   }
     76 
     77   DISALLOW_COPY_AND_ASSIGN(PositionChangedMessageFilter);
     78 };
     79 
     80 void AddFilterToParentHwndSubclass(HWND hwnd, ui::HWNDMessageFilter* filter) {
     81   HWND parent = ::GetAncestor(hwnd, GA_ROOT);
     82   if (parent) {
     83     ui::HWNDSubclass::RemoveFilterFromAllTargets(filter);
     84     ui::HWNDSubclass::AddFilterToTarget(parent, filter);
     85   }
     86 }
     87 
     88 }  // namespace namespace
     89 
     90 WebContentsViewWin::WebContentsViewWin(WebContentsImpl* web_contents,
     91                                        WebContentsViewDelegate* delegate)
     92     : web_contents_(web_contents),
     93       delegate_(delegate),
     94       hwnd_message_filter_(new PositionChangedMessageFilter) {
     95 }
     96 
     97 WebContentsViewWin::~WebContentsViewWin() {
     98   RemoveHwndToWcvMapEntry(this);
     99 
    100   if (IsWindow(hwnd()))
    101     DestroyWindow(hwnd());
    102 }
    103 
    104 gfx::NativeView WebContentsViewWin::GetNativeView() const {
    105   return hwnd();
    106 }
    107 
    108 gfx::NativeView WebContentsViewWin::GetContentNativeView() const {
    109   RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
    110   return rwhv ? rwhv->GetNativeView() : NULL;
    111 }
    112 
    113 gfx::NativeWindow WebContentsViewWin::GetTopLevelNativeWindow() const {
    114   return ::GetAncestor(GetNativeView(), GA_ROOT);
    115 }
    116 
    117 void WebContentsViewWin::GetContainerBounds(gfx::Rect *out) const {
    118   // Copied from NativeWidgetWin::GetClientAreaScreenBounds().
    119   RECT r;
    120   GetClientRect(hwnd(), &r);
    121   POINT point = { r.left, r.top };
    122   ClientToScreen(hwnd(), &point);
    123   *out = gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top);
    124 }
    125 
    126 void WebContentsViewWin::OnTabCrashed(base::TerminationStatus status,
    127                                       int error_code) {
    128 }
    129 
    130 void WebContentsViewWin::SizeContents(const gfx::Size& size) {
    131   gfx::Rect bounds;
    132   GetContainerBounds(&bounds);
    133   if (bounds.size() != size) {
    134     SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(),
    135                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
    136   } else {
    137     // Our size matches what we want but the renderers size may not match.
    138     // Pretend we were resized so that the renderers size is updated too.
    139     if (web_contents_->GetInterstitialPage())
    140       web_contents_->GetInterstitialPage()->SetSize(size);
    141     RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
    142     if (rwhv)
    143       rwhv->SetSize(size);
    144   }
    145 }
    146 
    147 void WebContentsViewWin::CreateView(
    148     const gfx::Size& initial_size, gfx::NativeView context) {
    149   initial_size_ = initial_size;
    150 
    151   set_window_style(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
    152 
    153   Init(ui::GetHiddenWindow(), gfx::Rect(initial_size_));
    154 
    155   // Remove the root view drop target so we can register our own.
    156   RevokeDragDrop(GetNativeView());
    157   drag_dest_ = new WebDragDest(hwnd(), web_contents_);
    158   if (delegate_) {
    159     WebDragDestDelegate* delegate = delegate_->GetDragDestDelegate();
    160     if (delegate)
    161       drag_dest_->set_delegate(delegate);
    162   }
    163 }
    164 
    165 void WebContentsViewWin::Focus() {
    166   if (web_contents_->GetInterstitialPage()) {
    167     web_contents_->GetInterstitialPage()->Focus();
    168     return;
    169   }
    170 
    171   if (delegate_.get() && delegate_->Focus())
    172     return;
    173 
    174   RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
    175   if (rwhv)
    176     rwhv->Focus();
    177 }
    178 
    179 void WebContentsViewWin::SetInitialFocus() {
    180   if (web_contents_->FocusLocationBarByDefault())
    181     web_contents_->SetFocusToLocationBar(false);
    182   else
    183     Focus();
    184 }
    185 
    186 void WebContentsViewWin::StoreFocus() {
    187   if (delegate_)
    188     delegate_->StoreFocus();
    189 }
    190 
    191 void WebContentsViewWin::RestoreFocus() {
    192   if (delegate_)
    193     delegate_->RestoreFocus();
    194 }
    195 
    196 DropData* WebContentsViewWin::GetDropData() const {
    197   return drag_dest_->current_drop_data();
    198 }
    199 
    200 gfx::Rect WebContentsViewWin::GetViewBounds() const {
    201   RECT r;
    202   GetWindowRect(hwnd(), &r);
    203   return gfx::Rect(r);
    204 }
    205 
    206 RenderWidgetHostView* WebContentsViewWin::CreateViewForWidget(
    207     RenderWidgetHost* render_widget_host)  {
    208   if (render_widget_host->GetView()) {
    209     // During testing, the view will already be set up in most cases to the
    210     // test view, so we don't want to clobber it with a real one. To verify that
    211     // this actually is happening (and somebody isn't accidentally creating the
    212     // view twice), we check for the RVH Factory, which will be set when we're
    213     // making special ones (which go along with the special views).
    214     DCHECK(RenderViewHostFactory::has_factory());
    215     return render_widget_host->GetView();
    216   }
    217 
    218   RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(
    219       RenderWidgetHostView::CreateViewForWidget(render_widget_host));
    220   view->CreateWnd(GetNativeView());
    221   view->ShowWindow(SW_SHOW);
    222   view->SetSize(initial_size_);
    223   return view;
    224 }
    225 
    226 RenderWidgetHostView* WebContentsViewWin::CreateViewForPopupWidget(
    227     RenderWidgetHost* render_widget_host) {
    228   return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host);
    229 }
    230 
    231 void WebContentsViewWin::SetPageTitle(const string16& title) {
    232   // It's possible to get this after the hwnd has been destroyed.
    233   if (GetNativeView())
    234     ::SetWindowText(GetNativeView(), title.c_str());
    235 }
    236 
    237 void WebContentsViewWin::RenderViewCreated(RenderViewHost* host) {
    238 }
    239 
    240 void WebContentsViewWin::RenderViewSwappedIn(RenderViewHost* host) {
    241 }
    242 
    243 void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled) {
    244 }
    245 
    246 void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) {
    247   if (delegate_)
    248     delegate_->ShowContextMenu(params);
    249 }
    250 
    251 void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds,
    252                                        int item_height,
    253                                        double item_font_size,
    254                                        int selected_item,
    255                                        const std::vector<MenuItem>& items,
    256                                        bool right_aligned,
    257                                        bool allow_multiple_selection) {
    258   // External popup menus are only used on Mac and Android.
    259   NOTIMPLEMENTED();
    260 }
    261 
    262 void WebContentsViewWin::StartDragging(const DropData& drop_data,
    263                                        WebKit::WebDragOperationsMask operations,
    264                                        const gfx::ImageSkia& image,
    265                                        const gfx::Vector2d& image_offset,
    266                                        const DragEventSourceInfo& event_info) {
    267   drag_handler_ = new WebContentsDragWin(
    268       GetNativeView(),
    269       web_contents_,
    270       drag_dest_,
    271       base::Bind(&WebContentsViewWin::EndDragging, base::Unretained(this)));
    272   drag_handler_->StartDragging(drop_data, operations, image, image_offset);
    273 }
    274 
    275 void WebContentsViewWin::UpdateDragCursor(WebKit::WebDragOperation operation) {
    276   drag_dest_->set_drag_cursor(operation);
    277 }
    278 
    279 void WebContentsViewWin::GotFocus() {
    280   if (web_contents_->GetDelegate())
    281     web_contents_->GetDelegate()->WebContentsFocused(web_contents_);
    282 }
    283 
    284 void WebContentsViewWin::TakeFocus(bool reverse) {
    285   if (web_contents_->GetDelegate() &&
    286       !web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) &&
    287       delegate_.get()) {
    288     delegate_->TakeFocus(reverse);
    289   }
    290 }
    291 
    292 void WebContentsViewWin::EndDragging() {
    293   drag_handler_ = NULL;
    294   web_contents_->SystemDragEnded();
    295 }
    296 
    297 void WebContentsViewWin::CloseTab() {
    298   RenderViewHost* rvh = web_contents_->GetRenderViewHost();
    299   rvh->GetDelegate()->Close(rvh);
    300 }
    301 
    302 LRESULT WebContentsViewWin::OnCreate(
    303     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    304   hwnd_to_wcv_map.insert(std::make_pair(hwnd(), this));
    305   AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get());
    306   return 0;
    307 }
    308 
    309 LRESULT WebContentsViewWin::OnDestroy(
    310     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    311   if (drag_dest_) {
    312     RevokeDragDrop(GetNativeView());
    313     drag_dest_ = NULL;
    314   }
    315   if (drag_handler_) {
    316     drag_handler_->CancelDrag();
    317     drag_handler_ = NULL;
    318   }
    319   return 0;
    320 }
    321 
    322 LRESULT WebContentsViewWin::OnWindowPosChanged(
    323     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    324 
    325   // Our parent might have changed. So we re-install our hwnd message filter.
    326   AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get());
    327 
    328   WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(lparam);
    329   if (window_pos->flags & SWP_HIDEWINDOW) {
    330     web_contents_->WasHidden();
    331     return 0;
    332   }
    333 
    334   // The WebContents was shown by a means other than the user selecting a
    335   // Tab, e.g. the window was minimized then restored.
    336   if (window_pos->flags & SWP_SHOWWINDOW)
    337     web_contents_->WasShown();
    338 
    339   RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
    340   if (rwhv) {
    341     RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(rwhv);
    342     view->UpdateScreenInfo(view->GetNativeView());
    343   }
    344 
    345   // Unless we were specifically told not to size, cause the renderer to be
    346   // sized to the new bounds, which forces a repaint. Not required for the
    347   // simple minimize-restore case described above, for example, since the
    348   // size hasn't changed.
    349   if (window_pos->flags & SWP_NOSIZE)
    350     return 0;
    351 
    352   gfx::Size size(window_pos->cx, window_pos->cy);
    353   if (web_contents_->GetInterstitialPage())
    354     web_contents_->GetInterstitialPage()->SetSize(size);
    355   if (rwhv)
    356     rwhv->SetSize(size);
    357 
    358   if (delegate_)
    359     delegate_->SizeChanged(size);
    360 
    361   return 0;
    362 }
    363 
    364 LRESULT WebContentsViewWin::OnMouseDown(
    365     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    366   // Make sure this WebContents is activated when it is clicked on.
    367   if (web_contents_->GetDelegate())
    368     web_contents_->GetDelegate()->ActivateContents(web_contents_);
    369   return 0;
    370 }
    371 
    372 LRESULT WebContentsViewWin::OnMouseMove(
    373     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    374   // Let our delegate know that the mouse moved (useful for resetting status
    375   // bubble state).
    376   if (web_contents_->GetDelegate()) {
    377     web_contents_->GetDelegate()->ContentsMouseEvent(
    378         web_contents_,
    379         gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
    380         true);
    381   }
    382   return 0;
    383 }
    384 
    385 LRESULT WebContentsViewWin::OnNCCalcSize(
    386     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    387   // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars
    388   // to receive scroll messages from ThinkPad touch-pad driver. Suppress
    389   // painting of scrollbars by returning 0 size for them.
    390   return 0;
    391 }
    392 
    393 LRESULT WebContentsViewWin::OnNCHitTest(
    394     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    395   return HTTRANSPARENT;
    396 }
    397 
    398 LRESULT WebContentsViewWin::OnScroll(
    399     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    400   int scroll_type = LOWORD(wparam);
    401   short position = HIWORD(wparam);
    402   HWND scrollbar = reinterpret_cast<HWND>(lparam);
    403   // This window can receive scroll events as a result of the ThinkPad's
    404   // touch-pad scroll wheel emulation.
    405   // If ctrl is held, zoom the UI.  There are three issues with this:
    406   // 1) Should the event be eaten or forwarded to content?  We eat the event,
    407   //    which is like Firefox and unlike IE.
    408   // 2) Should wheel up zoom in or out?  We zoom in (increase font size), which
    409   //    is like IE and Google maps, but unlike Firefox.
    410   // 3) Should the mouse have to be over the content area?  We zoom as long as
    411   //    content has focus, although FF and IE require that the mouse is over
    412   //    content.  This is because all events get forwarded when content has
    413   //    focus.
    414   if (GetAsyncKeyState(VK_CONTROL) & 0x8000) {
    415     int distance = 0;
    416     switch (scroll_type) {
    417       case SB_LINEUP:
    418         distance = WHEEL_DELTA;
    419         break;
    420       case SB_LINEDOWN:
    421         distance = -WHEEL_DELTA;
    422         break;
    423         // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION,
    424         // and SB_THUMBTRACK for completeness
    425       default:
    426         break;
    427     }
    428 
    429     web_contents_->GetDelegate()->ContentsZoomChange(distance > 0);
    430     return 0;
    431   }
    432 
    433   // Reflect scroll message to the view() to give it a chance
    434   // to process scrolling.
    435   SendMessage(GetContentNativeView(), message, wparam, lparam);
    436   return 0;
    437 }
    438 
    439 LRESULT WebContentsViewWin::OnSize(
    440     UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) {
    441   // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc,
    442   // OnSize is NOT called on window resize. This handler is called only once
    443   // when the window is created.
    444   // Don't call base class OnSize to avoid useless layout for 0x0 size.
    445   // We will get OnWindowPosChanged later and layout root view in WasSized.
    446 
    447   // Hack for ThinkPad touch-pad driver.
    448   // Set fake scrollbars so that we can get scroll messages,
    449   SCROLLINFO si = {0};
    450   si.cbSize = sizeof(si);
    451   si.fMask = SIF_ALL;
    452 
    453   si.nMin = 1;
    454   si.nMax = 100;
    455   si.nPage = 10;
    456   si.nPos = 50;
    457 
    458   ::SetScrollInfo(hwnd(), SB_HORZ, &si, FALSE);
    459   ::SetScrollInfo(hwnd(), SB_VERT, &si, FALSE);
    460 
    461   return 1;
    462 }
    463 
    464 }  // namespace content
    465