| OLD | NEW |
| (Empty) |
| 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/tab_contents/tab_contents_view_win.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "content/browser/renderer_host/render_view_host_factory.h" | |
| 9 #include "content/browser/renderer_host/render_view_host_impl.h" | |
| 10 #include "content/browser/renderer_host/render_widget_host_view_win.h" | |
| 11 #include "content/browser/tab_contents/tab_contents.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_drag_dest_win.h" | |
| 15 #include "content/public/browser/web_contents_delegate.h" | |
| 16 #include "content/public/browser/web_contents_view_delegate.h" | |
| 17 #include "ui/gfx/screen.h" | |
| 18 | |
| 19 using content::RenderViewHost; | |
| 20 using content::RenderWidgetHostView; | |
| 21 using content::WebContents; | |
| 22 using content::WebContentsViewDelegate; | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // We need to have a parent window for the compositing code to work correctly. | |
| 27 // | |
| 28 // A tab will not have a parent HWND whenever it is not active in its | |
| 29 // host window - for example at creation time and when it's in the | |
| 30 // background, so we provide a default widget to host them. | |
| 31 // | |
| 32 // It may be tempting to use GetDesktopWindow() instead, but this is | |
| 33 // problematic as the shell sends messages to children of the desktop | |
| 34 // window that interact poorly with us. | |
| 35 // | |
| 36 // See: http://crbug.com/16476 | |
| 37 class TempParent : public ui::WindowImpl { | |
| 38 public: | |
| 39 static TempParent* Get() { | |
| 40 static TempParent* g_temp_parent; | |
| 41 if (!g_temp_parent) { | |
| 42 g_temp_parent = new TempParent(); | |
| 43 | |
| 44 g_temp_parent->set_window_style(WS_POPUP); | |
| 45 g_temp_parent->set_window_ex_style(WS_EX_TOOLWINDOW); | |
| 46 g_temp_parent->Init(GetDesktopWindow(), gfx::Rect()); | |
| 47 EnableWindow(g_temp_parent->hwnd(), FALSE); | |
| 48 } | |
| 49 return g_temp_parent; | |
| 50 } | |
| 51 | |
| 52 private: | |
| 53 // Explicitly do nothing in Close. We do this as some external apps may get a | |
| 54 // handle to this window and attempt to close it. | |
| 55 void OnClose() { | |
| 56 } | |
| 57 | |
| 58 BEGIN_MSG_MAP_EX(TabContentsViewWin) | |
| 59 MSG_WM_CLOSE(OnClose) | |
| 60 END_MSG_MAP() | |
| 61 }; | |
| 62 | |
| 63 } // namespace namespace | |
| 64 | |
| 65 TabContentsViewWin::TabContentsViewWin(TabContents* tab_contents, | |
| 66 WebContentsViewDelegate* delegate) | |
| 67 : tab_contents_(tab_contents), | |
| 68 view_(NULL), | |
| 69 delegate_(delegate), | |
| 70 close_tab_after_drag_ends_(false) { | |
| 71 } | |
| 72 | |
| 73 TabContentsViewWin::~TabContentsViewWin() { | |
| 74 if (IsWindow(hwnd())) | |
| 75 DestroyWindow(hwnd()); | |
| 76 } | |
| 77 | |
| 78 void TabContentsViewWin::CreateView(const gfx::Size& initial_size) { | |
| 79 initial_size_ = initial_size; | |
| 80 | |
| 81 set_window_style(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); | |
| 82 | |
| 83 Init(TempParent::Get()->hwnd(), gfx::Rect(initial_size_)); | |
| 84 | |
| 85 // Remove the root view drop target so we can register our own. | |
| 86 RevokeDragDrop(GetNativeView()); | |
| 87 drag_dest_ = new WebDragDest(hwnd(), tab_contents_); | |
| 88 if (delegate_.get()) { | |
| 89 content::WebDragDestDelegate* delegate = delegate_->GetDragDestDelegate(); | |
| 90 if (delegate) | |
| 91 drag_dest_->set_delegate(delegate); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 RenderWidgetHostView* TabContentsViewWin::CreateViewForWidget( | |
| 96 content::RenderWidgetHost* render_widget_host) { | |
| 97 if (render_widget_host->GetView()) { | |
| 98 // During testing, the view will already be set up in most cases to the | |
| 99 // test view, so we don't want to clobber it with a real one. To verify that | |
| 100 // this actually is happening (and somebody isn't accidentally creating the | |
| 101 // view twice), we check for the RVH Factory, which will be set when we're | |
| 102 // making special ones (which go along with the special views). | |
| 103 DCHECK(RenderViewHostFactory::has_factory()); | |
| 104 return render_widget_host->GetView(); | |
| 105 } | |
| 106 | |
| 107 view_ = static_cast<RenderWidgetHostViewWin*>( | |
| 108 RenderWidgetHostView::CreateViewForWidget(render_widget_host)); | |
| 109 view_->CreateWnd(GetNativeView()); | |
| 110 view_->ShowWindow(SW_SHOW); | |
| 111 view_->SetSize(initial_size_); | |
| 112 return view_; | |
| 113 } | |
| 114 | |
| 115 gfx::NativeView TabContentsViewWin::GetNativeView() const { | |
| 116 return hwnd(); | |
| 117 } | |
| 118 | |
| 119 gfx::NativeView TabContentsViewWin::GetContentNativeView() const { | |
| 120 RenderWidgetHostView* rwhv = tab_contents_->GetRenderWidgetHostView(); | |
| 121 return rwhv ? rwhv->GetNativeView() : NULL; | |
| 122 } | |
| 123 | |
| 124 gfx::NativeWindow TabContentsViewWin::GetTopLevelNativeWindow() const { | |
| 125 return GetParent(GetNativeView()); | |
| 126 } | |
| 127 | |
| 128 void TabContentsViewWin::GetContainerBounds(gfx::Rect *out) const { | |
| 129 // Copied from NativeWidgetWin::GetClientAreaScreenBounds(). | |
| 130 RECT r; | |
| 131 GetClientRect(hwnd(), &r); | |
| 132 POINT point = { r.left, r.top }; | |
| 133 ClientToScreen(hwnd(), &point); | |
| 134 *out = gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); | |
| 135 } | |
| 136 | |
| 137 void TabContentsViewWin::SetPageTitle(const string16& title) { | |
| 138 // It's possible to get this after the hwnd has been destroyed. | |
| 139 if (GetNativeView()) | |
| 140 ::SetWindowText(GetNativeView(), title.c_str()); | |
| 141 } | |
| 142 | |
| 143 void TabContentsViewWin::OnTabCrashed(base::TerminationStatus status, | |
| 144 int error_code) { | |
| 145 // TODO(avi): No other TCV implementation does anything in this callback. Can | |
| 146 // this be moved elsewhere so that |OnTabCrashed| can be removed everywhere? | |
| 147 view_ = NULL; | |
| 148 } | |
| 149 | |
| 150 void TabContentsViewWin::SizeContents(const gfx::Size& size) { | |
| 151 gfx::Rect bounds; | |
| 152 GetContainerBounds(&bounds); | |
| 153 if (bounds.size() != size) { | |
| 154 SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(), | |
| 155 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); | |
| 156 } else { | |
| 157 // Our size matches what we want but the renderers size may not match. | |
| 158 // Pretend we were resized so that the renderers size is updated too. | |
| 159 if (tab_contents_->GetInterstitialPage()) | |
| 160 tab_contents_->GetInterstitialPage()->SetSize(size); | |
| 161 RenderWidgetHostView* rwhv = tab_contents_->GetRenderWidgetHostView(); | |
| 162 if (rwhv) | |
| 163 rwhv->SetSize(size); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 void TabContentsViewWin::RenderViewCreated(RenderViewHost* host) { | |
| 168 } | |
| 169 | |
| 170 void TabContentsViewWin::Focus() { | |
| 171 if (tab_contents_->GetInterstitialPage()) { | |
| 172 tab_contents_->GetInterstitialPage()->Focus(); | |
| 173 return; | |
| 174 } | |
| 175 | |
| 176 if (delegate_.get() && delegate_->Focus()) | |
| 177 return; | |
| 178 | |
| 179 RenderWidgetHostView* rwhv = tab_contents_->GetRenderWidgetHostView(); | |
| 180 if (rwhv) | |
| 181 rwhv->Focus(); | |
| 182 } | |
| 183 | |
| 184 void TabContentsViewWin::SetInitialFocus() { | |
| 185 if (tab_contents_->FocusLocationBarByDefault()) | |
| 186 tab_contents_->SetFocusToLocationBar(false); | |
| 187 else | |
| 188 Focus(); | |
| 189 } | |
| 190 | |
| 191 void TabContentsViewWin::StoreFocus() { | |
| 192 if (delegate_.get()) | |
| 193 delegate_->StoreFocus(); | |
| 194 } | |
| 195 | |
| 196 void TabContentsViewWin::RestoreFocus() { | |
| 197 if (delegate_.get()) | |
| 198 delegate_->RestoreFocus(); | |
| 199 } | |
| 200 | |
| 201 bool TabContentsViewWin::IsDoingDrag() const { | |
| 202 return drag_handler_.get() != NULL; | |
| 203 } | |
| 204 | |
| 205 void TabContentsViewWin::CancelDragAndCloseTab() { | |
| 206 DCHECK(IsDoingDrag()); | |
| 207 // We can't close the tab while we're in the drag and | |
| 208 // |drag_handler_->CancelDrag()| is async. Instead, set a flag to cancel | |
| 209 // the drag and when the drag nested message loop ends, close the tab. | |
| 210 drag_handler_->CancelDrag(); | |
| 211 close_tab_after_drag_ends_ = true; | |
| 212 } | |
| 213 | |
| 214 bool TabContentsViewWin::IsEventTracking() const { | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 void TabContentsViewWin::CloseTabAfterEventTracking() { | |
| 219 } | |
| 220 | |
| 221 void TabContentsViewWin::GetViewBounds(gfx::Rect* out) const { | |
| 222 RECT r; | |
| 223 GetWindowRect(hwnd(), &r); | |
| 224 *out = gfx::Rect(r); | |
| 225 } | |
| 226 | |
| 227 void TabContentsViewWin::CreateNewWindow( | |
| 228 int route_id, | |
| 229 const ViewHostMsg_CreateWindow_Params& params) { | |
| 230 tab_contents_view_helper_.CreateNewWindow(tab_contents_, route_id, params); | |
| 231 } | |
| 232 | |
| 233 void TabContentsViewWin::CreateNewWidget(int route_id, | |
| 234 WebKit::WebPopupType popup_type) { | |
| 235 tab_contents_view_helper_.CreateNewWidget(tab_contents_, | |
| 236 route_id, | |
| 237 false, | |
| 238 popup_type); | |
| 239 } | |
| 240 | |
| 241 void TabContentsViewWin::CreateNewFullscreenWidget(int route_id) { | |
| 242 tab_contents_view_helper_.CreateNewWidget(tab_contents_, | |
| 243 route_id, | |
| 244 true, | |
| 245 WebKit::WebPopupTypeNone); | |
| 246 } | |
| 247 | |
| 248 void TabContentsViewWin::ShowCreatedWindow(int route_id, | |
| 249 WindowOpenDisposition disposition, | |
| 250 const gfx::Rect& initial_pos, | |
| 251 bool user_gesture) { | |
| 252 tab_contents_view_helper_.ShowCreatedWindow( | |
| 253 tab_contents_, route_id, disposition, initial_pos, user_gesture); | |
| 254 } | |
| 255 | |
| 256 void TabContentsViewWin::ShowCreatedWidget(int route_id, | |
| 257 const gfx::Rect& initial_pos) { | |
| 258 tab_contents_view_helper_.ShowCreatedWidget(tab_contents_, | |
| 259 route_id, | |
| 260 false, | |
| 261 initial_pos); | |
| 262 } | |
| 263 | |
| 264 void TabContentsViewWin::ShowCreatedFullscreenWidget(int route_id) { | |
| 265 tab_contents_view_helper_.ShowCreatedWidget(tab_contents_, | |
| 266 route_id, | |
| 267 true, | |
| 268 gfx::Rect()); | |
| 269 } | |
| 270 | |
| 271 void TabContentsViewWin::ShowContextMenu( | |
| 272 const content::ContextMenuParams& params) { | |
| 273 // Allow WebContentsDelegates to handle the context menu operation first. | |
| 274 if (tab_contents_->GetDelegate() && | |
| 275 tab_contents_->GetDelegate()->HandleContextMenu(params)) { | |
| 276 return; | |
| 277 } | |
| 278 | |
| 279 if (delegate_.get()) | |
| 280 delegate_->ShowContextMenu(params); | |
| 281 } | |
| 282 | |
| 283 void TabContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds, | |
| 284 int item_height, | |
| 285 double item_font_size, | |
| 286 int selected_item, | |
| 287 const std::vector<WebMenuItem>& items, | |
| 288 bool right_aligned) { | |
| 289 // External popup menus are only used on Mac. | |
| 290 NOTIMPLEMENTED(); | |
| 291 } | |
| 292 | |
| 293 void TabContentsViewWin::StartDragging(const WebDropData& drop_data, | |
| 294 WebKit::WebDragOperationsMask operations, | |
| 295 const SkBitmap& image, | |
| 296 const gfx::Point& image_offset) { | |
| 297 drag_handler_ = new WebContentsDragWin( | |
| 298 GetNativeView(), | |
| 299 tab_contents_, | |
| 300 drag_dest_, | |
| 301 base::Bind(&TabContentsViewWin::EndDragging, base::Unretained(this))); | |
| 302 drag_handler_->StartDragging(drop_data, operations, image, image_offset); | |
| 303 } | |
| 304 | |
| 305 void TabContentsViewWin::UpdateDragCursor(WebKit::WebDragOperation operation) { | |
| 306 drag_dest_->set_drag_cursor(operation); | |
| 307 } | |
| 308 | |
| 309 void TabContentsViewWin::GotFocus() { | |
| 310 if (tab_contents_->GetDelegate()) | |
| 311 tab_contents_->GetDelegate()->WebContentsFocused(tab_contents_); | |
| 312 } | |
| 313 | |
| 314 void TabContentsViewWin::TakeFocus(bool reverse) { | |
| 315 if (tab_contents_->GetDelegate() && | |
| 316 !tab_contents_->GetDelegate()->TakeFocus(reverse) && | |
| 317 delegate_.get()) { | |
| 318 delegate_->TakeFocus(reverse); | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 void TabContentsViewWin::EndDragging() { | |
| 323 drag_handler_ = NULL; | |
| 324 if (close_tab_after_drag_ends_) { | |
| 325 close_tab_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(0), | |
| 326 this, &TabContentsViewWin::CloseTab); | |
| 327 } | |
| 328 tab_contents_->SystemDragEnded(); | |
| 329 } | |
| 330 | |
| 331 void TabContentsViewWin::CloseTab() { | |
| 332 RenderViewHost* rvh = tab_contents_->GetRenderViewHost(); | |
| 333 rvh->GetDelegate()->Close(rvh); | |
| 334 } | |
| 335 | |
| 336 LRESULT TabContentsViewWin::OnDestroy( | |
| 337 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 338 if (drag_dest_.get()) { | |
| 339 RevokeDragDrop(GetNativeView()); | |
| 340 drag_dest_ = NULL; | |
| 341 } | |
| 342 return 0; | |
| 343 } | |
| 344 | |
| 345 LRESULT TabContentsViewWin::OnWindowPosChanged( | |
| 346 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 347 WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(lparam); | |
| 348 if (window_pos->flags & SWP_HIDEWINDOW) { | |
| 349 tab_contents_->HideContents(); | |
| 350 return 0; | |
| 351 } | |
| 352 | |
| 353 // The TabContents was shown by a means other than the user selecting a | |
| 354 // Tab, e.g. the window was minimized then restored. | |
| 355 if (window_pos->flags & SWP_SHOWWINDOW) | |
| 356 tab_contents_->ShowContents(); | |
| 357 | |
| 358 // Unless we were specifically told not to size, cause the renderer to be | |
| 359 // sized to the new bounds, which forces a repaint. Not required for the | |
| 360 // simple minimize-restore case described above, for example, since the | |
| 361 // size hasn't changed. | |
| 362 if (window_pos->flags & SWP_NOSIZE) | |
| 363 return 0; | |
| 364 | |
| 365 gfx::Size size(window_pos->cx, window_pos->cy); | |
| 366 if (tab_contents_->GetInterstitialPage()) | |
| 367 tab_contents_->GetInterstitialPage()->SetSize(size); | |
| 368 RenderWidgetHostView* rwhv = tab_contents_->GetRenderWidgetHostView(); | |
| 369 if (rwhv) | |
| 370 rwhv->SetSize(size); | |
| 371 | |
| 372 if (delegate_.get()) | |
| 373 delegate_->SizeChanged(size); | |
| 374 | |
| 375 return 0; | |
| 376 } | |
| 377 | |
| 378 LRESULT TabContentsViewWin::OnMouseDown( | |
| 379 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 380 // Make sure this TabContents is activated when it is clicked on. | |
| 381 if (tab_contents_->GetDelegate()) | |
| 382 tab_contents_->GetDelegate()->ActivateContents(tab_contents_); | |
| 383 return 0; | |
| 384 } | |
| 385 | |
| 386 LRESULT TabContentsViewWin::OnMouseMove( | |
| 387 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 388 // Let our delegate know that the mouse moved (useful for resetting status | |
| 389 // bubble state). | |
| 390 if (tab_contents_->GetDelegate()) { | |
| 391 tab_contents_->GetDelegate()->ContentsMouseEvent( | |
| 392 tab_contents_, gfx::Screen::GetCursorScreenPoint(), true); | |
| 393 } | |
| 394 return 0; | |
| 395 } | |
| 396 | |
| 397 LRESULT TabContentsViewWin::OnReflectedMessage( | |
| 398 UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 399 MSG* message = reinterpret_cast<MSG*>(lparam); | |
| 400 switch (message->message) { | |
| 401 case WM_MOUSEWHEEL: | |
| 402 // This message is reflected from the view() to this window. | |
| 403 if (GET_KEYSTATE_WPARAM(message->wParam) & MK_CONTROL) { | |
| 404 tab_contents_->GetDelegate()->ContentsZoomChange( | |
| 405 GET_WHEEL_DELTA_WPARAM(message->wParam) > 0); | |
| 406 return 1; | |
| 407 } | |
| 408 break; | |
| 409 } | |
| 410 return 0; | |
| 411 } | |
| 412 | |
| 413 LRESULT TabContentsViewWin::OnNCCalcSize( | |
| 414 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 415 // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars | |
| 416 // to receive scroll messages from ThinkPad touch-pad driver. Suppress | |
| 417 // painting of scrollbars by returning 0 size for them. | |
| 418 return 0; | |
| 419 } | |
| 420 | |
| 421 LRESULT TabContentsViewWin::OnScroll( | |
| 422 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 423 int scroll_type = LOWORD(wparam); | |
| 424 short position = HIWORD(wparam); | |
| 425 HWND scrollbar = reinterpret_cast<HWND>(lparam); | |
| 426 // This window can receive scroll events as a result of the ThinkPad's | |
| 427 // touch-pad scroll wheel emulation. | |
| 428 // If ctrl is held, zoom the UI. There are three issues with this: | |
| 429 // 1) Should the event be eaten or forwarded to content? We eat the event, | |
| 430 // which is like Firefox and unlike IE. | |
| 431 // 2) Should wheel up zoom in or out? We zoom in (increase font size), which | |
| 432 // is like IE and Google maps, but unlike Firefox. | |
| 433 // 3) Should the mouse have to be over the content area? We zoom as long as | |
| 434 // content has focus, although FF and IE require that the mouse is over | |
| 435 // content. This is because all events get forwarded when content has | |
| 436 // focus. | |
| 437 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { | |
| 438 int distance = 0; | |
| 439 switch (scroll_type) { | |
| 440 case SB_LINEUP: | |
| 441 distance = WHEEL_DELTA; | |
| 442 break; | |
| 443 case SB_LINEDOWN: | |
| 444 distance = -WHEEL_DELTA; | |
| 445 break; | |
| 446 // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION, | |
| 447 // and SB_THUMBTRACK for completeness | |
| 448 default: | |
| 449 break; | |
| 450 } | |
| 451 | |
| 452 tab_contents_->GetDelegate()->ContentsZoomChange(distance > 0); | |
| 453 return 0; | |
| 454 } | |
| 455 | |
| 456 // Reflect scroll message to the view() to give it a chance | |
| 457 // to process scrolling. | |
| 458 SendMessage(GetContentNativeView(), message, wparam, lparam); | |
| 459 return 0; | |
| 460 } | |
| 461 | |
| 462 LRESULT TabContentsViewWin::OnSize( | |
| 463 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
| 464 // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc, | |
| 465 // OnSize is NOT called on window resize. This handler is called only once | |
| 466 // when the window is created. | |
| 467 // Don't call base class OnSize to avoid useless layout for 0x0 size. | |
| 468 // We will get OnWindowPosChanged later and layout root view in WasSized. | |
| 469 | |
| 470 // Hack for ThinkPad touch-pad driver. | |
| 471 // Set fake scrollbars so that we can get scroll messages, | |
| 472 SCROLLINFO si = {0}; | |
| 473 si.cbSize = sizeof(si); | |
| 474 si.fMask = SIF_ALL; | |
| 475 | |
| 476 si.nMin = 1; | |
| 477 si.nMax = 100; | |
| 478 si.nPage = 10; | |
| 479 si.nPos = 50; | |
| 480 | |
| 481 ::SetScrollInfo(hwnd(), SB_HORZ, &si, FALSE); | |
| 482 ::SetScrollInfo(hwnd(), SB_VERT, &si, FALSE); | |
| 483 | |
| 484 return 1; | |
| 485 } | |
| OLD | NEW |