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_touch.h" 6 7 #include "base/string_util.h" 8 #include "build/build_config.h" 9 #include "chrome/browser/download/download_shelf.h" 10 #include "chrome/browser/renderer_host/render_widget_host_view_views.h" 11 #include "chrome/browser/ui/views/sad_tab_view.h" 12 #include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h" 13 #include "content/browser/renderer_host/render_view_host.h" 14 #include "content/browser/renderer_host/render_view_host_factory.h" 15 #include "content/browser/tab_contents/interstitial_page.h" 16 #include "content/browser/tab_contents/tab_contents.h" 17 #include "content/browser/tab_contents/tab_contents_delegate.h" 18 #include "ui/gfx/canvas_skia_paint.h" 19 #include "ui/gfx/point.h" 20 #include "ui/gfx/rect.h" 21 #include "ui/gfx/size.h" 22 #include "views/controls/native/native_view_host.h" 23 #include "views/focus/focus_manager.h" 24 #include "views/focus/view_storage.h" 25 #include "views/layout/fill_layout.h" 26 #include "views/screen.h" 27 #include "views/widget/widget.h" 28 29 using WebKit::WebDragOperation; 30 using WebKit::WebDragOperationsMask; 31 using WebKit::WebInputEvent; 32 33 // static 34 TabContentsView* TabContentsView::Create(TabContents* tab_contents) { 35 return new TabContentsViewTouch(tab_contents); 36 } 37 38 TabContentsViewTouch::TabContentsViewTouch(TabContents* tab_contents) 39 : TabContentsView(tab_contents), 40 sad_tab_(NULL), 41 ignore_next_char_event_(false) { 42 last_focused_view_storage_id_ = 43 views::ViewStorage::GetInstance()->CreateStorageID(); 44 SetLayoutManager(new views::FillLayout()); 45 } 46 47 TabContentsViewTouch::~TabContentsViewTouch() { 48 // Make sure to remove any stored view we may still have in the ViewStorage. 49 // 50 // It is possible the view went away before us, so we only do this if the 51 // view is registered. 52 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 53 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL) 54 view_storage->RemoveView(last_focused_view_storage_id_); 55 } 56 57 void TabContentsViewTouch::AttachConstrainedWindow( 58 ConstrainedWindowGtk* constrained_window) { 59 // TODO(anicolao): reimplement all dialogs as WebUI 60 NOTIMPLEMENTED(); 61 } 62 63 void TabContentsViewTouch::RemoveConstrainedWindow( 64 ConstrainedWindowGtk* constrained_window) { 65 // TODO(anicolao): reimplement all dialogs as WebUI 66 NOTIMPLEMENTED(); 67 } 68 69 void TabContentsViewTouch::CreateView(const gfx::Size& initial_size) { 70 SetBoundsRect(gfx::Rect(bounds().origin(), initial_size)); 71 } 72 73 RenderWidgetHostView* TabContentsViewTouch::CreateViewForWidget( 74 RenderWidgetHost* render_widget_host) { 75 if (render_widget_host->view()) { 76 // During testing, the view will already be set up in most cases to the 77 // test view, so we don't want to clobber it with a real one. To verify that 78 // this actually is happening (and somebody isn't accidentally creating the 79 // view twice), we check for the RVH Factory, which will be set when we're 80 // making special ones (which go along with the special views). 81 DCHECK(RenderViewHostFactory::has_factory()); 82 return render_widget_host->view(); 83 } 84 85 // If we were showing sad tab, remove it now. 86 if (sad_tab_ != NULL) { 87 RemoveChildView(sad_tab_.get()); 88 sad_tab_.reset(); 89 } 90 91 RenderWidgetHostViewViews* view = 92 new RenderWidgetHostViewViews(render_widget_host); 93 AddChildView(view); 94 view->Show(); 95 view->InitAsChild(); 96 97 // TODO(anicolao): implement drag'n'drop hooks if needed 98 99 return view; 100 } 101 102 gfx::NativeView TabContentsViewTouch::GetNativeView() const { 103 return GetWidget()->GetNativeView(); 104 } 105 106 gfx::NativeView TabContentsViewTouch::GetContentNativeView() const { 107 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 108 if (!rwhv) 109 return NULL; 110 return rwhv->GetNativeView(); 111 } 112 113 gfx::NativeWindow TabContentsViewTouch::GetTopLevelNativeWindow() const { 114 GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW); 115 return window ? GTK_WINDOW(window) : NULL; 116 } 117 118 void TabContentsViewTouch::GetContainerBounds(gfx::Rect* out) const { 119 *out = bounds(); 120 } 121 122 void TabContentsViewTouch::StartDragging(const WebDropData& drop_data, 123 WebDragOperationsMask ops, 124 const SkBitmap& image, 125 const gfx::Point& image_offset) { 126 // TODO(anicolao): implement dragging 127 } 128 129 void TabContentsViewTouch::SetPageTitle(const std::wstring& title) { 130 // TODO(anicolao): figure out if there's anything useful to do here 131 } 132 133 void TabContentsViewTouch::OnTabCrashed(base::TerminationStatus status, 134 int /* error_code */) { 135 if (sad_tab_ != NULL) 136 return; 137 138 sad_tab_.reset(new SadTabView( 139 tab_contents(), 140 status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ? 141 SadTabView::KILLED : SadTabView::CRASHED)); 142 RemoveAllChildViews(true); 143 AddChildView(sad_tab_.get()); 144 Layout(); 145 } 146 147 void TabContentsViewTouch::SizeContents(const gfx::Size& size) { 148 WasSized(size); 149 150 // We need to send this immediately. 151 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 152 if (rwhv) 153 rwhv->SetSize(size); 154 } 155 156 void TabContentsViewTouch::Focus() { 157 if (tab_contents()->interstitial_page()) { 158 tab_contents()->interstitial_page()->Focus(); 159 return; 160 } 161 162 if (tab_contents()->is_crashed() && sad_tab_ != NULL) { 163 sad_tab_->RequestFocus(); 164 return; 165 } 166 167 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 168 if (rwhv) 169 rwhv->Focus(); 170 } 171 172 void TabContentsViewTouch::SetInitialFocus() { 173 if (tab_contents()->FocusLocationBarByDefault()) 174 tab_contents()->SetFocusToLocationBar(false); 175 else 176 Focus(); 177 } 178 179 void TabContentsViewTouch::StoreFocus() { 180 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 181 182 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL) 183 view_storage->RemoveView(last_focused_view_storage_id_); 184 185 views::FocusManager* focus_manager = 186 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 187 if (focus_manager) { 188 // |focus_manager| can be NULL if the tab has been detached but still 189 // exists. 190 views::View* focused_view = focus_manager->GetFocusedView(); 191 if (focused_view) 192 view_storage->StoreView(last_focused_view_storage_id_, focused_view); 193 } 194 } 195 196 void TabContentsViewTouch::RestoreFocus() { 197 views::ViewStorage* view_storage = views::ViewStorage::GetInstance(); 198 views::View* last_focused_view = 199 view_storage->RetrieveView(last_focused_view_storage_id_); 200 if (!last_focused_view) { 201 SetInitialFocus(); 202 } else { 203 views::FocusManager* focus_manager = 204 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 205 206 // If you hit this DCHECK, please report it to Jay (jcampan). 207 DCHECK(focus_manager != NULL) << "No focus manager when restoring focus."; 208 209 if (last_focused_view->IsFocusableInRootView() && focus_manager && 210 focus_manager->ContainsView(last_focused_view)) { 211 last_focused_view->RequestFocus(); 212 } else { 213 // The focused view may not belong to the same window hierarchy (e.g. 214 // if the location bar was focused and the tab is dragged out), or it may 215 // no longer be focusable (e.g. if the location bar was focused and then 216 // we switched to fullscreen mode). In that case we default to the 217 // default focus. 218 SetInitialFocus(); 219 } 220 view_storage->RemoveView(last_focused_view_storage_id_); 221 } 222 } 223 224 void TabContentsViewTouch::GetViewBounds(gfx::Rect* out) const { 225 out->SetRect(x(), y(), width(), height()); 226 } 227 228 void TabContentsViewTouch::OnBoundsChanged(const gfx::Rect& previous_bounds) { 229 if (IsVisibleInRootView()) 230 WasSized(size()); 231 } 232 233 void TabContentsViewTouch::OnPaint(gfx::Canvas* canvas) { 234 } 235 236 void TabContentsViewTouch::UpdateDragCursor(WebDragOperation operation) { 237 NOTIMPLEMENTED(); 238 // It's not even clear a drag cursor will make sense for touch. 239 // TODO(anicolao): implement dragging 240 } 241 242 void TabContentsViewTouch::GotFocus() { 243 if (tab_contents()->delegate()) 244 tab_contents()->delegate()->TabContentsFocused(tab_contents()); 245 } 246 247 void TabContentsViewTouch::TakeFocus(bool reverse) { 248 if (tab_contents()->delegate() && 249 !tab_contents()->delegate()->TakeFocus(reverse)) { 250 251 views::FocusManager* focus_manager = 252 views::FocusManager::GetFocusManagerForNativeView(GetNativeView()); 253 254 // We may not have a focus manager if the tab has been switched before this 255 // message arrived. 256 if (focus_manager) 257 focus_manager->AdvanceFocus(reverse); 258 } 259 } 260 261 void TabContentsViewTouch::VisibilityChanged(views::View *, bool is_visible) { 262 if (is_visible) { 263 WasShown(); 264 } else { 265 WasHidden(); 266 } 267 } 268 269 void TabContentsViewTouch::ShowContextMenu(const ContextMenuParams& params) { 270 // Allow delegates to handle the context menu operation first. 271 if (tab_contents()->delegate() && 272 tab_contents()->delegate()->HandleContextMenu(params)) 273 return; 274 275 context_menu_.reset(new RenderViewContextMenuViews(tab_contents(), params)); 276 context_menu_->Init(); 277 278 gfx::Point screen_point(params.x, params.y); 279 RenderWidgetHostViewViews* rwhv = static_cast<RenderWidgetHostViewViews*> 280 (tab_contents()->GetRenderWidgetHostView()); 281 if (rwhv) { 282 views::View::ConvertPointToScreen(rwhv, &screen_point); 283 } 284 285 // Enable recursive tasks on the message loop so we can get updates while 286 // the context menu is being displayed. 287 bool old_state = MessageLoop::current()->NestableTasksAllowed(); 288 MessageLoop::current()->SetNestableTasksAllowed(true); 289 context_menu_->RunMenuAt(screen_point.x(), screen_point.y()); 290 MessageLoop::current()->SetNestableTasksAllowed(old_state); 291 } 292 293 void TabContentsViewTouch::ShowPopupMenu(const gfx::Rect& bounds, 294 int item_height, 295 double item_font_size, 296 int selected_item, 297 const std::vector<WebMenuItem>& items, 298 bool right_aligned) { 299 // External popup menus are only used on Mac. 300 NOTREACHED(); 301 } 302 303 void TabContentsViewTouch::WasHidden() { 304 tab_contents()->HideContents(); 305 } 306 307 void TabContentsViewTouch::WasShown() { 308 tab_contents()->ShowContents(); 309 } 310 311 void TabContentsViewTouch::WasSized(const gfx::Size& size) { 312 // We have to check that the RenderWidgetHostView is the proper size. 313 // It can be wrong in cases where the renderer has died and the host 314 // view needed to be recreated. 315 bool needs_resize = size != size_; 316 317 if (needs_resize) { 318 size_ = size; 319 if (tab_contents()->interstitial_page()) 320 tab_contents()->interstitial_page()->SetSize(size); 321 } 322 323 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView(); 324 if (rwhv && rwhv->GetViewBounds().size() != size) 325 rwhv->SetSize(size); 326 327 if (needs_resize) 328 SetFloatingPosition(size); 329 } 330 331 void TabContentsViewTouch::SetFloatingPosition(const gfx::Size& size) { 332 // TODO(anicolao): rework this once we have WebUI views for dialogs 333 SetBounds(x(), y(), size.width(), size.height()); 334 } 335