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
Create(TabContents * tab_contents)35 TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
36 return new TabContentsViewViews(tab_contents);
37 }
38
TabContentsViewViews(TabContents * tab_contents)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
~TabContentsViewViews()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
Unparent()59 void TabContentsViewViews::Unparent() {
60 CHECK(native_tab_contents_view_.get());
61 native_tab_contents_view_->Unparent();
62 }
63
CreateView(const gfx::Size & initial_size)64 void TabContentsViewViews::CreateView(const gfx::Size& initial_size) {
65 native_tab_contents_view_->InitNativeTabContentsView();
66 }
67
CreateViewForWidget(RenderWidgetHost * render_widget_host)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
GetNativeView() const90 gfx::NativeView TabContentsViewViews::GetNativeView() const {
91 return GetWidget()->GetNativeView();
92 }
93
GetContentNativeView() const94 gfx::NativeView TabContentsViewViews::GetContentNativeView() const {
95 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
96 return rwhv ? rwhv->GetNativeView() : NULL;
97 }
98
GetTopLevelNativeWindow() const99 gfx::NativeWindow TabContentsViewViews::GetTopLevelNativeWindow() const {
100 return native_tab_contents_view_->GetTopLevelNativeWindow();
101 }
102
GetContainerBounds(gfx::Rect * out) const103 void TabContentsViewViews::GetContainerBounds(gfx::Rect* out) const {
104 *out = GetWidget()->GetClientAreaScreenBounds();
105 }
106
StartDragging(const WebDropData & drop_data,WebDragOperationsMask ops,const SkBitmap & image,const gfx::Point & image_offset)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
SetPageTitle(const std::wstring & title)114 void TabContentsViewViews::SetPageTitle(const std::wstring& title) {
115 native_tab_contents_view_->SetPageTitle(title);
116 }
117
OnTabCrashed(base::TerminationStatus status,int)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
SizeContents(const gfx::Size & size)132 void TabContentsViewViews::SizeContents(const gfx::Size& size) {
133 GetWidget()->SetSize(size);
134 }
135
Focus()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
SetInitialFocus()159 void TabContentsViewViews::SetInitialFocus() {
160 if (tab_contents()->FocusLocationBarByDefault())
161 tab_contents()->SetFocusToLocationBar(false);
162 else
163 Focus();
164 }
165
StoreFocus()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
RestoreFocus()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
IsDoingDrag() const212 bool TabContentsViewViews::IsDoingDrag() const {
213 return native_tab_contents_view_->IsDoingDrag();
214 }
215
CancelDragAndCloseTab()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
GetViewBounds(gfx::Rect * out) const225 void TabContentsViewViews::GetViewBounds(gfx::Rect* out) const {
226 *out = GetWidget()->GetWindowScreenBounds();
227 }
228
UpdateDragCursor(WebDragOperation operation)229 void TabContentsViewViews::UpdateDragCursor(WebDragOperation operation) {
230 native_tab_contents_view_->SetDragCursor(operation);
231 }
232
GotFocus()233 void TabContentsViewViews::GotFocus() {
234 if (tab_contents()->delegate())
235 tab_contents()->delegate()->TabContentsFocused(tab_contents());
236 }
237
TakeFocus(bool reverse)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
GetWidget()250 views::Widget* TabContentsViewViews::GetWidget() {
251 return native_tab_contents_view_->AsNativeWidget()->GetWidget();
252 }
253
GetWidget() const254 const views::Widget* TabContentsViewViews::GetWidget() const {
255 return native_tab_contents_view_->AsNativeWidget()->GetWidget();
256 }
257
CloseTab()258 void TabContentsViewViews::CloseTab() {
259 tab_contents()->Close(tab_contents()->render_view_host());
260 }
261
ShowContextMenu(const ContextMenuParams & params)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
ShowPopupMenu(const gfx::Rect & bounds,int item_height,double item_font_size,int selected_item,const std::vector<WebMenuItem> & items,bool right_aligned)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
GetTabContents()294 TabContents* TabContentsViewViews::GetTabContents() {
295 return tab_contents();
296 }
297
IsShowingSadTab() const298 bool TabContentsViewViews::IsShowingSadTab() const {
299 return tab_contents()->is_crashed() && sad_tab_;
300 }
301
OnNativeTabContentsViewShown()302 void TabContentsViewViews::OnNativeTabContentsViewShown() {
303 tab_contents()->ShowContents();
304 }
305
OnNativeTabContentsViewHidden()306 void TabContentsViewViews::OnNativeTabContentsViewHidden() {
307 tab_contents()->HideContents();
308 }
309
OnNativeTabContentsViewSized(const gfx::Size & size)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
OnNativeTabContentsViewWheelZoom(int distance)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
OnNativeTabContentsViewMouseDown()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
OnNativeTabContentsViewMouseMove()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
OnNativeTabContentsViewDraggingEnded()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