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

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

Issue 10966023: Fix the crash that could occur when the window is closed while web contents drag is in progress. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « content/browser/web_contents/web_contents_drag_win.h ('k') | content/browser/web_contents/web_contents_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698