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

Side by Side Diff: content/browser/tab_contents/web_contents_drag_win.cc

Issue 10024066: TabContents -> WebContentsImpl, part 4. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 8 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
OLDNEW
(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/tab_contents/web_contents_drag_win.h"
6
7 #include <windows.h>
8
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/file_path.h"
13 #include "base/message_loop.h"
14 #include "base/pickle.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/threading/thread.h"
17 #include "base/utf_string_conversions.h"
18 #include "content/browser/download/drag_download_file.h"
19 #include "content/browser/download/drag_download_util.h"
20 #include "content/browser/web_contents/web_drag_dest_win.h"
21 #include "content/browser/web_contents/web_drag_source_win.h"
22 #include "content/browser/web_contents/web_drag_utils_win.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/content_browser_client.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_drag_dest_delegate.h"
27 #include "net/base/net_util.h"
28 #include "ui/base/clipboard/clipboard_util_win.h"
29 #include "ui/base/clipboard/custom_data_helper.h"
30 #include "ui/base/dragdrop/drag_utils.h"
31 #include "ui/gfx/size.h"
32 #include "webkit/glue/webdropdata.h"
33
34 using content::BrowserThread;
35 using WebKit::WebDragOperationsMask;
36 using WebKit::WebDragOperationCopy;
37 using WebKit::WebDragOperationLink;
38 using WebKit::WebDragOperationMove;
39
40 namespace {
41
42 HHOOK msg_hook = NULL;
43 DWORD drag_out_thread_id = 0;
44 bool mouse_up_received = false;
45
46 LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
47 if (code == base::MessagePumpForUI::kMessageFilterCode &&
48 !mouse_up_received) {
49 MSG* msg = reinterpret_cast<MSG*>(lparam);
50 // We do not care about WM_SYSKEYDOWN and WM_SYSKEYUP because when ALT key
51 // is pressed down on drag-and-drop, it means to create a link.
52 if (msg->message == WM_MOUSEMOVE || msg->message == WM_LBUTTONUP ||
53 msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
54 // Forward the message from the UI thread to the drag-and-drop thread.
55 PostThreadMessage(drag_out_thread_id,
56 msg->message,
57 msg->wParam,
58 msg->lParam);
59
60 // If the left button is up, we do not need to forward the message any
61 // more.
62 if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000))
63 mouse_up_received = true;
64
65 return TRUE;
66 }
67 }
68 return CallNextHookEx(msg_hook, code, wparam, lparam);
69 }
70
71 } // namespace
72
73 class DragDropThread : public base::Thread {
74 public:
75 explicit DragDropThread(WebContentsDragWin* drag_handler)
76 : base::Thread("Chrome_DragDropThread"),
77 drag_handler_(drag_handler) {
78 }
79
80 virtual ~DragDropThread() {
81 Thread::Stop();
82 }
83
84 protected:
85 // base::Thread implementations:
86 virtual void Init() {
87 int ole_result = OleInitialize(NULL);
88 DCHECK(ole_result == S_OK);
89 }
90
91 virtual void CleanUp() {
92 OleUninitialize();
93 }
94
95 private:
96 // Hold a reference count to WebContentsDragWin to make sure that it is always
97 // alive in the thread lifetime.
98 scoped_refptr<WebContentsDragWin> drag_handler_;
99
100 DISALLOW_COPY_AND_ASSIGN(DragDropThread);
101 };
102
103 WebContentsDragWin::WebContentsDragWin(
104 gfx::NativeWindow source_window,
105 content::WebContents* web_contents,
106 WebDragDest* drag_dest,
107 const base::Callback<void()>& drag_end_callback)
108 : drag_drop_thread_id_(0),
109 source_window_(source_window),
110 web_contents_(web_contents),
111 drag_dest_(drag_dest),
112 drag_ended_(false),
113 old_drop_target_suspended_state_(false),
114 drag_end_callback_(drag_end_callback) {
115 }
116
117 WebContentsDragWin::~WebContentsDragWin() {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
119 DCHECK(!drag_drop_thread_.get());
120 }
121
122 void WebContentsDragWin::StartDragging(const WebDropData& drop_data,
123 WebDragOperationsMask ops,
124 const SkBitmap& image,
125 const gfx::Point& image_offset) {
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
127
128 drag_source_ = new WebDragSource(source_window_, web_contents_);
129
130 const GURL& page_url = web_contents_->GetURL();
131 const std::string& page_encoding = web_contents_->GetEncoding();
132
133 // If it is not drag-out, do the drag-and-drop in the current UI thread.
134 if (drop_data.download_metadata.empty()) {
135 DoDragging(drop_data, ops, page_url, page_encoding, image, image_offset);
136 EndDragging(false);
137 return;
138 }
139
140 // We do not want to drag and drop the download to itself.
141 old_drop_target_suspended_state_ = drag_dest_->suspended();
142 drag_dest_->set_suspended(true);
143
144 // Start a background thread to do the drag-and-drop.
145 DCHECK(!drag_drop_thread_.get());
146 drag_drop_thread_.reset(new DragDropThread(this));
147 base::Thread::Options options;
148 options.message_loop_type = MessageLoop::TYPE_UI;
149 if (drag_drop_thread_->StartWithOptions(options)) {
150 drag_drop_thread_->message_loop()->PostTask(
151 FROM_HERE,
152 base::Bind(&WebContentsDragWin::StartBackgroundDragging, this,
153 drop_data, ops, page_url, page_encoding, image,
154 image_offset));
155 }
156
157 // Install a hook procedure to monitor the messages so that we can forward
158 // the appropriate ones to the background thread.
159 drag_out_thread_id = drag_drop_thread_->thread_id();
160 mouse_up_received = false;
161 DCHECK(!msg_hook);
162 msg_hook = SetWindowsHookEx(WH_MSGFILTER,
163 MsgFilterProc,
164 NULL,
165 GetCurrentThreadId());
166
167 // Attach the input state of the background thread to the UI thread so that
168 // SetCursor can work from the background thread.
169 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE);
170 }
171
172 void WebContentsDragWin::StartBackgroundDragging(
173 const WebDropData& drop_data,
174 WebDragOperationsMask ops,
175 const GURL& page_url,
176 const std::string& page_encoding,
177 const SkBitmap& image,
178 const gfx::Point& image_offset) {
179 drag_drop_thread_id_ = base::PlatformThread::CurrentId();
180
181 DoDragging(drop_data, ops, page_url, page_encoding, image, image_offset);
182 BrowserThread::PostTask(
183 BrowserThread::UI,
184 FROM_HERE,
185 base::Bind(&WebContentsDragWin::EndDragging, this, true));
186 }
187
188 void WebContentsDragWin::PrepareDragForDownload(
189 const WebDropData& drop_data,
190 ui::OSExchangeData* data,
191 const GURL& page_url,
192 const std::string& page_encoding) {
193 // Parse the download metadata.
194 string16 mime_type;
195 FilePath file_name;
196 GURL download_url;
197 if (!drag_download_util::ParseDownloadMetadata(drop_data.download_metadata,
198 &mime_type,
199 &file_name,
200 &download_url))
201 return;
202
203 // Generate the file name based on both mime type and proposed file name.
204 std::string default_name =
205 content::GetContentClient()->browser()->GetDefaultDownloadName();
206 FilePath generated_download_file_name =
207 net::GenerateFileName(download_url,
208 std::string(),
209 std::string(),
210 UTF16ToUTF8(file_name.value()),
211 UTF16ToUTF8(mime_type),
212 default_name);
213
214 // Provide the data as file (CF_HDROP). A temporary download file with the
215 // Zone.Identifier ADS (Alternate Data Stream) attached will be created.
216 linked_ptr<net::FileStream> empty_file_stream;
217 scoped_refptr<DragDownloadFile> download_file =
218 new DragDownloadFile(generated_download_file_name,
219 empty_file_stream,
220 download_url,
221 page_url,
222 page_encoding,
223 web_contents_);
224 ui::OSExchangeData::DownloadFileInfo file_download(FilePath(),
225 download_file.get());
226 data->SetDownloadFileInfo(file_download);
227
228 // Enable asynchronous operation.
229 ui::OSExchangeDataProviderWin::GetIAsyncOperation(*data)->SetAsyncMode(TRUE);
230 }
231
232 void WebContentsDragWin::PrepareDragForFileContents(
233 const WebDropData& drop_data, ui::OSExchangeData* data) {
234 static const int kMaxFilenameLength = 255; // FAT and NTFS
235 FilePath file_name(drop_data.file_description_filename);
236 string16 extension = file_name.Extension();
237 file_name = file_name.BaseName().RemoveExtension();
238 // Images without ALT text will only have a file extension so we need to
239 // synthesize one from the provided extension and URL.
240 if (file_name.value().empty()) {
241 // Retrieve the name from the URL.
242 file_name = FilePath(
243 net::GetSuggestedFilename(drop_data.url, "", "", "", "", ""));
244 if (file_name.value().size() + extension.size() > kMaxFilenameLength) {
245 file_name = FilePath(file_name.value().substr(
246 0, kMaxFilenameLength - extension.size()));
247 }
248 }
249 file_name = file_name.ReplaceExtension(extension);
250 data->SetFileContents(file_name, drop_data.file_contents);
251 }
252
253 void WebContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data,
254 ui::OSExchangeData* data) {
255 if (drag_dest_->delegate()->AddDragData(drop_data, data))
256 return;
257
258 data->SetURL(drop_data.url, drop_data.url_title);
259 }
260
261 void WebContentsDragWin::DoDragging(const WebDropData& drop_data,
262 WebDragOperationsMask ops,
263 const GURL& page_url,
264 const std::string& page_encoding,
265 const SkBitmap& image,
266 const gfx::Point& image_offset) {
267 ui::OSExchangeData data;
268
269 // TODO(dcheng): Figure out why this is mutually exclusive.
270 if (!drop_data.download_metadata.empty()) {
271 PrepareDragForDownload(drop_data, &data, page_url, page_encoding);
272
273 // Set the observer.
274 ui::OSExchangeDataProviderWin::GetDataObjectImpl(data)->set_observer(this);
275 } else {
276 // We set the file contents before the URL because the URL also sets file
277 // contents (to a .URL shortcut). We want to prefer file content data over
278 // a shortcut so we add it first.
279 if (!drop_data.file_contents.empty())
280 PrepareDragForFileContents(drop_data, &data);
281 if (!drop_data.text_html.empty())
282 data.SetHtml(drop_data.text_html, drop_data.html_base_url);
283 // We set the text contents before the URL because the URL also sets text
284 // content.
285 if (!drop_data.plain_text.empty())
286 data.SetString(drop_data.plain_text);
287 if (drop_data.url.is_valid())
288 PrepareDragForUrl(drop_data, &data);
289 if (!drop_data.custom_data.empty()) {
290 Pickle pickle;
291 ui::WriteCustomDataToPickle(drop_data.custom_data, &pickle);
292 data.SetPickledData(ui::ClipboardUtil::GetWebCustomDataFormat()->cfFormat,
293 pickle);
294 }
295 }
296
297 // Set drag image.
298 if (!image.isNull()) {
299 drag_utils::SetDragImageOnDataObject(
300 image, gfx::Size(image.width(), image.height()), image_offset, &data);
301 }
302
303 // We need to enable recursive tasks on the message loop so we can get
304 // updates while in the system DoDragDrop loop.
305 DWORD effect;
306 {
307 MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
308 DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
309 drag_source_,
310 web_drag_utils_win::WebDragOpMaskToWinDragOpMask(ops),
311 &effect);
312 }
313
314 // This works because WebDragSource::OnDragSourceDrop uses PostTask to
315 // dispatch the actual event.
316 drag_source_->set_effect(effect);
317 }
318
319 void WebContentsDragWin::EndDragging(bool restore_suspended_state) {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321
322 if (drag_ended_)
323 return;
324 drag_ended_ = true;
325
326 if (restore_suspended_state)
327 drag_dest_->set_suspended(old_drop_target_suspended_state_);
328
329 if (msg_hook) {
330 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE);
331 UnhookWindowsHookEx(msg_hook);
332 msg_hook = NULL;
333 }
334
335 drag_end_callback_.Run();
336 }
337
338 void WebContentsDragWin::CancelDrag() {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
340
341 drag_source_->CancelDrag();
342 }
343
344 void WebContentsDragWin::CloseThread() {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346
347 drag_drop_thread_.reset();
348 }
349
350 void WebContentsDragWin::OnWaitForData() {
351 DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId());
352
353 // When the left button is release and we start to wait for the data, end
354 // the dragging before DoDragDrop returns. This makes the page leave the drag
355 // mode so that it can start to process the normal input events.
356 BrowserThread::PostTask(
357 BrowserThread::UI,
358 FROM_HERE,
359 base::Bind(&WebContentsDragWin::EndDragging, this, true));
360 }
361
362 void WebContentsDragWin::OnDataObjectDisposed() {
363 DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId());
364
365 // The drag-and-drop thread is only closed after OLE is done with
366 // DataObjectImpl.
367 BrowserThread::PostTask(
368 BrowserThread::UI,
369 FROM_HERE,
370 base::Bind(&WebContentsDragWin::CloseThread, this));
371 }
OLDNEW
« no previous file with comments | « content/browser/tab_contents/web_contents_drag_win.h ('k') | content/browser/tab_contents/web_contents_view_android.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698