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

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: Patch to land Created 8 years, 2 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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698