browser_root_view.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 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/frame/browser_root_view.h"
6
7#include "chrome/browser/autocomplete/autocomplete_classifier.h"
8#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
9#include "chrome/browser/autocomplete/autocomplete_match.h"
10#include "chrome/browser/defaults.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/browser_commands.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/browser/ui/views/frame/browser_frame.h"
15#include "chrome/browser/ui/views/frame/browser_view.h"
16#include "chrome/browser/ui/views/tabs/tab_strip.h"
17#include "chrome/browser/ui/views/touch_uma/touch_uma.h"
18#include "ui/base/dragdrop/drag_drop_types.h"
19#include "ui/base/dragdrop/os_exchange_data.h"
20
21// static
22const char BrowserRootView::kViewClassName[] =
23    "browser/ui/views/frame/BrowserRootView";
24
25BrowserRootView::BrowserRootView(BrowserView* browser_view,
26                                 views::Widget* widget)
27    : views::internal::RootView(widget),
28      browser_view_(browser_view),
29      forwarding_to_tab_strip_(false) { }
30
31bool BrowserRootView::GetDropFormats(
32      int* formats,
33      std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
34  if (tabstrip() && tabstrip()->visible()) {
35    *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING;
36    return true;
37  }
38  return false;
39}
40
41bool BrowserRootView::AreDropTypesRequired() {
42  return true;
43}
44
45bool BrowserRootView::CanDrop(const ui::OSExchangeData& data) {
46  if (!tabstrip() || !tabstrip()->visible())
47    return false;
48
49  // If there is a URL, we'll allow the drop.
50  if (data.HasURL())
51    return true;
52
53  // If there isn't a URL, see if we can 'paste and go'.
54  return GetPasteAndGoURL(data, NULL);
55}
56
57void BrowserRootView::OnDragEntered(const ui::DropTargetEvent& event) {
58  if (ShouldForwardToTabStrip(event)) {
59    forwarding_to_tab_strip_ = true;
60    scoped_ptr<ui::DropTargetEvent> mapped_event(
61        MapEventToTabStrip(event, event.data()));
62    tabstrip()->OnDragEntered(*mapped_event.get());
63  }
64}
65
66int BrowserRootView::OnDragUpdated(const ui::DropTargetEvent& event) {
67  if (ShouldForwardToTabStrip(event)) {
68    scoped_ptr<ui::DropTargetEvent> mapped_event(
69        MapEventToTabStrip(event, event.data()));
70    if (!forwarding_to_tab_strip_) {
71      tabstrip()->OnDragEntered(*mapped_event.get());
72      forwarding_to_tab_strip_ = true;
73    }
74    return tabstrip()->OnDragUpdated(*mapped_event.get());
75  } else if (forwarding_to_tab_strip_) {
76    forwarding_to_tab_strip_ = false;
77    tabstrip()->OnDragExited();
78  }
79  return ui::DragDropTypes::DRAG_NONE;
80}
81
82void BrowserRootView::OnDragExited() {
83  if (forwarding_to_tab_strip_) {
84    forwarding_to_tab_strip_ = false;
85    tabstrip()->OnDragExited();
86  }
87}
88
89int BrowserRootView::OnPerformDrop(const ui::DropTargetEvent& event) {
90  if (!forwarding_to_tab_strip_)
91    return ui::DragDropTypes::DRAG_NONE;
92
93  // Extract the URL and create a new ui::OSExchangeData containing the URL. We
94  // do this as the TabStrip doesn't know about the autocomplete edit and needs
95  // to know about it to handle 'paste and go'.
96  GURL url;
97  base::string16 title;
98  ui::OSExchangeData mapped_data;
99  if (!event.data().GetURLAndTitle(&url, &title) || !url.is_valid()) {
100    // The url isn't valid. Use the paste and go url.
101    if (GetPasteAndGoURL(event.data(), &url))
102      mapped_data.SetURL(url, base::string16());
103    // else case: couldn't extract a url or 'paste and go' url. This ends up
104    // passing through an ui::OSExchangeData with nothing in it. We need to do
105    // this so that the tab strip cleans up properly.
106  } else {
107    mapped_data.SetURL(url, base::string16());
108  }
109  forwarding_to_tab_strip_ = false;
110  scoped_ptr<ui::DropTargetEvent> mapped_event(
111      MapEventToTabStrip(event, mapped_data));
112  return tabstrip()->OnPerformDrop(*mapped_event);
113}
114
115const char* BrowserRootView::GetClassName() const {
116  return kViewClassName;
117}
118
119bool BrowserRootView::OnMouseWheel(const ui::MouseWheelEvent& event) {
120  if (browser_defaults::kScrollEventChangesTab) {
121    // Switch to the left/right tab if the wheel-scroll happens over the
122    // tabstrip, or the empty space beside the tabstrip.
123    views::View* hit_view = GetEventHandlerForPoint(event.location());
124    views::NonClientView* non_client = GetWidget()->non_client_view();
125    if (tabstrip()->Contains(hit_view) ||
126        hit_view == non_client->frame_view()) {
127      int scroll_offset = abs(event.y_offset()) > abs(event.x_offset()) ?
128          event.y_offset() : -event.x_offset();
129      Browser* browser = browser_view_->browser();
130      TabStripModel* model = browser->tab_strip_model();
131      // Switch to the next tab only if not at the end of the tab-strip.
132      if (scroll_offset < 0 && model->active_index() + 1 < model->count()) {
133        chrome::SelectNextTab(browser);
134        return true;
135      }
136
137      // Switch to the previous tab only if not at the beginning of the
138      // tab-strip.
139      if (scroll_offset > 0 && model->active_index() > 0) {
140        chrome::SelectPreviousTab(browser);
141        return true;
142      }
143    }
144  }
145  return RootView::OnMouseWheel(event);
146}
147
148void BrowserRootView::DispatchGestureEvent(ui::GestureEvent* event) {
149  if (event->type() == ui::ET_GESTURE_TAP &&
150      event->location().y() <= 0 &&
151      event->location().x() <= browser_view_->GetBounds().width()) {
152    TouchUMA::RecordGestureAction(TouchUMA::GESTURE_ROOTVIEWTOP_TAP);
153  }
154
155  RootView::DispatchGestureEvent(event);
156}
157
158bool BrowserRootView::ShouldForwardToTabStrip(
159    const ui::DropTargetEvent& event) {
160  if (!tabstrip()->visible())
161    return false;
162
163  // Allow the drop as long as the mouse is over the tabstrip or vertically
164  // before it.
165  gfx::Point tab_loc_in_host;
166  ConvertPointToTarget(tabstrip(), this, &tab_loc_in_host);
167  return event.y() < tab_loc_in_host.y() + tabstrip()->height();
168}
169
170ui::DropTargetEvent* BrowserRootView::MapEventToTabStrip(
171    const ui::DropTargetEvent& event,
172    const ui::OSExchangeData& data) {
173  gfx::Point tab_strip_loc(event.location());
174  ConvertPointToTarget(this, tabstrip(), &tab_strip_loc);
175  return new ui::DropTargetEvent(data, tab_strip_loc, tab_strip_loc,
176                                 event.source_operations());
177}
178
179TabStrip* BrowserRootView::tabstrip() const {
180  return browser_view_->tabstrip();
181}
182
183bool BrowserRootView::GetPasteAndGoURL(const ui::OSExchangeData& data,
184                                       GURL* url) {
185  if (!data.HasString())
186    return false;
187
188  base::string16 text;
189  if (!data.GetString(&text) || text.empty())
190    return false;
191  text = AutocompleteMatch::SanitizeString(text);
192
193  AutocompleteMatch match;
194  AutocompleteClassifierFactory::GetForProfile(
195      browser_view_->browser()->profile())->Classify(text, false, false, &match,
196                                                     NULL);
197  if (!match.destination_url.is_valid())
198    return false;
199
200  if (url)
201    *url = match.destination_url;
202  return true;
203}
204