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/tab_contents/web_drop_target_win.h"
6
7 #include <windows.h>
8 #include <shlobj.h>
9
10 #include "chrome/browser/bookmarks/bookmark_node_data.h"
11 #include "chrome/browser/tab_contents/web_drag_utils_win.h"
12 #include "content/browser/renderer_host/render_view_host.h"
13 #include "content/browser/tab_contents/tab_contents.h"
14 #include "googleurl/src/gurl.h"
15 #include "net/base/net_util.h"
16 #include "ui/base/clipboard/clipboard_util_win.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
19 #include "ui/gfx/point.h"
20 #include "webkit/glue/webdropdata.h"
21 #include "webkit/glue/window_open_disposition.h"
22
23 using WebKit::WebDragOperationNone;
24 using WebKit::WebDragOperationCopy;
25 using WebKit::WebDragOperationLink;
26 using WebKit::WebDragOperationMove;
27 using WebKit::WebDragOperationGeneric;
28
29 namespace {
30
31 // A helper method for getting the preferred drop effect.
GetPreferredDropEffect(DWORD effect)32 DWORD GetPreferredDropEffect(DWORD effect) {
33 if (effect & DROPEFFECT_COPY)
34 return DROPEFFECT_COPY;
35 if (effect & DROPEFFECT_LINK)
36 return DROPEFFECT_LINK;
37 if (effect & DROPEFFECT_MOVE)
38 return DROPEFFECT_MOVE;
39 return DROPEFFECT_NONE;
40 }
41
42 } // namespace
43
44 // InterstitialDropTarget is like a app::win::DropTarget implementation that
45 // WebDropTarget passes through to if an interstitial is showing. Rather than
46 // passing messages on to the renderer, we just check to see if there's a link
47 // in the drop data and handle links as navigations.
48 class InterstitialDropTarget {
49 public:
InterstitialDropTarget(TabContents * tab_contents)50 explicit InterstitialDropTarget(TabContents* tab_contents)
51 : tab_contents_(tab_contents) {}
52
OnDragEnter(IDataObject * data_object,DWORD effect)53 DWORD OnDragEnter(IDataObject* data_object, DWORD effect) {
54 return ui::ClipboardUtil::HasUrl(data_object) ?
55 GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
56 }
57
OnDragOver(IDataObject * data_object,DWORD effect)58 DWORD OnDragOver(IDataObject* data_object, DWORD effect) {
59 return ui::ClipboardUtil::HasUrl(data_object) ?
60 GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
61 }
62
OnDragLeave(IDataObject * data_object)63 void OnDragLeave(IDataObject* data_object) {
64 }
65
OnDrop(IDataObject * data_object,DWORD effect)66 DWORD OnDrop(IDataObject* data_object, DWORD effect) {
67 if (ui::ClipboardUtil::HasUrl(data_object)) {
68 std::wstring url;
69 std::wstring title;
70 ui::ClipboardUtil::GetUrl(data_object, &url, &title, true);
71 tab_contents_->OpenURL(GURL(url), GURL(), CURRENT_TAB,
72 PageTransition::AUTO_BOOKMARK);
73 return GetPreferredDropEffect(effect);
74 }
75 return DROPEFFECT_NONE;
76 }
77
78 private:
79 TabContents* tab_contents_;
80
81 DISALLOW_COPY_AND_ASSIGN(InterstitialDropTarget);
82 };
83
WebDropTarget(HWND source_hwnd,TabContents * tab_contents)84 WebDropTarget::WebDropTarget(HWND source_hwnd, TabContents* tab_contents)
85 : ui::DropTarget(source_hwnd),
86 tab_contents_(tab_contents),
87 current_rvh_(NULL),
88 drag_cursor_(WebDragOperationNone),
89 interstitial_drop_target_(new InterstitialDropTarget(tab_contents)) {
90 }
91
~WebDropTarget()92 WebDropTarget::~WebDropTarget() {
93 }
94
OnDragEnter(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effects)95 DWORD WebDropTarget::OnDragEnter(IDataObject* data_object,
96 DWORD key_state,
97 POINT cursor_position,
98 DWORD effects) {
99 current_rvh_ = tab_contents_->render_view_host();
100
101 // Don't pass messages to the renderer if an interstitial page is showing
102 // because we don't want the interstitial page to navigate. Instead,
103 // pass the messages on to a separate interstitial DropTarget handler.
104 if (tab_contents_->showing_interstitial_page())
105 return interstitial_drop_target_->OnDragEnter(data_object, effects);
106
107 // TODO(tc): PopulateWebDropData can be slow depending on what is in the
108 // IDataObject. Maybe we can do this in a background thread.
109 WebDropData drop_data;
110 WebDropData::PopulateWebDropData(data_object, &drop_data);
111
112 if (drop_data.url.is_empty())
113 ui::OSExchangeDataProviderWin::GetPlainTextURL(data_object, &drop_data.url);
114
115 drag_cursor_ = WebDragOperationNone;
116
117 POINT client_pt = cursor_position;
118 ScreenToClient(GetHWND(), &client_pt);
119 tab_contents_->render_view_host()->DragTargetDragEnter(drop_data,
120 gfx::Point(client_pt.x, client_pt.y),
121 gfx::Point(cursor_position.x, cursor_position.y),
122 web_drag_utils_win::WinDragOpMaskToWebDragOpMask(effects));
123
124 // This is non-null if tab_contents_ is showing an ExtensionWebUI with
125 // support for (at the moment experimental) drag and drop extensions.
126 if (tab_contents_->GetBookmarkDragDelegate()) {
127 ui::OSExchangeData os_exchange_data(
128 new ui::OSExchangeDataProviderWin(data_object));
129 BookmarkNodeData bookmark_drag_data;
130 if (bookmark_drag_data.Read(os_exchange_data))
131 tab_contents_->GetBookmarkDragDelegate()->OnDragEnter(bookmark_drag_data);
132 }
133
134 // We lie here and always return a DROPEFFECT because we don't want to
135 // wait for the IPC call to return.
136 return web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
137 }
138
OnDragOver(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effects)139 DWORD WebDropTarget::OnDragOver(IDataObject* data_object,
140 DWORD key_state,
141 POINT cursor_position,
142 DWORD effects) {
143 DCHECK(current_rvh_);
144 if (current_rvh_ != tab_contents_->render_view_host())
145 OnDragEnter(data_object, key_state, cursor_position, effects);
146
147 if (tab_contents_->showing_interstitial_page())
148 return interstitial_drop_target_->OnDragOver(data_object, effects);
149
150 POINT client_pt = cursor_position;
151 ScreenToClient(GetHWND(), &client_pt);
152 tab_contents_->render_view_host()->DragTargetDragOver(
153 gfx::Point(client_pt.x, client_pt.y),
154 gfx::Point(cursor_position.x, cursor_position.y),
155 web_drag_utils_win::WinDragOpMaskToWebDragOpMask(effects));
156
157 if (tab_contents_->GetBookmarkDragDelegate()) {
158 ui::OSExchangeData os_exchange_data(
159 new ui::OSExchangeDataProviderWin(data_object));
160 BookmarkNodeData bookmark_drag_data;
161 if (bookmark_drag_data.Read(os_exchange_data))
162 tab_contents_->GetBookmarkDragDelegate()->OnDragOver(bookmark_drag_data);
163 }
164
165 return web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
166 }
167
OnDragLeave(IDataObject * data_object)168 void WebDropTarget::OnDragLeave(IDataObject* data_object) {
169 DCHECK(current_rvh_);
170 if (current_rvh_ != tab_contents_->render_view_host())
171 return;
172
173 if (tab_contents_->showing_interstitial_page()) {
174 interstitial_drop_target_->OnDragLeave(data_object);
175 } else {
176 tab_contents_->render_view_host()->DragTargetDragLeave();
177 }
178
179 if (tab_contents_->GetBookmarkDragDelegate()) {
180 ui::OSExchangeData os_exchange_data(
181 new ui::OSExchangeDataProviderWin(data_object));
182 BookmarkNodeData bookmark_drag_data;
183 if (bookmark_drag_data.Read(os_exchange_data))
184 tab_contents_->GetBookmarkDragDelegate()->OnDragLeave(bookmark_drag_data);
185 }
186 }
187
OnDrop(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effect)188 DWORD WebDropTarget::OnDrop(IDataObject* data_object,
189 DWORD key_state,
190 POINT cursor_position,
191 DWORD effect) {
192 DCHECK(current_rvh_);
193 if (current_rvh_ != tab_contents_->render_view_host())
194 OnDragEnter(data_object, key_state, cursor_position, effect);
195
196 if (tab_contents_->showing_interstitial_page())
197 interstitial_drop_target_->OnDragOver(data_object, effect);
198
199 if (tab_contents_->showing_interstitial_page())
200 return interstitial_drop_target_->OnDrop(data_object, effect);
201
202 POINT client_pt = cursor_position;
203 ScreenToClient(GetHWND(), &client_pt);
204 tab_contents_->render_view_host()->DragTargetDrop(
205 gfx::Point(client_pt.x, client_pt.y),
206 gfx::Point(cursor_position.x, cursor_position.y));
207
208 if (tab_contents_->GetBookmarkDragDelegate()) {
209 ui::OSExchangeData os_exchange_data(
210 new ui::OSExchangeDataProviderWin(data_object));
211 BookmarkNodeData bookmark_drag_data;
212 if (bookmark_drag_data.Read(os_exchange_data))
213 tab_contents_->GetBookmarkDragDelegate()->OnDrop(bookmark_drag_data);
214 }
215
216 current_rvh_ = NULL;
217
218 // This isn't always correct, but at least it's a close approximation.
219 // For now, we always map a move to a copy to prevent potential data loss.
220 DWORD drop_effect = web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
221 return drop_effect != DROPEFFECT_MOVE ? drop_effect : DROPEFFECT_COPY;
222 }
223