1 // Copyright (c) 2011 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 "chrome/browser/ui/views/tab_contents/tab_contents_view_views.h" 6 7 #include <windows.h> 8 9 #include <vector> 10 11 #include "base/time.h" 12 #include "chrome/browser/ui/views/sad_tab_view.h" 13 #include "chrome/browser/ui/views/tab_contents/native_tab_contents_view.h" 14 #include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h" 15 #include "content/browser/renderer_host/render_process_host.h" 16 #include "content/browser/renderer_host/render_view_host.h" 17 #include "content/browser/renderer_host/render_view_host_factory.h" 18 #include "content/browser/renderer_host/render_widget_host_view.h" 19 #include "content/browser/tab_contents/interstitial_page.h" 20 #include "content/browser/tab_contents/tab_contents.h" 21 #include "content/browser/tab_contents/tab_contents_delegate.h" 22 #include "views/focus/focus_manager.h" 23 #include "views/focus/view_storage.h" 24 #include "views/screen.h" 25 #include "views/widget/native_widget.h" 26 #include "views/widget/root_view.h" 27 #include "views/widget/widget.h" 28 29 using WebKit::WebDragOperation; 30 using WebKit::WebDragOperationNone; 31 using WebKit::WebDragOperationsMask; 32 using WebKit::WebInputEvent; 33 34 // static 35 TabContentsView* TabContentsView::Create(TabContents* tab_contents) { 36 return new TabContentsViewViews(tab_contents); 37 } 38 39 TabContentsViewViews::TabContentsViewViews(TabContents* tab_contents) 40 : TabContentsView(tab_contents), 41 ALLOW_THIS_IN_INITIALIZER_LIST(native_tab_contents_view_( 42 NativeTabContentsView::CreateNativeTabContentsView(this))), 43 close_tab_after_drag_ends_(false), 44 sad_tab_(NULL) { 45 last_focused_view_storage_id_ = 46 views::ViewStorage::GetInstance()->CreateStorageID(); 47 } 48 49 TabContentsViewViews::~TabContentsViewViews() { 50 // Makes sure to remove any stored view we may still have in the ViewStorage. 51 // 52 // It is possible the view went away before us, so we only do this if the 53 // view is registered. 54 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 55 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL) 56 view_storage->RemoveView(last_focused_view_storage_id_); 57 } 58 59 void TabContentsViewViews::Unparent() { 60 CHECK(native_tab_contents_view_.get()); 61 native_tab_contents_view_->Unparent(); 62 } 63 64 void TabContentsViewViews::CreateView(const gfx::Size& initial_size) { 65 native_tab_contents_view_->InitNativeTabContentsView(); 66 } 67 68 RenderWidgetHostView* TabContentsViewViews::CreateViewForWidget( 69 RenderWidgetHost* render_widget_host) { 70 if (render_widget_host->view()) { 71 // During testing, the view will already be set up in most cases to the 72 // test view, so we don't want to clobber it with a real one. To verify that 73 // this actually is happening (and somebody isn't accidentally creating the 74 // view twice), we check for the RVH Factory, which will be set when we're 75 // making special ones (which go along with the special views). 76 DCHECK(RenderViewHostFactory::has_factory()); 77 return render_widget_host->view(); 78 } 79 80 // If we were showing sad tab, remove it now. 81 if (sad_tab_) { 82 GetWidget()->SetContentsView(new views::View()); 83 sad_tab_ = NULL; 84 } 85 86 return native_tab_contents_view_->CreateRenderWidgetHostView( 87 render_widget_host); 88 } 89 90 gfx::NativeView TabContentsViewViews::GetNativeView() const { 91 return GetWidget()->GetNativeView(); 92 } 93 94 gfx::NativeView TabContentsViewViews::GetContentNativeView() const { 95 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 96 return rwhv ? rwhv->GetNativeView() : NULL; 97 } 98 99 gfx::NativeWindow TabContentsViewViews::GetTopLevelNativeWindow() const { 100 return native_tab_contents_view_->GetTopLevelNativeWindow(); 101 } 102 103 void TabContentsViewViews::GetContainerBounds(gfx::Rect* out) const { 104 *out = GetWidget()->GetClientAreaScreenBounds(); 105 } 106 107 void TabContentsViewViews::StartDragging(const WebDropData& drop_data, 108 WebDragOperationsMask ops, 109 const SkBitmap& image, 110 const gfx::Point& image_offset) { 111 native_tab_contents_view_->StartDragging(drop_data, ops, image, image_offset); 112 } 113 114 void TabContentsViewViews::SetPageTitle(const std::wstring& title) { 115 native_tab_contents_view_->SetPageTitle(title); 116 } 117 118 void TabContentsViewViews::OnTabCrashed(base::TerminationStatus status, 119 int /* error_code */) { 120 // Force an invalidation to render sad tab. 121 // Note that it's possible to get this message after the window was destroyed. 122 if (::IsWindow(GetNativeView())) { 123 SadTabView::Kind kind = 124 status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ? 125 SadTabView::KILLED : SadTabView::CRASHED; 126 sad_tab_ = new SadTabView(tab_contents(), kind); 127 GetWidget()->SetContentsView(sad_tab_); 128 sad_tab_->SchedulePaint(); 129 } 130 } 131 132 void TabContentsViewViews::SizeContents(const gfx::Size& size) { 133 GetWidget()->SetSize(size); 134 } 135 136 void TabContentsViewViews::Focus() { 137 if (tab_contents()->interstitial_page()) { 138 tab_contents()->interstitial_page()->Focus(); 139 return; 140 } 141 142 if (tab_contents()->is_crashed() && sad_tab_ != NULL) { 143 sad_tab_->RequestFocus(); 144 return; 145 } 146 147 if (tab_contents()->constrained_window_count() > 0) { 148 ConstrainedWindow* window = *tab_contents()->constrained_window_begin(); 149 DCHECK(window); 150 window->FocusConstrainedWindow(); 151 return; 152 } 153 154 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 155 GetWidget()->GetFocusManager()->FocusNativeView(rwhv ? rwhv->GetNativeView() 156 : GetNativeView()); 157 } 158 159 void TabContentsViewViews::SetInitialFocus() { 160 if (tab_contents()->FocusLocationBarByDefault()) 161 tab_contents()->SetFocusToLocationBar(false); 162 else 163 Focus(); 164 } 165 166 void TabContentsViewViews::StoreFocus() { 167 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 168 169 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL) 170 view_storage->RemoveView(last_focused_view_storage_id_); 171 172 views::FocusManager* focus_manager = 173 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 174 if (focus_manager) { 175 // |focus_manager| can be NULL if the tab has been detached but still 176 // exists. 177 views::View* focused_view = focus_manager->GetFocusedView(); 178 if (focused_view) 179 view_storage->StoreView(last_focused_view_storage_id_, focused_view); 180 } 181 } 182 183 void TabContentsViewViews::RestoreFocus() { 184 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 185 views::View* last_focused_view = 186 view_storage->RetrieveView(last_focused_view_storage_id_); 187 188 if (!last_focused_view) { 189 SetInitialFocus(); 190 } else { 191 views::FocusManager* focus_manager = 192 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 193 194 // If you hit this DCHECK, please report it to Jay (jcampan). 195 DCHECK(focus_manager != NULL) << "No focus manager when restoring focus."; 196 197 if (last_focused_view->IsFocusableInRootView() && focus_manager && 198 focus_manager->ContainsView(last_focused_view)) { 199 last_focused_view->RequestFocus(); 200 } else { 201 // The focused view may not belong to the same window hierarchy (e.g. 202 // if the location bar was focused and the tab is dragged out), or it may 203 // no longer be focusable (e.g. if the location bar was focused and then 204 // we switched to fullscreen mode). In that case we default to the 205 // default focus. 206 SetInitialFocus(); 207 } 208 view_storage->RemoveView(last_focused_view_storage_id_); 209 } 210 } 211 212 bool TabContentsViewViews::IsDoingDrag() const { 213 return native_tab_contents_view_->IsDoingDrag(); 214 } 215 216 void TabContentsViewViews::CancelDragAndCloseTab() { 217 DCHECK(IsDoingDrag()); 218 // We can't close the tab while we're in the drag and 219 // |drag_handler_->CancelDrag()| is async. Instead, set a flag to cancel 220 // the drag and when the drag nested message loop ends, close the tab. 221 native_tab_contents_view_->CancelDrag(); 222 close_tab_after_drag_ends_ = true; 223 } 224 225 void TabContentsViewViews::GetViewBounds(gfx::Rect* out) const { 226 *out = GetWidget()->GetWindowScreenBounds(); 227 } 228 229 void TabContentsViewViews::UpdateDragCursor(WebDragOperation operation) { 230 native_tab_contents_view_->SetDragCursor(operation); 231 } 232 233 void TabContentsViewViews::GotFocus() { 234 if (tab_contents()->delegate()) 235 tab_contents()->delegate()->TabContentsFocused(tab_contents()); 236 } 237 238 void TabContentsViewViews::TakeFocus(bool reverse) { 239 if (!tab_contents()->delegate()->TakeFocus(reverse)) { 240 views::FocusManager* focus_manager = 241 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 242 243 // We may not have a focus manager if the tab has been switched before this 244 // message arrived. 245 if (focus_manager) 246 focus_manager->AdvanceFocus(reverse); 247 } 248 } 249 250 views::Widget* TabContentsViewViews::GetWidget() { 251 return native_tab_contents_view_->AsNativeWidget()->GetWidget(); 252 } 253 254 const views::Widget* TabContentsViewViews::GetWidget() const { 255 return native_tab_contents_view_->AsNativeWidget()->GetWidget(); 256 } 257 258 void TabContentsViewViews::CloseTab() { 259 tab_contents()->Close(tab_contents()->render_view_host()); 260 } 261 262 void TabContentsViewViews::ShowContextMenu(const ContextMenuParams& params) { 263 // Allow delegates to handle the context menu operation first. 264 if (tab_contents()->delegate()->HandleContextMenu(params)) 265 return; 266 267 context_menu_.reset(new RenderViewContextMenuViews(tab_contents(), params)); 268 context_menu_->Init(); 269 270 POINT screen_pt = { params.x, params.y }; 271 MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1); 272 273 // Enable recursive tasks on the message loop so we can get updates while 274 // the context menu is being displayed. 275 bool old_state = MessageLoop::current()->NestableTasksAllowed(); 276 MessageLoop::current()->SetNestableTasksAllowed(true); 277 context_menu_->RunMenuAt(screen_pt.x, screen_pt.y); 278 MessageLoop::current()->SetNestableTasksAllowed(old_state); 279 } 280 281 void TabContentsViewViews::ShowPopupMenu(const gfx::Rect& bounds, 282 int item_height, 283 double item_font_size, 284 int selected_item, 285 const std::vector<WebMenuItem>& items, 286 bool right_aligned) { 287 // External popup menus are only used on Mac. 288 NOTREACHED(); 289 } 290 291 //////////////////////////////////////////////////////////////////////////////// 292 // TabContentsViewViews, internal::NativeTabContentsViewDelegate implementation: 293 294 TabContents* TabContentsViewViews::GetTabContents() { 295 return tab_contents(); 296 } 297 298 bool TabContentsViewViews::IsShowingSadTab() const { 299 return tab_contents()->is_crashed() && sad_tab_; 300 } 301 302 void TabContentsViewViews::OnNativeTabContentsViewShown() { 303 tab_contents()->ShowContents(); 304 } 305 306 void TabContentsViewViews::OnNativeTabContentsViewHidden() { 307 tab_contents()->HideContents(); 308 } 309 310 void TabContentsViewViews::OnNativeTabContentsViewSized(const gfx::Size& size) { 311 if (tab_contents()->interstitial_page()) 312 tab_contents()->interstitial_page()->SetSize(size); 313 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 314 if (rwhv) 315 rwhv->SetSize(size); 316 } 317 318 void TabContentsViewViews::OnNativeTabContentsViewWheelZoom(int distance) { 319 if (tab_contents()->delegate()) { 320 bool zoom_in = distance > 0; 321 tab_contents()->delegate()->ContentsZoomChange(zoom_in); 322 } 323 } 324 325 void TabContentsViewViews::OnNativeTabContentsViewMouseDown() { 326 // Make sure this TabContents is activated when it is clicked on. 327 if (tab_contents()->delegate()) 328 tab_contents()->delegate()->ActivateContents(tab_contents()); 329 } 330 331 void TabContentsViewViews::OnNativeTabContentsViewMouseMove() { 332 // Let our delegate know that the mouse moved (useful for resetting status 333 // bubble state). 334 if (tab_contents()->delegate()) { 335 tab_contents()->delegate()->ContentsMouseEvent( 336 tab_contents(), views::Screen::GetCursorScreenPoint(), true); 337 } 338 } 339 340 void TabContentsViewViews::OnNativeTabContentsViewDraggingEnded() { 341 if (close_tab_after_drag_ends_) { 342 close_tab_timer_.Start(base::TimeDelta::FromMilliseconds(0), this, 343 &TabContentsViewViews::CloseTab); 344 } 345 tab_contents()->SystemDragEnded(); 346 } 347