Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: chrome/browser/views/tab_contents/tab_contents_drag_win.cc

Issue 351029: Support dragging a virtual file out of the browser. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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/views/tab_contents/tab_contents_drag_win.h"
6
7 #include <windows.h>
8
9 #include "base/file_path.h"
10 #include "base/message_loop.h"
11 #include "base/task.h"
12 #include "base/thread.h"
13 #include "base/win_util.h"
14 #include "chrome/browser/bookmarks/bookmark_drag_data.h"
15 #include "chrome/browser/chrome_thread.h"
16 #include "chrome/browser/download/drag_download_file_win.h"
17 #include "chrome/browser/profile.h"
18 #include "chrome/browser/tab_contents/tab_contents.h"
19 #include "chrome/browser/tab_contents/web_drag_source_win.h"
20 #include "chrome/browser/tab_contents/web_drop_target_win.h"
21 #include "chrome/browser/views/tab_contents/tab_contents_view_win.h"
22 #include "chrome/common/url_constants.h"
23 #include "net/base/net_util.h"
24 #include "webkit/glue/webdropdata.h"
25
26 using WebKit::WebDragOperationsMask;
27
28 namespace {
29
30 HHOOK msg_hook = NULL;
31 DWORD drag_out_thread_id = 0;
32 bool mouse_up_received = false;
33
34 LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
35 if (code == base::MessagePumpForUI::kMessageFilterCode &&
36 !mouse_up_received) {
37 MSG* msg = reinterpret_cast<MSG*>(lparam);
38 // We do not care about WM_SYSKEYDOWN and WM_SYSKEYUP because when ALT key
39 // is pressed down on drag-and-drop, it means to create a link.
40 if (msg->message == WM_MOUSEMOVE || msg->message == WM_LBUTTONUP ||
41 msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
42 // Forward the message from the UI thread to the drag-and-drop thread.
43 PostThreadMessage(drag_out_thread_id,
44 msg->message,
45 msg->wParam,
46 msg->lParam);
47
48 // If the left button is up, we do not need to forward the message any
49 // more.
50 if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000))
51 mouse_up_received = true;
52
53 return TRUE;
54 }
55 }
56 return CallNextHookEx(msg_hook, code, wparam, lparam);
57 }
58
59 } // namespace
60
61 class DragDropThread : public base::Thread {
62 public:
63 explicit DragDropThread(TabContentsDragWin* drag_handler)
64 : base::Thread("Chrome_DragDropThread"),
65 drag_handler_(drag_handler) {
66 }
67
68 virtual ~DragDropThread() {
69 Thread::Stop();
70 }
71
72 protected:
73 // base::Thread implementations:
74 virtual void Init() {
75 int ole_result = OleInitialize(NULL);
76 DCHECK(ole_result == S_OK);
77 }
78
79 virtual void CleanUp() {
80 OleUninitialize();
81 }
82
83 private:
84 // Hold a reference count to TabContentsDragWin to make sure that it is always
85 // alive in the thread lifetime.
86 scoped_refptr<TabContentsDragWin> drag_handler_;
87
88 DISALLOW_COPY_AND_ASSIGN(DragDropThread);
89 };
90
91 TabContentsDragWin::TabContentsDragWin(TabContentsViewWin* view)
92 : view_(view),
93 drag_ended_(false),
94 old_drop_target_suspended_state_(false) {
95 #ifndef NDEBUG
96 drag_drop_thread_id_ = 0;
97 #endif
98 }
99
100 TabContentsDragWin::~TabContentsDragWin() {
101 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
102 DCHECK(!drag_source_.get());
103 DCHECK(!drag_drop_thread_.get());
104 }
105
106 void TabContentsDragWin::StartDragging(const WebDropData& drop_data,
107 WebDragOperationsMask ops) {
108 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
109
110 drag_source_ = new WebDragSource(view_->GetNativeView(),
111 view_->tab_contents());
112
113 const GURL& page_url = view_->tab_contents()->GetURL();
114 const std::string& page_encoding = view_->tab_contents()->encoding();
115
116 // If it is not drag-out, do the drag-and-drop in the current UI thread.
117 if (!drop_data.download_url.is_valid()) {
118 DoDragging(drop_data, ops, page_url, page_encoding);
119 EndDragging(false);
120 return;
121 }
122
123 // We do not want to drag and drop the download to itself.
124 old_drop_target_suspended_state_ = view_->drop_target()->suspended();
125 view_->drop_target()->set_suspended(true);
126
127 // Start a background thread to do the drag-and-drop.
128 DCHECK(!drag_drop_thread_.get());
129 drag_drop_thread_.reset(new DragDropThread(this));
130 base::Thread::Options options;
131 options.message_loop_type = MessageLoop::TYPE_UI;
132 if (drag_drop_thread_->StartWithOptions(options)) {
133 drag_drop_thread_->message_loop()->PostTask(
134 FROM_HERE,
135 NewRunnableMethod(this,
136 &TabContentsDragWin::StartBackgroundDragging,
137 drop_data,
138 ops,
139 page_url,
140 page_encoding));
141 }
142
143 // Install a hook procedure to monitor the messages so that we can forward
144 // the appropriate ones to the background thread.
145 drag_out_thread_id = drag_drop_thread_->thread_id();
146 mouse_up_received = false;
147 DCHECK(!msg_hook);
148 msg_hook = SetWindowsHookEx(WH_MSGFILTER,
149 MsgFilterProc,
150 NULL,
151 GetCurrentThreadId());
152
153 // Attach the input state of the background thread to the UI thread so that
154 // SetCursor can work from the background thread.
155 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE);
156 }
157
158 void TabContentsDragWin::StartBackgroundDragging(
159 const WebDropData& drop_data,
160 WebDragOperationsMask ops,
161 const GURL& page_url,
162 const std::string& page_encoding) {
163 #ifndef NDEBUG
164 drag_drop_thread_id_ = PlatformThread::CurrentId();
165 #endif
166
167 DoDragging(drop_data, ops, page_url, page_encoding);
168 ChromeThread::PostTask(
169 ChromeThread::UI, FROM_HERE,
170 NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true));
171 }
172
173 void TabContentsDragWin::PrepareDragForDownload(
174 const WebDropData& drop_data,
175 OSExchangeData* data,
176 const GURL& page_url,
177 const std::string& page_encoding) {
178 // Provide the data as file (CF_HDROP). A temporary download file with the
179 // Zone.Identifier ADS (Alternate Data Stream) attached will be created.
180 scoped_refptr<DragDownloadFile> download_file =
181 new DragDownloadFile(drop_data.download_url,
182 page_url,
183 page_encoding,
184 view_->tab_contents());
185 OSExchangeData::DownloadFileInfo* file_download =
186 new OSExchangeData::DownloadFileInfo(FilePath(),
187 0,
188 download_file.get());
189 data->SetDownloadFileInfo(file_download);
190
191 // Enable asynchronous operation.
192 OSExchangeDataProviderWin::GetIAsyncOperation(*data)->SetAsyncMode(TRUE);
193 }
194
195 void TabContentsDragWin::PrepareDragForFileContents(
196 const WebDropData& drop_data, OSExchangeData* data) {
197 // Images without ALT text will only have a file extension so we need to
198 // synthesize one from the provided extension and URL.
199 FilePath file_name(drop_data.file_description_filename);
200 file_name = file_name.BaseName().RemoveExtension();
201 if (file_name.value().empty()) {
202 // Retrieve the name from the URL.
203 file_name = net::GetSuggestedFilename(drop_data.url, "", "", FilePath());
204 if (file_name.value().size() + drop_data.file_extension.size() + 1 >
205 MAX_PATH) {
206 file_name = FilePath(file_name.value().substr(
207 0, MAX_PATH - drop_data.file_extension.size() - 2));
208 }
209 }
210 file_name = file_name.ReplaceExtension(drop_data.file_extension);
211 data->SetFileContents(file_name.value(), drop_data.file_contents);
212 }
213
214 void TabContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data,
215 OSExchangeData* data) {
216 if (drop_data.url.SchemeIs(chrome::kJavaScriptScheme)) {
217 // We don't want to allow javascript URLs to be dragged to the desktop,
218 // but we do want to allow them to be added to the bookmarks bar
219 // (bookmarklets). So we create a fake bookmark entry (BookmarkDragData
220 // object) which explorer.exe cannot handle, and write the entry to data.
221 BookmarkDragData::Element bm_elt;
222 bm_elt.is_url = true;
223 bm_elt.url = drop_data.url;
224 bm_elt.title = drop_data.url_title;
225
226 BookmarkDragData bm_drag_data;
227 bm_drag_data.elements.push_back(bm_elt);
228
229 // Pass in NULL as the profile so that the bookmark always adds the url
230 // rather than trying to move an existing url.
231 bm_drag_data.Write(NULL, data);
232 } else {
233 data->SetURL(drop_data.url, drop_data.url_title);
234 }
235 }
236
237 void TabContentsDragWin::DoDragging(const WebDropData& drop_data,
238 WebDragOperationsMask ops,
239 const GURL& page_url,
240 const std::string& page_encoding) {
241 OSExchangeData data;
242
243 // TODO(tc): Generate an appropriate drag image.
244
245 if (drop_data.download_url.is_valid()) {
246 PrepareDragForDownload(drop_data, &data, page_url, page_encoding);
247
248 // Set the observer.
249 OSExchangeDataProviderWin::GetDataObjectImpl(data)->set_observer(this);
250 } else {
251 // We set the file contents before the URL because the URL also sets file
252 // contents (to a .URL shortcut). We want to prefer file content data over
253 // a shortcut so we add it first.
254 if (!drop_data.file_contents.empty())
255 PrepareDragForFileContents(drop_data, &data);
256 if (!drop_data.text_html.empty())
257 data.SetHtml(drop_data.text_html, drop_data.html_base_url);
258 if (drop_data.url.is_valid())
259 PrepareDragForUrl(drop_data, &data);
260 if (!drop_data.plain_text.empty())
261 data.SetString(drop_data.plain_text);
262 }
263
264 DWORD effects = 0;
265
266 // We need to enable recursive tasks on the message loop so we can get
267 // updates while in the system DoDragDrop loop.
268 bool old_state = MessageLoop::current()->NestableTasksAllowed();
269 MessageLoop::current()->SetNestableTasksAllowed(true);
270 DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source_,
271 DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
272 // TODO(snej): Use 'ops' param instead of hardcoding dropeffects
273 MessageLoop::current()->SetNestableTasksAllowed(old_state);
274 }
275
276 void TabContentsDragWin::EndDragging(bool restore_suspended_state) {
277 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
278
279 if (drag_ended_)
280 return;
281 drag_ended_ = true;
282
283 if (restore_suspended_state)
284 view_->drop_target()->set_suspended(old_drop_target_suspended_state_);
285
286 drag_source_ = NULL;
287
288 if (msg_hook) {
289 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE);
290 UnhookWindowsHookEx(msg_hook);
291 msg_hook = NULL;
292 }
293
294 view_->EndDragging();
295 }
296
297 void TabContentsDragWin::CancelDrag() {
298 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
299
300 drag_source_->CancelDrag();
301 }
302
303 void TabContentsDragWin::CloseThread() {
304 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
305
306 drag_drop_thread_.reset();
307 }
308
309 void TabContentsDragWin::OnWaitForData() {
310 DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId());
311
312 // When the left button is release and we start to wait for the data, end
313 // the dragging before DoDragDrop returns. This makes the page leave the drag
314 // mode so that it can start to process the normal input events.
315 ChromeThread::PostTask(
316 ChromeThread::UI, FROM_HERE,
317 NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true));
318 }
319
320 void TabContentsDragWin::OnDataObjectDisposed() {
321 DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId());
322
323 // The drag-and-drop thread is only closed after OLE is done with
324 // DataObjectImpl.
325 ChromeThread::PostTask(
326 ChromeThread::UI, FROM_HERE,
327 NewRunnableMethod(this, &TabContentsDragWin::CloseThread));
328 }
OLDNEW
« no previous file with comments | « chrome/browser/views/tab_contents/tab_contents_drag_win.h ('k') | chrome/browser/views/tab_contents/tab_contents_view_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698