OLD | NEW |
| (Empty) |
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 "content/browser/web_contents/web_drag_dest_win.h" | |
6 | |
7 #include <windows.h> | |
8 #include <shlobj.h> | |
9 | |
10 #include "base/win/win_util.h" | |
11 #include "content/browser/renderer_host/render_view_host_impl.h" | |
12 #include "content/browser/web_contents/web_drag_utils_win.h" | |
13 #include "content/public/browser/web_contents.h" | |
14 #include "content/public/browser/web_contents_delegate.h" | |
15 #include "content/public/browser/web_drag_dest_delegate.h" | |
16 #include "content/public/common/drop_data.h" | |
17 #include "net/base/net_util.h" | |
18 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
19 #include "ui/base/clipboard/clipboard_util_win.h" | |
20 #include "ui/base/dragdrop/os_exchange_data.h" | |
21 #include "ui/base/dragdrop/os_exchange_data_provider_win.h" | |
22 #include "ui/base/window_open_disposition.h" | |
23 #include "ui/gfx/point.h" | |
24 #include "url/gurl.h" | |
25 | |
26 using blink::WebDragOperationNone; | |
27 using blink::WebDragOperationCopy; | |
28 using blink::WebDragOperationLink; | |
29 using blink::WebDragOperationMove; | |
30 using blink::WebDragOperationGeneric; | |
31 | |
32 namespace content { | |
33 namespace { | |
34 | |
35 const unsigned short kHighBitMaskShort = 0x8000; | |
36 | |
37 // A helper method for getting the preferred drop effect. | |
38 DWORD GetPreferredDropEffect(DWORD effect) { | |
39 if (effect & DROPEFFECT_COPY) | |
40 return DROPEFFECT_COPY; | |
41 if (effect & DROPEFFECT_LINK) | |
42 return DROPEFFECT_LINK; | |
43 if (effect & DROPEFFECT_MOVE) | |
44 return DROPEFFECT_MOVE; | |
45 return DROPEFFECT_NONE; | |
46 } | |
47 | |
48 int GetModifierFlags() { | |
49 int modifier_state = 0; | |
50 if (base::win::IsShiftPressed()) | |
51 modifier_state |= blink::WebInputEvent::ShiftKey; | |
52 if (base::win::IsCtrlPressed()) | |
53 modifier_state |= blink::WebInputEvent::ControlKey; | |
54 if (base::win::IsAltPressed()) | |
55 modifier_state |= blink::WebInputEvent::AltKey; | |
56 if (::GetKeyState(VK_LWIN) & kHighBitMaskShort) | |
57 modifier_state |= blink::WebInputEvent::MetaKey; | |
58 if (::GetKeyState(VK_RWIN) & kHighBitMaskShort) | |
59 modifier_state |= blink::WebInputEvent::MetaKey; | |
60 return modifier_state; | |
61 } | |
62 | |
63 // Helper method for converting Window's specific IDataObject to a DropData | |
64 // object. | |
65 void PopulateDropData(IDataObject* data_object, DropData* drop_data) { | |
66 base::string16 url_str; | |
67 if (ui::ClipboardUtil::GetUrl( | |
68 data_object, &url_str, &drop_data->url_title, false)) { | |
69 GURL test_url(url_str); | |
70 if (test_url.is_valid()) | |
71 drop_data->url = test_url; | |
72 } | |
73 std::vector<base::string16> filenames; | |
74 ui::ClipboardUtil::GetFilenames(data_object, &filenames); | |
75 for (size_t i = 0; i < filenames.size(); ++i) | |
76 drop_data->filenames.push_back( | |
77 DropData::FileInfo(filenames[i], base::string16())); | |
78 base::string16 text; | |
79 ui::ClipboardUtil::GetPlainText(data_object, &text); | |
80 if (!text.empty()) { | |
81 drop_data->text = base::NullableString16(text, false); | |
82 } | |
83 base::string16 html; | |
84 std::string html_base_url; | |
85 ui::ClipboardUtil::GetHtml(data_object, &html, &html_base_url); | |
86 if (!html.empty()) { | |
87 drop_data->html = base::NullableString16(html, false); | |
88 } | |
89 if (!html_base_url.empty()) { | |
90 drop_data->html_base_url = GURL(html_base_url); | |
91 } | |
92 ui::ClipboardUtil::GetWebCustomData(data_object, &drop_data->custom_data); | |
93 } | |
94 | |
95 } // namespace | |
96 | |
97 // InterstitialDropTarget is like a ui::DropTargetWin implementation that | |
98 // WebDragDest passes through to if an interstitial is showing. Rather than | |
99 // passing messages on to the renderer, we just check to see if there's a link | |
100 // in the drop data and handle links as navigations. | |
101 class InterstitialDropTarget { | |
102 public: | |
103 explicit InterstitialDropTarget(WebContents* web_contents) | |
104 : web_contents_(web_contents) {} | |
105 | |
106 DWORD OnDragEnter(IDataObject* data_object, DWORD effect) { | |
107 return ui::ClipboardUtil::HasUrl(data_object) ? | |
108 GetPreferredDropEffect(effect) : DROPEFFECT_NONE; | |
109 } | |
110 | |
111 DWORD OnDragOver(IDataObject* data_object, DWORD effect) { | |
112 return ui::ClipboardUtil::HasUrl(data_object) ? | |
113 GetPreferredDropEffect(effect) : DROPEFFECT_NONE; | |
114 } | |
115 | |
116 void OnDragLeave(IDataObject* data_object) { | |
117 } | |
118 | |
119 DWORD OnDrop(IDataObject* data_object, DWORD effect) { | |
120 if (!ui::ClipboardUtil::HasUrl(data_object)) | |
121 return DROPEFFECT_NONE; | |
122 | |
123 std::wstring url; | |
124 std::wstring title; | |
125 ui::ClipboardUtil::GetUrl(data_object, &url, &title, true); | |
126 OpenURLParams params(GURL(url), Referrer(), CURRENT_TAB, | |
127 PAGE_TRANSITION_AUTO_BOOKMARK, false); | |
128 web_contents_->OpenURL(params); | |
129 return GetPreferredDropEffect(effect); | |
130 } | |
131 | |
132 private: | |
133 WebContents* web_contents_; | |
134 | |
135 DISALLOW_COPY_AND_ASSIGN(InterstitialDropTarget); | |
136 }; | |
137 | |
138 WebDragDest::WebDragDest(HWND source_hwnd, WebContents* web_contents) | |
139 : ui::DropTargetWin(source_hwnd), | |
140 web_contents_(web_contents), | |
141 current_rvh_(NULL), | |
142 drag_cursor_(WebDragOperationNone), | |
143 interstitial_drop_target_(new InterstitialDropTarget(web_contents)), | |
144 delegate_(NULL), | |
145 canceled_(false) { | |
146 } | |
147 | |
148 WebDragDest::~WebDragDest() { | |
149 } | |
150 | |
151 DWORD WebDragDest::OnDragEnter(IDataObject* data_object, | |
152 DWORD key_state, | |
153 POINT cursor_position, | |
154 DWORD effects) { | |
155 current_rvh_ = web_contents_->GetRenderViewHost(); | |
156 | |
157 // TODO(tc): PopulateDropData can be slow depending on what is in the | |
158 // IDataObject. Maybe we can do this in a background thread. | |
159 scoped_ptr<DropData> drop_data; | |
160 drop_data.reset(new DropData()); | |
161 PopulateDropData(data_object, drop_data.get()); | |
162 | |
163 if (drop_data->url.is_empty()) | |
164 ui::OSExchangeDataProviderWin::GetPlainTextURL(data_object, | |
165 &drop_data->url); | |
166 | |
167 // Give the delegate an opportunity to cancel the drag. | |
168 canceled_ = !web_contents_->GetDelegate()->CanDragEnter( | |
169 web_contents_, | |
170 *drop_data, | |
171 WinDragOpMaskToWebDragOpMask(effects)); | |
172 if (canceled_) | |
173 return DROPEFFECT_NONE; | |
174 | |
175 if (delegate_) | |
176 delegate_->DragInitialize(web_contents_); | |
177 | |
178 // Don't pass messages to the renderer if an interstitial page is showing | |
179 // because we don't want the interstitial page to navigate. Instead, | |
180 // pass the messages on to a separate interstitial DropTarget handler. | |
181 if (web_contents_->ShowingInterstitialPage()) | |
182 return interstitial_drop_target_->OnDragEnter(data_object, effects); | |
183 | |
184 drop_data_.swap(drop_data); | |
185 drag_cursor_ = WebDragOperationNone; | |
186 | |
187 POINT client_pt = cursor_position; | |
188 ScreenToClient(GetHWND(), &client_pt); | |
189 web_contents_->GetRenderViewHost()->DragTargetDragEnter(*drop_data_, | |
190 gfx::Point(client_pt.x, client_pt.y), | |
191 gfx::Point(cursor_position.x, cursor_position.y), | |
192 WinDragOpMaskToWebDragOpMask(effects), | |
193 GetModifierFlags()); | |
194 | |
195 if (delegate_) | |
196 delegate_->OnDragEnter(data_object); | |
197 | |
198 // We lie here and always return a DROPEFFECT because we don't want to | |
199 // wait for the IPC call to return. | |
200 return WebDragOpToWinDragOp(drag_cursor_); | |
201 } | |
202 | |
203 DWORD WebDragDest::OnDragOver(IDataObject* data_object, | |
204 DWORD key_state, | |
205 POINT cursor_position, | |
206 DWORD effects) { | |
207 DCHECK(current_rvh_); | |
208 if (current_rvh_ != web_contents_->GetRenderViewHost()) | |
209 OnDragEnter(data_object, key_state, cursor_position, effects); | |
210 | |
211 if (canceled_) | |
212 return DROPEFFECT_NONE; | |
213 | |
214 if (web_contents_->ShowingInterstitialPage()) | |
215 return interstitial_drop_target_->OnDragOver(data_object, effects); | |
216 | |
217 POINT client_pt = cursor_position; | |
218 ScreenToClient(GetHWND(), &client_pt); | |
219 web_contents_->GetRenderViewHost()->DragTargetDragOver( | |
220 gfx::Point(client_pt.x, client_pt.y), | |
221 gfx::Point(cursor_position.x, cursor_position.y), | |
222 WinDragOpMaskToWebDragOpMask(effects), | |
223 GetModifierFlags()); | |
224 | |
225 if (delegate_) | |
226 delegate_->OnDragOver(data_object); | |
227 | |
228 return WebDragOpToWinDragOp(drag_cursor_); | |
229 } | |
230 | |
231 void WebDragDest::OnDragLeave(IDataObject* data_object) { | |
232 DCHECK(current_rvh_); | |
233 if (current_rvh_ != web_contents_->GetRenderViewHost()) | |
234 return; | |
235 | |
236 if (canceled_) | |
237 return; | |
238 | |
239 if (web_contents_->ShowingInterstitialPage()) { | |
240 interstitial_drop_target_->OnDragLeave(data_object); | |
241 } else { | |
242 web_contents_->GetRenderViewHost()->DragTargetDragLeave(); | |
243 } | |
244 | |
245 if (delegate_) | |
246 delegate_->OnDragLeave(data_object); | |
247 | |
248 drop_data_.reset(); | |
249 } | |
250 | |
251 DWORD WebDragDest::OnDrop(IDataObject* data_object, | |
252 DWORD key_state, | |
253 POINT cursor_position, | |
254 DWORD effect) { | |
255 DCHECK(current_rvh_); | |
256 if (current_rvh_ != web_contents_->GetRenderViewHost()) | |
257 OnDragEnter(data_object, key_state, cursor_position, effect); | |
258 | |
259 if (web_contents_->ShowingInterstitialPage()) | |
260 interstitial_drop_target_->OnDragOver(data_object, effect); | |
261 | |
262 if (web_contents_->ShowingInterstitialPage()) | |
263 return interstitial_drop_target_->OnDrop(data_object, effect); | |
264 | |
265 POINT client_pt = cursor_position; | |
266 ScreenToClient(GetHWND(), &client_pt); | |
267 web_contents_->GetRenderViewHost()->DragTargetDrop( | |
268 gfx::Point(client_pt.x, client_pt.y), | |
269 gfx::Point(cursor_position.x, cursor_position.y), | |
270 GetModifierFlags()); | |
271 | |
272 if (delegate_) | |
273 delegate_->OnDrop(data_object); | |
274 | |
275 current_rvh_ = NULL; | |
276 | |
277 // This isn't always correct, but at least it's a close approximation. | |
278 // For now, we always map a move to a copy to prevent potential data loss. | |
279 DWORD drop_effect = WebDragOpToWinDragOp(drag_cursor_); | |
280 DWORD result = drop_effect != DROPEFFECT_MOVE ? drop_effect : DROPEFFECT_COPY; | |
281 | |
282 drop_data_.reset(); | |
283 return result; | |
284 } | |
285 | |
286 } // namespace content | |
OLD | NEW |