| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/web_contents/web_contents_drag_win.h" | 5 #include "content/browser/web_contents/web_contents_drag_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 | 8 |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 // more. | 65 // more. |
| 66 if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000)) | 66 if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000)) |
| 67 mouse_up_received = true; | 67 mouse_up_received = true; |
| 68 | 68 |
| 69 return TRUE; | 69 return TRUE; |
| 70 } | 70 } |
| 71 } | 71 } |
| 72 return CallNextHookEx(msg_hook, code, wparam, lparam); | 72 return CallNextHookEx(msg_hook, code, wparam, lparam); |
| 73 } | 73 } |
| 74 | 74 |
| 75 void EnableBackgroundDraggingSupport(DWORD thread_id) { |
| 76 // Install a hook procedure to monitor the messages so that we can forward |
| 77 // the appropriate ones to the background thread. |
| 78 drag_out_thread_id = thread_id; |
| 79 mouse_up_received = false; |
| 80 DCHECK(!msg_hook); |
| 81 msg_hook = SetWindowsHookEx(WH_MSGFILTER, |
| 82 MsgFilterProc, |
| 83 NULL, |
| 84 GetCurrentThreadId()); |
| 85 |
| 86 // Attach the input state of the background thread to the UI thread so that |
| 87 // SetCursor can work from the background thread. |
| 88 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE); |
| 89 } |
| 90 |
| 91 void DisableBackgroundDraggingSupport() { |
| 92 DCHECK(msg_hook); |
| 93 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE); |
| 94 UnhookWindowsHookEx(msg_hook); |
| 95 msg_hook = NULL; |
| 96 } |
| 97 |
| 98 bool IsBackgroundDraggingSupportEnabled() { |
| 99 return msg_hook != NULL; |
| 100 } |
| 101 |
| 75 } // namespace | 102 } // namespace |
| 76 | 103 |
| 77 class DragDropThread : public base::Thread { | 104 class DragDropThread : public base::Thread { |
| 78 public: | 105 public: |
| 79 explicit DragDropThread(WebContentsDragWin* drag_handler) | 106 explicit DragDropThread(WebContentsDragWin* drag_handler) |
| 80 : base::Thread("Chrome_DragDropThread"), | 107 : base::Thread("Chrome_DragDropThread"), |
| 81 drag_handler_(drag_handler) { | 108 drag_handler_(drag_handler) { |
| 82 } | 109 } |
| 83 | 110 |
| 84 virtual ~DragDropThread() { | 111 virtual ~DragDropThread() { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 const gfx::Point& image_offset) { | 155 const gfx::Point& image_offset) { |
| 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 130 | 157 |
| 131 drag_source_ = new WebDragSource(source_window_, web_contents_); | 158 drag_source_ = new WebDragSource(source_window_, web_contents_); |
| 132 | 159 |
| 133 const GURL& page_url = web_contents_->GetURL(); | 160 const GURL& page_url = web_contents_->GetURL(); |
| 134 const std::string& page_encoding = web_contents_->GetEncoding(); | 161 const std::string& page_encoding = web_contents_->GetEncoding(); |
| 135 | 162 |
| 136 // If it is not drag-out, do the drag-and-drop in the current UI thread. | 163 // If it is not drag-out, do the drag-and-drop in the current UI thread. |
| 137 if (drop_data.download_metadata.empty()) { | 164 if (drop_data.download_metadata.empty()) { |
| 138 DoDragging(drop_data, ops, page_url, page_encoding, image, image_offset); | 165 if (DoDragging(drop_data, ops, page_url, page_encoding, |
| 139 EndDragging(); | 166 image, image_offset)) |
| 167 EndDragging(); |
| 140 return; | 168 return; |
| 141 } | 169 } |
| 142 | 170 |
| 143 // Start a background thread to do the drag-and-drop. | 171 // Start a background thread to do the drag-and-drop. |
| 144 DCHECK(!drag_drop_thread_.get()); | 172 DCHECK(!drag_drop_thread_.get()); |
| 145 drag_drop_thread_.reset(new DragDropThread(this)); | 173 drag_drop_thread_.reset(new DragDropThread(this)); |
| 146 base::Thread::Options options; | 174 base::Thread::Options options; |
| 147 options.message_loop_type = MessageLoop::TYPE_UI; | 175 options.message_loop_type = MessageLoop::TYPE_UI; |
| 148 if (drag_drop_thread_->StartWithOptions(options)) { | 176 if (drag_drop_thread_->StartWithOptions(options)) { |
| 149 gfx::Display display = | 177 gfx::Display display = |
| 150 gfx::Screen::GetDisplayNearestWindow(web_contents_->GetNativeView()); | 178 gfx::Screen::GetDisplayNearestWindow(web_contents_->GetNativeView()); |
| 151 ui::ScaleFactor scale_factor = ui::GetScaleFactorFromScale( | 179 ui::ScaleFactor scale_factor = ui::GetScaleFactorFromScale( |
| 152 display.device_scale_factor()); | 180 display.device_scale_factor()); |
| 153 drag_drop_thread_->message_loop()->PostTask( | 181 drag_drop_thread_->message_loop()->PostTask( |
| 154 FROM_HERE, | 182 FROM_HERE, |
| 155 base::Bind(&WebContentsDragWin::StartBackgroundDragging, this, | 183 base::Bind(&WebContentsDragWin::StartBackgroundDragging, this, |
| 156 drop_data, ops, page_url, page_encoding, | 184 drop_data, ops, page_url, page_encoding, |
| 157 image.GetRepresentation(scale_factor).sk_bitmap(), | 185 image.GetRepresentation(scale_factor).sk_bitmap(), |
| 158 image_offset)); | 186 image_offset)); |
| 159 } | 187 } |
| 160 | 188 |
| 161 // Install a hook procedure to monitor the messages so that we can forward | 189 EnableBackgroundDraggingSupport(drag_drop_thread_->thread_id()); |
| 162 // the appropriate ones to the background thread. | |
| 163 drag_out_thread_id = drag_drop_thread_->thread_id(); | |
| 164 mouse_up_received = false; | |
| 165 DCHECK(!msg_hook); | |
| 166 msg_hook = SetWindowsHookEx(WH_MSGFILTER, | |
| 167 MsgFilterProc, | |
| 168 NULL, | |
| 169 GetCurrentThreadId()); | |
| 170 | |
| 171 // Attach the input state of the background thread to the UI thread so that | |
| 172 // SetCursor can work from the background thread. | |
| 173 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE); | |
| 174 } | 190 } |
| 175 | 191 |
| 176 void WebContentsDragWin::StartBackgroundDragging( | 192 void WebContentsDragWin::StartBackgroundDragging( |
| 177 const WebDropData& drop_data, | 193 const WebDropData& drop_data, |
| 178 WebDragOperationsMask ops, | 194 WebDragOperationsMask ops, |
| 179 const GURL& page_url, | 195 const GURL& page_url, |
| 180 const std::string& page_encoding, | 196 const std::string& page_encoding, |
| 181 const SkBitmap& image, | 197 const SkBitmap& image, |
| 182 const gfx::Point& image_offset) { | 198 const gfx::Point& image_offset) { |
| 183 drag_drop_thread_id_ = base::PlatformThread::CurrentId(); | 199 drag_drop_thread_id_ = base::PlatformThread::CurrentId(); |
| 184 | 200 |
| 185 DoDragging(drop_data, ops, page_url, page_encoding, image, image_offset); | 201 if (DoDragging(drop_data, ops, page_url, page_encoding, |
| 186 BrowserThread::PostTask( | 202 image, image_offset)) { |
| 187 BrowserThread::UI, | 203 BrowserThread::PostTask( |
| 188 FROM_HERE, | 204 BrowserThread::UI, |
| 189 base::Bind(&WebContentsDragWin::EndDragging, this)); | 205 FROM_HERE, |
| 206 base::Bind(&WebContentsDragWin::EndDragging, this)); |
| 207 } else { |
| 208 // When DoDragging returns false, the contents view window is gone and thus |
| 209 // WebContentsViewWin instance becomes invalid though WebContentsDragWin |
| 210 // instance is still alive because the task holds a reference count to it. |
| 211 // We should not do more than the following cleanup items: |
| 212 // 1) Remove the background dragging support. This is safe since it does not |
| 213 // access the instance at all. |
| 214 // 2) Stop the background thread. This is done in OnDataObjectDisposed. |
| 215 // Only drag_drop_thread_ member is accessed. |
| 216 BrowserThread::PostTask( |
| 217 BrowserThread::UI, |
| 218 FROM_HERE, |
| 219 base::Bind(&DisableBackgroundDraggingSupport)); |
| 220 } |
| 190 } | 221 } |
| 191 | 222 |
| 192 void WebContentsDragWin::PrepareDragForDownload( | 223 void WebContentsDragWin::PrepareDragForDownload( |
| 193 const WebDropData& drop_data, | 224 const WebDropData& drop_data, |
| 194 ui::OSExchangeData* data, | 225 ui::OSExchangeData* data, |
| 195 const GURL& page_url, | 226 const GURL& page_url, |
| 196 const std::string& page_encoding) { | 227 const std::string& page_encoding) { |
| 197 // Parse the download metadata. | 228 // Parse the download metadata. |
| 198 string16 mime_type; | 229 string16 mime_type; |
| 199 FilePath file_name; | 230 FilePath file_name; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 void WebContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data, | 289 void WebContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data, |
| 259 ui::OSExchangeData* data) { | 290 ui::OSExchangeData* data) { |
| 260 if (drag_dest_->delegate() && | 291 if (drag_dest_->delegate() && |
| 261 drag_dest_->delegate()->AddDragData(drop_data, data)) { | 292 drag_dest_->delegate()->AddDragData(drop_data, data)) { |
| 262 return; | 293 return; |
| 263 } | 294 } |
| 264 | 295 |
| 265 data->SetURL(drop_data.url, drop_data.url_title); | 296 data->SetURL(drop_data.url, drop_data.url_title); |
| 266 } | 297 } |
| 267 | 298 |
| 268 void WebContentsDragWin::DoDragging(const WebDropData& drop_data, | 299 bool WebContentsDragWin::DoDragging(const WebDropData& drop_data, |
| 269 WebDragOperationsMask ops, | 300 WebDragOperationsMask ops, |
| 270 const GURL& page_url, | 301 const GURL& page_url, |
| 271 const std::string& page_encoding, | 302 const std::string& page_encoding, |
| 272 const gfx::ImageSkia& image, | 303 const gfx::ImageSkia& image, |
| 273 const gfx::Point& image_offset) { | 304 const gfx::Point& image_offset) { |
| 274 ui::OSExchangeData data; | 305 ui::OSExchangeData data; |
| 275 | 306 |
| 276 if (!drop_data.download_metadata.empty()) { | 307 if (!drop_data.download_metadata.empty()) { |
| 277 PrepareDragForDownload(drop_data, &data, page_url, page_encoding); | 308 PrepareDragForDownload(drop_data, &data, page_url, page_encoding); |
| 278 | 309 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 299 data.SetPickledData(ui::ClipboardUtil::GetWebCustomDataFormat()->cfFormat, | 330 data.SetPickledData(ui::ClipboardUtil::GetWebCustomDataFormat()->cfFormat, |
| 300 pickle); | 331 pickle); |
| 301 } | 332 } |
| 302 | 333 |
| 303 // Set drag image. | 334 // Set drag image. |
| 304 if (!image.isNull()) { | 335 if (!image.isNull()) { |
| 305 drag_utils::SetDragImageOnDataObject(image, | 336 drag_utils::SetDragImageOnDataObject(image, |
| 306 gfx::Size(image.width(), image.height()), image_offset, &data); | 337 gfx::Size(image.width(), image.height()), image_offset, &data); |
| 307 } | 338 } |
| 308 | 339 |
| 340 // Use a local variable to keep track of the contents view window handle. |
| 341 // It might not be safe to access the instance after DoDragDrop returns |
| 342 // because the window could be disposed in the nested message loop. |
| 343 HWND native_window = web_contents_->GetNativeView(); |
| 344 |
| 309 // We need to enable recursive tasks on the message loop so we can get | 345 // We need to enable recursive tasks on the message loop so we can get |
| 310 // updates while in the system DoDragDrop loop. | 346 // updates while in the system DoDragDrop loop. |
| 311 DWORD effect; | 347 DWORD effect; |
| 312 { | 348 { |
| 349 // Keep a reference count such that |drag_source_| will not get deleted |
| 350 // if the contents view window is gone in the nested message loop invoked |
| 351 // from DoDragDrop. |
| 352 scoped_refptr<WebDragSource> retain_this(drag_source_); |
| 353 |
| 313 MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); | 354 MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); |
| 314 DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), | 355 DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), |
| 315 drag_source_, | 356 drag_source_, |
| 316 web_drag_utils_win::WebDragOpMaskToWinDragOpMask(ops), | 357 web_drag_utils_win::WebDragOpMaskToWinDragOpMask(ops), |
| 317 &effect); | 358 &effect); |
| 318 } | 359 } |
| 319 | 360 |
| 361 // Bail out immediately if the contents view window is gone. |
| 362 if (!IsWindow(native_window)) |
| 363 return false; |
| 364 |
| 320 // Normally, the drop and dragend events get dispatched in the system | 365 // Normally, the drop and dragend events get dispatched in the system |
| 321 // DoDragDrop message loop so it'd be too late to set the effect to send back | 366 // DoDragDrop message loop so it'd be too late to set the effect to send back |
| 322 // to the renderer here. However, we use PostTask to delay the execution of | 367 // to the renderer here. However, we use PostTask to delay the execution of |
| 323 // WebDragSource::OnDragSourceDrop, which means that the delayed dragend | 368 // WebDragSource::OnDragSourceDrop, which means that the delayed dragend |
| 324 // callback to the renderer doesn't run until this has been set to the correct | 369 // callback to the renderer doesn't run until this has been set to the correct |
| 325 // value. | 370 // value. |
| 326 drag_source_->set_effect(effect); | 371 drag_source_->set_effect(effect); |
| 372 |
| 373 return true; |
| 327 } | 374 } |
| 328 | 375 |
| 329 void WebContentsDragWin::EndDragging() { | 376 void WebContentsDragWin::EndDragging() { |
| 330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 331 | 378 |
| 332 if (drag_ended_) | 379 if (drag_ended_) |
| 333 return; | 380 return; |
| 334 drag_ended_ = true; | 381 drag_ended_ = true; |
| 335 | 382 |
| 336 if (msg_hook) { | 383 if (IsBackgroundDraggingSupportEnabled()) |
| 337 AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE); | 384 DisableBackgroundDraggingSupport(); |
| 338 UnhookWindowsHookEx(msg_hook); | |
| 339 msg_hook = NULL; | |
| 340 } | |
| 341 | 385 |
| 342 drag_end_callback_.Run(); | 386 drag_end_callback_.Run(); |
| 343 } | 387 } |
| 344 | 388 |
| 345 void WebContentsDragWin::CancelDrag() { | 389 void WebContentsDragWin::CancelDrag() { |
| 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 347 | 391 |
| 348 drag_source_->CancelDrag(); | 392 drag_source_->CancelDrag(); |
| 349 } | 393 } |
| 350 | 394 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 369 void WebContentsDragWin::OnDataObjectDisposed() { | 413 void WebContentsDragWin::OnDataObjectDisposed() { |
| 370 DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId()); | 414 DCHECK(drag_drop_thread_id_ == base::PlatformThread::CurrentId()); |
| 371 | 415 |
| 372 // The drag-and-drop thread is only closed after OLE is done with | 416 // The drag-and-drop thread is only closed after OLE is done with |
| 373 // DataObjectImpl. | 417 // DataObjectImpl. |
| 374 BrowserThread::PostTask( | 418 BrowserThread::PostTask( |
| 375 BrowserThread::UI, | 419 BrowserThread::UI, |
| 376 FROM_HERE, | 420 FROM_HERE, |
| 377 base::Bind(&WebContentsDragWin::CloseThread, this)); | 421 base::Bind(&WebContentsDragWin::CloseThread, this)); |
| 378 } | 422 } |
| OLD | NEW |