tab_contents_view_views.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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 "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_view_host.h"
11#include "chrome/browser/renderer_host/render_view_host_factory.h"
12#include "chrome/browser/renderer_host/render_widget_host_view_views.h"
13#include "chrome/browser/tab_contents/interstitial_page.h"
14#include "chrome/browser/tab_contents/tab_contents.h"
15#include "chrome/browser/tab_contents/tab_contents_delegate.h"
16#include "chrome/browser/ui/views/sad_tab_view.h"
17#include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.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
29using WebKit::WebDragOperation;
30using WebKit::WebDragOperationsMask;
31using WebKit::WebInputEvent;
32
33// static
34TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
35  return new TabContentsViewViews(tab_contents);
36}
37
38TabContentsViewViews::TabContentsViewViews(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
47TabContentsViewViews::~TabContentsViewViews() {
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
57void TabContentsViewViews::AttachConstrainedWindow(
58    ConstrainedWindowGtk* constrained_window) {
59  // TODO(anicolao): reimplement all dialogs as WebUI
60  NOTIMPLEMENTED();
61}
62
63void TabContentsViewViews::RemoveConstrainedWindow(
64    ConstrainedWindowGtk* constrained_window) {
65  // TODO(anicolao): reimplement all dialogs as WebUI
66  NOTIMPLEMENTED();
67}
68
69void TabContentsViewViews::CreateView(const gfx::Size& initial_size) {
70  SetBoundsRect(gfx::Rect(bounds().origin(), initial_size));
71}
72
73RenderWidgetHostView* TabContentsViewViews::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
102gfx::NativeView TabContentsViewViews::GetNativeView() const {
103  return GetWidget()->GetNativeView();
104}
105
106gfx::NativeView TabContentsViewViews::GetContentNativeView() const {
107  RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
108  if (!rwhv)
109    return NULL;
110  return rwhv->GetNativeView();
111}
112
113gfx::NativeWindow TabContentsViewViews::GetTopLevelNativeWindow() const {
114  GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW);
115  return window ? GTK_WINDOW(window) : NULL;
116}
117
118void TabContentsViewViews::GetContainerBounds(gfx::Rect* out) const {
119  *out = bounds();
120}
121
122void TabContentsViewViews::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
129void TabContentsViewViews::SetPageTitle(const std::wstring& title) {
130  // TODO(anicolao): figure out if there's anything useful to do here
131}
132
133void TabContentsViewViews::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
147void TabContentsViewViews::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
156void TabContentsViewViews::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
172void TabContentsViewViews::SetInitialFocus() {
173  if (tab_contents()->FocusLocationBarByDefault())
174    tab_contents()->SetFocusToLocationBar(false);
175  else
176    Focus();
177}
178
179void TabContentsViewViews::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
196void TabContentsViewViews::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
224void TabContentsViewViews::GetViewBounds(gfx::Rect* out) const {
225  out->SetRect(x(), y(), width(), height());
226}
227
228void TabContentsViewViews::OnBoundsChanged() {
229  if (IsVisibleInRootView())
230    WasSized(size());
231}
232
233void TabContentsViewViews::Paint(gfx::Canvas* canvas) {
234}
235
236void TabContentsViewViews::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
242void TabContentsViewViews::GotFocus() {
243  if (tab_contents()->delegate())
244    tab_contents()->delegate()->TabContentsFocused(tab_contents());
245}
246
247void TabContentsViewViews::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
261void TabContentsViewViews::VisibilityChanged(views::View *, bool is_visible) {
262  if (is_visible) {
263    WasShown();
264  } else {
265    WasHidden();
266  }
267}
268
269void TabContentsViewViews::ShowContextMenu(const ContextMenuParams& params) {
270  // Allow delegates to handle the context menu operation first.
271  if (tab_contents()->delegate()->HandleContextMenu(params))
272    return;
273
274  context_menu_.reset(new RenderViewContextMenuViews(tab_contents(), params));
275  context_menu_->Init();
276
277  gfx::Point screen_point(params.x, params.y);
278  RenderWidgetHostViewViews* rwhv = static_cast<RenderWidgetHostViewViews*>
279      (tab_contents()->GetRenderWidgetHostView());
280  if (rwhv) {
281    views::View::ConvertPointToScreen(rwhv, &screen_point);
282  }
283
284  // Enable recursive tasks on the message loop so we can get updates while
285  // the context menu is being displayed.
286  bool old_state = MessageLoop::current()->NestableTasksAllowed();
287  MessageLoop::current()->SetNestableTasksAllowed(true);
288  context_menu_->RunMenuAt(screen_point.x(), screen_point.y());
289  MessageLoop::current()->SetNestableTasksAllowed(old_state);
290}
291
292void TabContentsViewViews::ShowPopupMenu(const gfx::Rect& bounds,
293                                         int item_height,
294                                         double item_font_size,
295                                         int selected_item,
296                                         const std::vector<WebMenuItem>& items,
297                                         bool right_aligned) {
298  // External popup menus are only used on Mac.
299  NOTREACHED();
300}
301
302void TabContentsViewViews::WasHidden() {
303  tab_contents()->HideContents();
304}
305
306void TabContentsViewViews::WasShown() {
307  tab_contents()->ShowContents();
308}
309
310void TabContentsViewViews::WasSized(const gfx::Size& size) {
311  // We have to check that the RenderWidgetHostView is the proper size.
312  // It can be wrong in cases where the renderer has died and the host
313  // view needed to be recreated.
314  bool needs_resize = size != size_;
315
316  if (needs_resize) {
317    size_ = size;
318    if (tab_contents()->interstitial_page())
319      tab_contents()->interstitial_page()->SetSize(size);
320  }
321
322  RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
323  if (rwhv && rwhv->GetViewBounds().size() != size)
324    rwhv->SetSize(size);
325
326  if (needs_resize)
327    SetFloatingPosition(size);
328}
329
330void TabContentsViewViews::SetFloatingPosition(const gfx::Size& size) {
331  // TODO(anicolao): rework this once we have WebUI views for dialogs
332  SetBounds(x(), y(), size.width(), size.height());
333}
334