Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(553)

Side by Side Diff: chrome/browser/ui/views/tab_contents/tab_contents_view_gtk.cc

Issue 7015051: Re-land: (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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_gtk.h"
6
7 #include <gdk/gdk.h>
8 #include <gtk/gtk.h>
9
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "build/build_config.h"
13 #include "chrome/browser/download/download_shelf.h"
14 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
15 #include "chrome/browser/tab_contents/web_drag_dest_gtk.h"
16 #include "chrome/browser/ui/gtk/constrained_window_gtk.h"
17 #include "chrome/browser/ui/gtk/tab_contents_drag_source.h"
18 #include "chrome/browser/ui/views/sad_tab_view.h"
19 #include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h"
20 #include "content/browser/renderer_host/render_view_host.h"
21 #include "content/browser/renderer_host/render_view_host_factory.h"
22 #include "content/browser/tab_contents/interstitial_page.h"
23 #include "content/browser/tab_contents/tab_contents.h"
24 #include "content/browser/tab_contents/tab_contents_delegate.h"
25 #include "ui/gfx/canvas_skia_paint.h"
26 #include "ui/gfx/point.h"
27 #include "ui/gfx/rect.h"
28 #include "ui/gfx/size.h"
29 #include "views/controls/native/native_view_host.h"
30 #include "views/focus/view_storage.h"
31 #include "views/screen.h"
32 #include "views/widget/root_view.h"
33 #include "views/widget/widget_gtk.h"
34
35 using WebKit::WebDragOperation;
36 using WebKit::WebDragOperationsMask;
37 using WebKit::WebInputEvent;
38
39
40 namespace {
41
42 // Called when the content view gtk widget is tabbed to, or after the call to
43 // gtk_widget_child_focus() in TakeFocus(). We return true
44 // and grab focus if we don't have it. The call to
45 // FocusThroughTabTraversal(bool) forwards the "move focus forward" effect to
46 // webkit.
47 gboolean OnFocus(GtkWidget* widget, GtkDirectionType focus,
48 TabContents* tab_contents) {
49 // If we already have focus, let the next widget have a shot at it. We will
50 // reach this situation after the call to gtk_widget_child_focus() in
51 // TakeFocus().
52 if (gtk_widget_is_focus(widget))
53 return FALSE;
54
55 gtk_widget_grab_focus(widget);
56 bool reverse = focus == GTK_DIR_TAB_BACKWARD;
57 tab_contents->FocusThroughTabTraversal(reverse);
58 return TRUE;
59 }
60
61 // Called when the mouse leaves the widget. We notify our delegate.
62 // WidgetGtk also defines OnLeaveNotify, so we use the name OnLeaveNotify2
63 // here.
64 gboolean OnLeaveNotify2(GtkWidget* widget, GdkEventCrossing* event,
65 TabContents* tab_contents) {
66 if (tab_contents->delegate())
67 tab_contents->delegate()->ContentsMouseEvent(
68 tab_contents, views::Screen::GetCursorScreenPoint(), false);
69 return FALSE;
70 }
71
72 // Called when the mouse moves within the widget.
73 gboolean CallMouseMove(GtkWidget* widget, GdkEventMotion* event,
74 TabContentsViewGtk* tab_contents_view) {
75 return tab_contents_view->OnMouseMove(widget, event);
76 }
77
78 // See tab_contents_view_gtk.cc for discussion of mouse scroll zooming.
79 gboolean OnMouseScroll(GtkWidget* widget, GdkEventScroll* event,
80 TabContents* tab_contents) {
81 if ((event->state & gtk_accelerator_get_default_mod_mask()) ==
82 GDK_CONTROL_MASK) {
83 if (tab_contents->delegate()) {
84 if (event->direction == GDK_SCROLL_DOWN) {
85 tab_contents->delegate()->ContentsZoomChange(false);
86 return TRUE;
87 } else if (event->direction == GDK_SCROLL_UP) {
88 tab_contents->delegate()->ContentsZoomChange(true);
89 return TRUE;
90 }
91 }
92 }
93
94 return FALSE;
95 }
96
97 } // namespace
98
99 // static
100 TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
101 return new TabContentsViewGtk(tab_contents);
102 }
103
104 TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents)
105 : TabContentsView(tab_contents),
106 sad_tab_(NULL),
107 ignore_next_char_event_(false) {
108 drag_source_.reset(new TabContentsDragSource(this));
109 last_focused_view_storage_id_ =
110 views::ViewStorage::GetInstance()->CreateStorageID();
111 }
112
113 TabContentsViewGtk::~TabContentsViewGtk() {
114 // Make sure to remove any stored view we may still have in the ViewStorage.
115 //
116 // It is possible the view went away before us, so we only do this if the
117 // view is registered.
118 views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
119 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
120 view_storage->RemoveView(last_focused_view_storage_id_);
121
122 // Just deleting the object doesn't destroy the GtkWidget. We need to do that
123 // manually, and synchronously, since subsequent signal handlers may expect
124 // to locate this object.
125 CloseNow();
126 }
127
128 void TabContentsViewGtk::AttachConstrainedWindow(
129 ConstrainedWindowGtk* constrained_window) {
130 DCHECK(find(constrained_windows_.begin(), constrained_windows_.end(),
131 constrained_window) == constrained_windows_.end());
132
133 constrained_windows_.push_back(constrained_window);
134 AddChild(constrained_window->widget());
135
136 gfx::Rect bounds;
137 GetContainerBounds(&bounds);
138 SetFloatingPosition(bounds.size());
139 }
140
141 void TabContentsViewGtk::RemoveConstrainedWindow(
142 ConstrainedWindowGtk* constrained_window) {
143 std::vector<ConstrainedWindowGtk*>::iterator item =
144 find(constrained_windows_.begin(), constrained_windows_.end(),
145 constrained_window);
146 DCHECK(item != constrained_windows_.end());
147 RemoveChild((*item)->widget());
148 constrained_windows_.erase(item);
149 }
150
151 void TabContentsViewGtk::CreateView(const gfx::Size& initial_size) {
152 views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
153 params.delete_on_destroy = false;
154 params.bounds = gfx::Rect(initial_size);
155 GetWidget()->Init(params);
156 // We need to own the widget in order to attach/detach the native view
157 // to container.
158 gtk_object_ref(GTK_OBJECT(GetNativeView()));
159 }
160
161 RenderWidgetHostView* TabContentsViewGtk::CreateViewForWidget(
162 RenderWidgetHost* render_widget_host) {
163 if (render_widget_host->view()) {
164 // During testing, the view will already be set up in most cases to the
165 // test view, so we don't want to clobber it with a real one. To verify that
166 // this actually is happening (and somebody isn't accidentally creating the
167 // view twice), we check for the RVH Factory, which will be set when we're
168 // making special ones (which go along with the special views).
169 DCHECK(RenderViewHostFactory::has_factory());
170 return render_widget_host->view();
171 }
172
173 // If we were showing sad tab, remove it now.
174 if (sad_tab_ != NULL) {
175 SetContentsView(new views::View());
176 sad_tab_ = NULL;
177 }
178
179 RenderWidgetHostViewGtk* view =
180 new RenderWidgetHostViewGtk(render_widget_host);
181 view->InitAsChild();
182 g_signal_connect(view->native_view(), "focus",
183 G_CALLBACK(OnFocus), tab_contents());
184 g_signal_connect(view->native_view(), "leave-notify-event",
185 G_CALLBACK(OnLeaveNotify2), tab_contents());
186 g_signal_connect(view->native_view(), "motion-notify-event",
187 G_CALLBACK(CallMouseMove), this);
188 g_signal_connect(view->native_view(), "scroll-event",
189 G_CALLBACK(OnMouseScroll), tab_contents());
190 gtk_widget_add_events(view->native_view(), GDK_LEAVE_NOTIFY_MASK |
191 GDK_POINTER_MOTION_MASK);
192
193 // Let widget know that the tab contents has been painted.
194 views::WidgetGtk::RegisterChildExposeHandler(view->native_view());
195
196 // Renderer target DnD.
197 if (tab_contents()->ShouldAcceptDragAndDrop())
198 drag_dest_.reset(new WebDragDestGtk(tab_contents(), view->native_view()));
199
200 gtk_fixed_put(GTK_FIXED(GetNativeView()), view->native_view(), 0, 0);
201 return view;
202 }
203
204 gfx::NativeView TabContentsViewGtk::GetNativeView() const {
205 return WidgetGtk::GetNativeView();
206 }
207
208 gfx::NativeView TabContentsViewGtk::GetContentNativeView() const {
209 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
210 if (!rwhv)
211 return NULL;
212 return rwhv->GetNativeView();
213 }
214
215 gfx::NativeWindow TabContentsViewGtk::GetTopLevelNativeWindow() const {
216 GtkWidget* window = gtk_widget_get_ancestor(GetNativeView(), GTK_TYPE_WINDOW);
217 return window ? GTK_WINDOW(window) : NULL;
218 }
219
220 void TabContentsViewGtk::GetContainerBounds(gfx::Rect* out) const {
221 // Callers expect the requested bounds not the actual bounds. For example,
222 // during init callers expect 0x0, but Gtk layout enforces a min size of 1x1.
223 *out = GetClientAreaScreenBounds();
224
225 gfx::Size size;
226 WidgetGtk::GetRequestedSize(&size);
227 out->set_size(size);
228 }
229
230 void TabContentsViewGtk::StartDragging(const WebDropData& drop_data,
231 WebDragOperationsMask ops,
232 const SkBitmap& image,
233 const gfx::Point& image_offset) {
234 drag_source_->StartDragging(drop_data, ops, &last_mouse_down_,
235 image, image_offset);
236 }
237
238 void TabContentsViewGtk::SetPageTitle(const std::wstring& title) {
239 // Set the window name to include the page title so it's easier to spot
240 // when debugging (e.g. via xwininfo -tree).
241 gfx::NativeView content_view = GetContentNativeView();
242 if (content_view && content_view->window)
243 gdk_window_set_title(content_view->window, WideToUTF8(title).c_str());
244 }
245
246 void TabContentsViewGtk::OnTabCrashed(base::TerminationStatus status,
247 int /* error_code */) {
248 SadTabView::Kind kind =
249 status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ?
250 SadTabView::KILLED : SadTabView::CRASHED;
251 sad_tab_ = new SadTabView(tab_contents(), kind);
252 SetContentsView(sad_tab_);
253 }
254
255 void TabContentsViewGtk::SizeContents(const gfx::Size& size) {
256 // TODO(brettw) this is a hack and should be removed. See tab_contents_view.h.
257
258 // We're contained in a fixed. To have the fixed relay us out to |size|, set
259 // the size request, which triggers OnSizeAllocate.
260 gtk_widget_set_size_request(GetNativeView(), size.width(), size.height());
261
262 // We need to send this immediately.
263 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
264 if (rwhv)
265 rwhv->SetSize(size);
266 }
267
268 void TabContentsViewGtk::Focus() {
269 if (tab_contents()->interstitial_page()) {
270 tab_contents()->interstitial_page()->Focus();
271 return;
272 }
273
274 if (tab_contents()->is_crashed() && sad_tab_ != NULL) {
275 sad_tab_->RequestFocus();
276 return;
277 }
278
279 if (constrained_windows_.size()) {
280 constrained_windows_.back()->FocusConstrainedWindow();
281 return;
282 }
283
284 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
285 gtk_widget_grab_focus(rwhv ? rwhv->GetNativeView() : GetNativeView());
286 }
287
288 void TabContentsViewGtk::SetInitialFocus() {
289 if (tab_contents()->FocusLocationBarByDefault())
290 tab_contents()->SetFocusToLocationBar(false);
291 else
292 Focus();
293 }
294
295 void TabContentsViewGtk::StoreFocus() {
296 views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
297
298 if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
299 view_storage->RemoveView(last_focused_view_storage_id_);
300
301 views::FocusManager* focus_manager =
302 views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
303 if (focus_manager) {
304 // |focus_manager| can be NULL if the tab has been detached but still
305 // exists.
306 views::View* focused_view = focus_manager->GetFocusedView();
307 if (focused_view)
308 view_storage->StoreView(last_focused_view_storage_id_, focused_view);
309 }
310 }
311
312 void TabContentsViewGtk::RestoreFocus() {
313 views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
314 views::View* last_focused_view =
315 view_storage->RetrieveView(last_focused_view_storage_id_);
316 if (!last_focused_view) {
317 SetInitialFocus();
318 } else {
319 views::FocusManager* focus_manager =
320 views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
321
322 // If you hit this DCHECK, please report it to Jay (jcampan).
323 DCHECK(focus_manager != NULL) << "No focus manager when restoring focus.";
324
325 if (last_focused_view->IsFocusableInRootView() && focus_manager &&
326 focus_manager->ContainsView(last_focused_view)) {
327 last_focused_view->RequestFocus();
328 } else {
329 // The focused view may not belong to the same window hierarchy (e.g.
330 // if the location bar was focused and the tab is dragged out), or it may
331 // no longer be focusable (e.g. if the location bar was focused and then
332 // we switched to fullscreen mode). In that case we default to the
333 // default focus.
334 SetInitialFocus();
335 }
336 view_storage->RemoveView(last_focused_view_storage_id_);
337 }
338 }
339
340 void TabContentsViewGtk::GetViewBounds(gfx::Rect* out) const {
341 *out = GetWindowScreenBounds();
342 }
343
344 void TabContentsViewGtk::UpdateDragCursor(WebDragOperation operation) {
345 if (drag_dest_.get())
346 drag_dest_->UpdateDragStatus(operation);
347 }
348
349 void TabContentsViewGtk::GotFocus() {
350 if (tab_contents()->delegate())
351 tab_contents()->delegate()->TabContentsFocused(tab_contents());
352 }
353
354 void TabContentsViewGtk::TakeFocus(bool reverse) {
355 if (tab_contents()->delegate() &&
356 !tab_contents()->delegate()->TakeFocus(reverse)) {
357
358 views::FocusManager* focus_manager =
359 views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
360
361 // We may not have a focus manager if the tab has been switched before this
362 // message arrived.
363 if (focus_manager)
364 focus_manager->AdvanceFocus(reverse);
365 }
366 }
367
368 void TabContentsViewGtk::ShowContextMenu(const ContextMenuParams& params) {
369 // Allow delegates to handle the context menu operation first.
370 if (tab_contents()->delegate()->HandleContextMenu(params))
371 return;
372
373 context_menu_.reset(new RenderViewContextMenuViews(tab_contents(), params));
374 context_menu_->Init();
375
376 gfx::Point screen_point(params.x, params.y);
377 views::View::ConvertPointToScreen(GetRootView(), &screen_point);
378
379 // Enable recursive tasks on the message loop so we can get updates while
380 // the context menu is being displayed.
381 bool old_state = MessageLoop::current()->NestableTasksAllowed();
382 MessageLoop::current()->SetNestableTasksAllowed(true);
383 context_menu_->RunMenuAt(screen_point.x(), screen_point.y());
384 MessageLoop::current()->SetNestableTasksAllowed(old_state);
385 }
386
387 void TabContentsViewGtk::ShowPopupMenu(const gfx::Rect& bounds,
388 int item_height,
389 double item_font_size,
390 int selected_item,
391 const std::vector<WebMenuItem>& items,
392 bool right_aligned) {
393 // External popup menus are only used on Mac.
394 NOTREACHED();
395 }
396
397 gboolean TabContentsViewGtk::OnButtonPress(GtkWidget* widget,
398 GdkEventButton* event) {
399 last_mouse_down_ = *event;
400 return views::WidgetGtk::OnButtonPress(widget, event);
401 }
402
403 void TabContentsViewGtk::OnSizeAllocate(GtkWidget* widget,
404 GtkAllocation* allocation) {
405 gfx::Size new_size(allocation->width, allocation->height);
406
407 // Always call WasSized() to allow checking to make sure the
408 // RenderWidgetHostView is the right size.
409 WasSized(new_size);
410 }
411
412 gboolean TabContentsViewGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) {
413 if (tab_contents()->render_view_host() &&
414 !tab_contents()->render_view_host()->IsRenderViewLive() &&
415 sad_tab_) {
416 gfx::CanvasSkiaPaint canvas(event);
417 sad_tab_->Paint(&canvas);
418 }
419 return false; // False indicates other widgets should get the event as well.
420 }
421
422 void TabContentsViewGtk::OnShow(GtkWidget* widget) {
423 WasShown();
424 }
425
426 void TabContentsViewGtk::OnHide(GtkWidget* widget) {
427 WasHidden();
428 }
429
430 void TabContentsViewGtk::WasHidden() {
431 tab_contents()->HideContents();
432 }
433
434 void TabContentsViewGtk::WasShown() {
435 tab_contents()->ShowContents();
436 }
437
438 void TabContentsViewGtk::WasSized(const gfx::Size& size) {
439 // We have to check that the RenderWidgetHostView is the proper size.
440 // It can be wrong in cases where the renderer has died and the host
441 // view needed to be recreated.
442 bool needs_resize = size != size_;
443
444 if (needs_resize) {
445 size_ = size;
446 if (tab_contents()->interstitial_page())
447 tab_contents()->interstitial_page()->SetSize(size);
448 }
449
450 RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
451 if (rwhv && rwhv->GetViewBounds().size() != size)
452 rwhv->SetSize(size);
453 if (sad_tab_ && sad_tab_->size() != size)
454 sad_tab_->SetSize(size);
455
456 if (needs_resize)
457 SetFloatingPosition(size);
458 }
459
460 void TabContentsViewGtk::SetFloatingPosition(const gfx::Size& size) {
461 // Place each ConstrainedWindow in the center of the view.
462 int half_view_width = size.width() / 2;
463
464 typedef std::vector<ConstrainedWindowGtk*>::iterator iterator;
465
466 for (iterator f = constrained_windows_.begin(),
467 l = constrained_windows_.end(); f != l; ++f) {
468 GtkWidget* widget = (*f)->widget();
469
470 GtkRequisition requisition;
471 gtk_widget_size_request(widget, &requisition);
472
473 int child_x = std::max(half_view_width - (requisition.width / 2), 0);
474 PositionChild(widget, child_x, 0, 0, 0);
475 }
476 }
477
478 // Called when the mouse moves within the widget. We notify SadTabView if it's
479 // not NULL, else our delegate.
480 gboolean TabContentsViewGtk::OnMouseMove(GtkWidget* widget,
481 GdkEventMotion* event) {
482 if (sad_tab_ != NULL)
483 WidgetGtk::OnMotionNotify(widget, event);
484 else if (tab_contents()->delegate())
485 tab_contents()->delegate()->ContentsMouseEvent(
486 tab_contents(), views::Screen::GetCursorScreenPoint(), true);
487 return FALSE;
488 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698