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 22namespace content { 23WebContentsViewPort* 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 32namespace { 33 34typedef std::map<HWND, WebContentsViewWin*> HwndToWcvMap; 35HwndToWcvMap hwnd_to_wcv_map; 36 37void 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 47BOOL 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 60class 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 80void 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 90WebContentsViewWin::WebContentsViewWin(WebContentsImpl* web_contents, 91 WebContentsViewDelegate* delegate) 92 : web_contents_(web_contents), 93 delegate_(delegate), 94 hwnd_message_filter_(new PositionChangedMessageFilter) { 95} 96 97WebContentsViewWin::~WebContentsViewWin() { 98 RemoveHwndToWcvMapEntry(this); 99 100 if (IsWindow(hwnd())) 101 DestroyWindow(hwnd()); 102} 103 104gfx::NativeView WebContentsViewWin::GetNativeView() const { 105 return hwnd(); 106} 107 108gfx::NativeView WebContentsViewWin::GetContentNativeView() const { 109 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); 110 return rwhv ? rwhv->GetNativeView() : NULL; 111} 112 113gfx::NativeWindow WebContentsViewWin::GetTopLevelNativeWindow() const { 114 return ::GetAncestor(GetNativeView(), GA_ROOT); 115} 116 117void 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 126void WebContentsViewWin::OnTabCrashed(base::TerminationStatus status, 127 int error_code) { 128} 129 130void 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 147void 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 165void 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 179void WebContentsViewWin::SetInitialFocus() { 180 if (web_contents_->FocusLocationBarByDefault()) 181 web_contents_->SetFocusToLocationBar(false); 182 else 183 Focus(); 184} 185 186void WebContentsViewWin::StoreFocus() { 187 if (delegate_) 188 delegate_->StoreFocus(); 189} 190 191void WebContentsViewWin::RestoreFocus() { 192 if (delegate_) 193 delegate_->RestoreFocus(); 194} 195 196DropData* WebContentsViewWin::GetDropData() const { 197 return drag_dest_->current_drop_data(); 198} 199 200gfx::Rect WebContentsViewWin::GetViewBounds() const { 201 RECT r; 202 GetWindowRect(hwnd(), &r); 203 return gfx::Rect(r); 204} 205 206RenderWidgetHostView* 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 226RenderWidgetHostView* WebContentsViewWin::CreateViewForPopupWidget( 227 RenderWidgetHost* render_widget_host) { 228 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host); 229} 230 231void 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 237void WebContentsViewWin::RenderViewCreated(RenderViewHost* host) { 238} 239 240void WebContentsViewWin::RenderViewSwappedIn(RenderViewHost* host) { 241} 242 243void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled) { 244} 245 246void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) { 247 if (delegate_) 248 delegate_->ShowContextMenu(params); 249} 250 251void 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 262void 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 275void WebContentsViewWin::UpdateDragCursor(WebKit::WebDragOperation operation) { 276 drag_dest_->set_drag_cursor(operation); 277} 278 279void WebContentsViewWin::GotFocus() { 280 if (web_contents_->GetDelegate()) 281 web_contents_->GetDelegate()->WebContentsFocused(web_contents_); 282} 283 284void 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 292void WebContentsViewWin::EndDragging() { 293 drag_handler_ = NULL; 294 web_contents_->SystemDragEnded(); 295} 296 297void WebContentsViewWin::CloseTab() { 298 RenderViewHost* rvh = web_contents_->GetRenderViewHost(); 299 rvh->GetDelegate()->Close(rvh); 300} 301 302LRESULT 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 309LRESULT 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 322LRESULT 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 364LRESULT 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 372LRESULT 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 385LRESULT 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 393LRESULT WebContentsViewWin::OnNCHitTest( 394 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 395 return HTTRANSPARENT; 396} 397 398LRESULT 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 439LRESULT 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