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/frame/browser_root_view.h"
6
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/autocomplete/autocomplete.h"
9#include "chrome/browser/autocomplete/autocomplete_classifier.h"
10#include "chrome/browser/autocomplete/autocomplete_match.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/omnibox/location_bar.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 "grit/chromium_strings.h"
17#include "ui/base/accessibility/accessible_view_state.h"
18#include "ui/base/dragdrop/drag_drop_types.h"
19#include "ui/base/dragdrop/os_exchange_data.h"
20#include "ui/base/l10n/l10n_util.h"
21
22BrowserRootView::BrowserRootView(BrowserView* browser_view,
23                                 views::Widget* widget)
24    : views::RootView(widget),
25      browser_view_(browser_view),
26      forwarding_to_tab_strip_(false) { }
27
28bool BrowserRootView::GetDropFormats(
29      int* formats,
30      std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
31  if (tabstrip() && tabstrip()->IsVisible()) {
32    *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING;
33    return true;
34  }
35  return false;
36}
37
38bool BrowserRootView::AreDropTypesRequired() {
39  return true;
40}
41
42bool BrowserRootView::CanDrop(const ui::OSExchangeData& data) {
43  if (!tabstrip() || !tabstrip()->IsVisible())
44    return false;
45
46  // If there is a URL, we'll allow the drop.
47  if (data.HasURL())
48    return true;
49
50  // If there isn't a URL, see if we can 'paste and go'.
51  return GetPasteAndGoURL(data, NULL);
52}
53
54void BrowserRootView::OnDragEntered(const views::DropTargetEvent& event) {
55  if (ShouldForwardToTabStrip(event)) {
56    forwarding_to_tab_strip_ = true;
57    scoped_ptr<views::DropTargetEvent> mapped_event(
58        MapEventToTabStrip(event, event.data()));
59    tabstrip()->OnDragEntered(*mapped_event.get());
60  }
61}
62
63int BrowserRootView::OnDragUpdated(const views::DropTargetEvent& event) {
64  if (ShouldForwardToTabStrip(event)) {
65    scoped_ptr<views::DropTargetEvent> mapped_event(
66        MapEventToTabStrip(event, event.data()));
67    if (!forwarding_to_tab_strip_) {
68      tabstrip()->OnDragEntered(*mapped_event.get());
69      forwarding_to_tab_strip_ = true;
70    }
71    return tabstrip()->OnDragUpdated(*mapped_event.get());
72  } else if (forwarding_to_tab_strip_) {
73    forwarding_to_tab_strip_ = false;
74    tabstrip()->OnDragExited();
75  }
76  return ui::DragDropTypes::DRAG_NONE;
77}
78
79void BrowserRootView::OnDragExited() {
80  if (forwarding_to_tab_strip_) {
81    forwarding_to_tab_strip_ = false;
82    tabstrip()->OnDragExited();
83  }
84}
85
86int BrowserRootView::OnPerformDrop(const views::DropTargetEvent& event) {
87  if (!forwarding_to_tab_strip_)
88    return ui::DragDropTypes::DRAG_NONE;
89
90  // Extract the URL and create a new ui::OSExchangeData containing the URL. We
91  // do this as the TabStrip doesn't know about the autocomplete edit and needs
92  // to know about it to handle 'paste and go'.
93  GURL url;
94  string16 title;
95  ui::OSExchangeData mapped_data;
96  if (!event.data().GetURLAndTitle(&url, &title) || !url.is_valid()) {
97    // The url isn't valid. Use the paste and go url.
98    if (GetPasteAndGoURL(event.data(), &url))
99      mapped_data.SetURL(url, string16());
100    // else case: couldn't extract a url or 'paste and go' url. This ends up
101    // passing through an ui::OSExchangeData with nothing in it. We need to do
102    // this so that the tab strip cleans up properly.
103  } else {
104    mapped_data.SetURL(url, string16());
105  }
106  forwarding_to_tab_strip_ = false;
107  scoped_ptr<views::DropTargetEvent> mapped_event(
108      MapEventToTabStrip(event, mapped_data));
109  return tabstrip()->OnPerformDrop(*mapped_event);
110}
111
112void BrowserRootView::GetAccessibleState(ui::AccessibleViewState* state) {
113  RootView::GetAccessibleState(state);
114  state->name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
115}
116
117bool BrowserRootView::ShouldForwardToTabStrip(
118    const views::DropTargetEvent& event) {
119  if (!tabstrip()->IsVisible())
120    return false;
121
122  // Allow the drop as long as the mouse is over the tabstrip or vertically
123  // before it.
124  gfx::Point tab_loc_in_host;
125  ConvertPointToView(tabstrip(), this, &tab_loc_in_host);
126  return event.y() < tab_loc_in_host.y() + tabstrip()->height();
127}
128
129views::DropTargetEvent* BrowserRootView::MapEventToTabStrip(
130    const views::DropTargetEvent& event,
131    const ui::OSExchangeData& data) {
132  gfx::Point tab_strip_loc(event.location());
133  ConvertPointToView(this, tabstrip(), &tab_strip_loc);
134  return new views::DropTargetEvent(data, tab_strip_loc.x(),
135                                    tab_strip_loc.y(),
136                                    event.source_operations());
137}
138
139AbstractTabStripView* BrowserRootView::tabstrip() const {
140  return browser_view_->tabstrip();
141}
142
143bool BrowserRootView::GetPasteAndGoURL(const ui::OSExchangeData& data,
144                                       GURL* url) {
145  if (!data.HasString())
146    return false;
147
148  string16 text;
149  if (!data.GetString(&text) || text.empty())
150    return false;
151
152  AutocompleteMatch match;
153  browser_view_->browser()->profile()->GetAutocompleteClassifier()->Classify(
154      text, string16(), false, &match, NULL);
155  if (!match.destination_url.is_valid())
156    return false;
157
158  if (url)
159    *url = match.destination_url;
160  return true;
161}
162