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/frame_host/interstitial_page_impl.h" 10 #include "content/browser/renderer_host/render_view_host_factory.h" 11 #include "content/browser/renderer_host/render_view_host_impl.h" 12 #include "content/browser/renderer_host/render_widget_host_view_win.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 base::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 // WARNING: this may have been deleted. 250 } 251 252 void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds, 253 int item_height, 254 double item_font_size, 255 int selected_item, 256 const std::vector<MenuItem>& items, 257 bool right_aligned, 258 bool allow_multiple_selection) { 259 // External popup menus are only used on Mac and Android. 260 NOTIMPLEMENTED(); 261 } 262 263 void WebContentsViewWin::StartDragging(const DropData& drop_data, 264 blink::WebDragOperationsMask operations, 265 const gfx::ImageSkia& image, 266 const gfx::Vector2d& image_offset, 267 const DragEventSourceInfo& event_info) { 268 drag_handler_ = new WebContentsDragWin( 269 GetNativeView(), 270 web_contents_, 271 drag_dest_, 272 base::Bind(&WebContentsViewWin::EndDragging, base::Unretained(this))); 273 drag_handler_->StartDragging(drop_data, operations, image, image_offset); 274 } 275 276 void WebContentsViewWin::UpdateDragCursor(blink::WebDragOperation operation) { 277 drag_dest_->set_drag_cursor(operation); 278 } 279 280 void WebContentsViewWin::GotFocus() { 281 if (web_contents_->GetDelegate()) 282 web_contents_->GetDelegate()->WebContentsFocused(web_contents_); 283 } 284 285 void WebContentsViewWin::TakeFocus(bool reverse) { 286 if (web_contents_->GetDelegate() && 287 !web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) && 288 delegate_.get()) { 289 delegate_->TakeFocus(reverse); 290 } 291 } 292 293 void WebContentsViewWin::EndDragging() { 294 drag_handler_ = NULL; 295 web_contents_->SystemDragEnded(); 296 } 297 298 void WebContentsViewWin::CloseTab() { 299 RenderViewHost* rvh = web_contents_->GetRenderViewHost(); 300 rvh->GetDelegate()->Close(rvh); 301 } 302 303 LRESULT WebContentsViewWin::OnCreate( 304 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 305 hwnd_to_wcv_map.insert(std::make_pair(hwnd(), this)); 306 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get()); 307 return 0; 308 } 309 310 LRESULT WebContentsViewWin::OnDestroy( 311 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 312 if (drag_dest_) { 313 RevokeDragDrop(GetNativeView()); 314 drag_dest_ = NULL; 315 } 316 if (drag_handler_) { 317 drag_handler_->CancelDrag(); 318 drag_handler_ = NULL; 319 } 320 return 0; 321 } 322 323 LRESULT WebContentsViewWin::OnWindowPosChanged( 324 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 325 326 // Our parent might have changed. So we re-install our hwnd message filter. 327 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get()); 328 329 WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(lparam); 330 if (window_pos->flags & SWP_HIDEWINDOW) { 331 web_contents_->WasHidden(); 332 return 0; 333 } 334 335 // The WebContents was shown by a means other than the user selecting a 336 // Tab, e.g. the window was minimized then restored. 337 if (window_pos->flags & SWP_SHOWWINDOW) 338 web_contents_->WasShown(); 339 340 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); 341 if (rwhv) { 342 RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(rwhv); 343 view->UpdateScreenInfo(view->GetNativeView()); 344 } 345 346 // Unless we were specifically told not to size, cause the renderer to be 347 // sized to the new bounds, which forces a repaint. Not required for the 348 // simple minimize-restore case described above, for example, since the 349 // size hasn't changed. 350 if (window_pos->flags & SWP_NOSIZE) 351 return 0; 352 353 gfx::Size size(window_pos->cx, window_pos->cy); 354 if (web_contents_->GetInterstitialPage()) 355 web_contents_->GetInterstitialPage()->SetSize(size); 356 if (rwhv) 357 rwhv->SetSize(size); 358 359 if (delegate_) 360 delegate_->SizeChanged(size); 361 362 return 0; 363 } 364 365 LRESULT WebContentsViewWin::OnMouseDown( 366 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 367 // Make sure this WebContents is activated when it is clicked on. 368 if (web_contents_->GetDelegate()) 369 web_contents_->GetDelegate()->ActivateContents(web_contents_); 370 return 0; 371 } 372 373 LRESULT WebContentsViewWin::OnMouseMove( 374 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 375 // Let our delegate know that the mouse moved (useful for resetting status 376 // bubble state). 377 if (web_contents_->GetDelegate()) { 378 web_contents_->GetDelegate()->ContentsMouseEvent( 379 web_contents_, 380 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(), 381 true); 382 } 383 return 0; 384 } 385 386 LRESULT WebContentsViewWin::OnNCCalcSize( 387 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 388 // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars 389 // to receive scroll messages from ThinkPad touch-pad driver. Suppress 390 // painting of scrollbars by returning 0 size for them. 391 return 0; 392 } 393 394 LRESULT WebContentsViewWin::OnNCHitTest( 395 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 396 return HTTRANSPARENT; 397 } 398 399 LRESULT WebContentsViewWin::OnScroll( 400 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 401 int scroll_type = LOWORD(wparam); 402 short position = HIWORD(wparam); 403 HWND scrollbar = reinterpret_cast<HWND>(lparam); 404 // This window can receive scroll events as a result of the ThinkPad's 405 // touch-pad scroll wheel emulation. 406 // If ctrl is held, zoom the UI. There are three issues with this: 407 // 1) Should the event be eaten or forwarded to content? We eat the event, 408 // which is like Firefox and unlike IE. 409 // 2) Should wheel up zoom in or out? We zoom in (increase font size), which 410 // is like IE and Google maps, but unlike Firefox. 411 // 3) Should the mouse have to be over the content area? We zoom as long as 412 // content has focus, although FF and IE require that the mouse is over 413 // content. This is because all events get forwarded when content has 414 // focus. 415 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { 416 int distance = 0; 417 switch (scroll_type) { 418 case SB_LINEUP: 419 distance = WHEEL_DELTA; 420 break; 421 case SB_LINEDOWN: 422 distance = -WHEEL_DELTA; 423 break; 424 // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION, 425 // and SB_THUMBTRACK for completeness 426 default: 427 break; 428 } 429 430 web_contents_->GetDelegate()->ContentsZoomChange(distance > 0); 431 return 0; 432 } 433 434 // Reflect scroll message to the view() to give it a chance 435 // to process scrolling. 436 SendMessage(GetContentNativeView(), message, wparam, lparam); 437 return 0; 438 } 439 440 LRESULT WebContentsViewWin::OnSize( 441 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { 442 // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc, 443 // OnSize is NOT called on window resize. This handler is called only once 444 // when the window is created. 445 // Don't call base class OnSize to avoid useless layout for 0x0 size. 446 // We will get OnWindowPosChanged later and layout root view in WasSized. 447 448 // Hack for ThinkPad touch-pad driver. 449 // Set fake scrollbars so that we can get scroll messages, 450 SCROLLINFO si = {0}; 451 si.cbSize = sizeof(si); 452 si.fMask = SIF_ALL; 453 454 si.nMin = 1; 455 si.nMax = 100; 456 si.nPage = 10; 457 si.nPos = 50; 458 459 ::SetScrollInfo(hwnd(), SB_HORZ, &si, FALSE); 460 ::SetScrollInfo(hwnd(), SB_VERT, &si, FALSE); 461 462 return 1; 463 } 464 465 } // namespace content 466