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