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