summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-10 23:10:42 +0000
committertc@google.com <tc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-10 23:10:42 +0000
commit24a4d10647ee76bae3d977caba9e8b13b5b81cf4 (patch)
treec35928b309d59b6fc60823364d4f122ca88a4c1e
parented1c9da9c46143da81c9449719b51b2b6fbc0e97 (diff)
downloadchromium_src-24a4d10647ee76bae3d977caba9e8b13b5b81cf4.zip
chromium_src-24a4d10647ee76bae3d977caba9e8b13b5b81cf4.tar.gz
chromium_src-24a4d10647ee76bae3d977caba9e8b13b5b81cf4.tar.bz2
Fix a crash that happens if a tab is closed while
we're in the middle of a drag originating from the tab. The problem is that the tab gets deleted out from under the drag operation which is happening in a nested message loop. To work around this, if we're in the middle of a drag and we get a tab close request, delay the tab close until after the drag operation is finished. BUG=16280 Review URL: http://codereview.chromium.org/149466 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20436 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base_drag_source.cc5
-rw-r--r--base/base_drag_source.h10
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc7
-rw-r--r--chrome/browser/tab_contents/tab_contents_view.h8
-rw-r--r--chrome/browser/tab_contents/web_drag_source.cc5
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.cc35
-rw-r--r--chrome/browser/views/tab_contents/tab_contents_view_win.h19
7 files changed, 79 insertions, 10 deletions
diff --git a/base/base_drag_source.cc b/base/base_drag_source.cc
index bdaab1e..bc81395 100644
--- a/base/base_drag_source.cc
+++ b/base/base_drag_source.cc
@@ -7,7 +7,7 @@
///////////////////////////////////////////////////////////////////////////////
// BaseDragSource, public:
-BaseDragSource::BaseDragSource() : ref_count_(0) {
+BaseDragSource::BaseDragSource() : ref_count_(0), cancel_drag_(false) {
}
///////////////////////////////////////////////////////////////////////////////
@@ -15,6 +15,9 @@ BaseDragSource::BaseDragSource() : ref_count_(0) {
HRESULT BaseDragSource::QueryContinueDrag(BOOL escape_pressed,
DWORD key_state) {
+ if (cancel_drag_)
+ return DRAGDROP_S_CANCEL;
+
if (escape_pressed) {
OnDragSourceCancel();
return DRAGDROP_S_CANCEL;
diff --git a/base/base_drag_source.h b/base/base_drag_source.h
index dea89af..3a4a94c 100644
--- a/base/base_drag_source.h
+++ b/base/base_drag_source.h
@@ -23,6 +23,13 @@ class BaseDragSource : public IDropSource {
BaseDragSource();
virtual ~BaseDragSource() { }
+ // Stop the drag operation at the next chance we get. This doesn't
+ // synchronously stop the drag (since Windows is controlling that),
+ // but lets us tell Windows to cancel the drag the next chance we get.
+ void CancelDrag() {
+ cancel_drag_ = true;
+ }
+
// IDropSource implementation:
HRESULT __stdcall QueryContinueDrag(BOOL escape_pressed, DWORD key_state);
HRESULT __stdcall GiveFeedback(DWORD effect);
@@ -40,6 +47,9 @@ class BaseDragSource : public IDropSource {
private:
LONG ref_count_;
+ // Set to true if we want to cancel the drag operation.
+ bool cancel_drag_;
+
DISALLOW_EVIL_CONSTRUCTORS(BaseDragSource);
};
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 9832fa4..bcbb09b 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -1904,6 +1904,13 @@ void TabContents::UpdateInspectorSettings(const std::wstring& raw_settings) {
}
void TabContents::Close(RenderViewHost* rvh) {
+ // If we close the tab while we're in the middle of a drag, we'll crash.
+ // Instead, cancel the drag and close it as soon as the drag ends.
+ if (view()->IsDoingDrag()) {
+ view()->CancelDragAndCloseTab();
+ return;
+ }
+
// Ignore this if it comes from a RenderViewHost that we aren't showing.
if (delegate() && rvh == render_view_host())
delegate()->CloseContents(this);
diff --git a/chrome/browser/tab_contents/tab_contents_view.h b/chrome/browser/tab_contents/tab_contents_view.h
index 6acbb30..b8e949b 100644
--- a/chrome/browser/tab_contents/tab_contents_view.h
+++ b/chrome/browser/tab_contents/tab_contents_view.h
@@ -138,6 +138,14 @@ class TabContentsView : public RenderViewHostDelegate::View {
return preferred_width_;
}
+ // If we try to close the tab while a drag is in progress, we crash. These
+ // methods allow the tab contents to determine if a drag is in progress and
+ // postpone the tab closing.
+ virtual bool IsDoingDrag() const {
+ return false;
+ }
+ virtual void CancelDragAndCloseTab() {}
+
protected:
TabContentsView() {} // Abstract interface.
diff --git a/chrome/browser/tab_contents/web_drag_source.cc b/chrome/browser/tab_contents/web_drag_source.cc
index d61f3ba2..8a9ce8b 100644
--- a/chrome/browser/tab_contents/web_drag_source.cc
+++ b/chrome/browser/tab_contents/web_drag_source.cc
@@ -37,11 +37,6 @@ WebDragSource::WebDragSource(gfx::NativeWindow source_wnd,
: BaseDragSource(),
source_wnd_(source_wnd),
render_view_host_(render_view_host) {
- // In an effort to try to track down http://crbug.com/12524 we now CHECK
- // when a NULL render_view_host is passed to us. I think this is what is
- // happening but it is hard to tell since the minidump is not helpful in this
- // case. At least we can then rule that out.
- CHECK(render_view_host_);
}
void WebDragSource::OnDragSourceCancel() {
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.cc b/chrome/browser/views/tab_contents/tab_contents_view_win.cc
index 0026c6b..7b6aabb 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_win.cc
+++ b/chrome/browser/views/tab_contents/tab_contents_view_win.cc
@@ -8,6 +8,7 @@
#include "app/gfx/canvas_paint.h"
#include "app/os_exchange_data.h"
+#include "base/time.h"
#include "chrome/browser/bookmarks/bookmark_drag_data.h"
#include "chrome/browser/browser.h" // TODO(beng): this dependency is awful.
#include "chrome/browser/browser_process.h"
@@ -54,7 +55,8 @@ TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
TabContentsViewWin::TabContentsViewWin(TabContents* tab_contents)
: TabContentsView(tab_contents),
ignore_next_char_event_(false),
- focus_manager_(NULL) {
+ focus_manager_(NULL),
+ close_tab_after_drag_ends_(false) {
last_focused_view_storage_id_ =
views::ViewStorage::GetSharedInstance()->CreateStorageID();
}
@@ -67,6 +69,8 @@ TabContentsViewWin::~TabContentsViewWin() {
views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance();
if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
view_storage->RemoveView(last_focused_view_storage_id_);
+
+ DCHECK(!drag_source_.get());
}
void TabContentsViewWin::Unparent() {
@@ -174,8 +178,8 @@ void TabContentsViewWin::StartDragging(const WebDropData& drop_data) {
if (!drop_data.plain_text.empty())
data->SetString(drop_data.plain_text);
- scoped_refptr<WebDragSource> drag_source(
- new WebDragSource(GetNativeView(), tab_contents()->render_view_host()));
+ drag_source_ = new WebDragSource(GetNativeView(),
+ tab_contents()->render_view_host());
DWORD effects;
@@ -183,9 +187,15 @@ void TabContentsViewWin::StartDragging(const WebDropData& drop_data) {
// updates while in the system DoDragDrop loop.
bool old_state = MessageLoop::current()->NestableTasksAllowed();
MessageLoop::current()->SetNestableTasksAllowed(true);
- DoDragDrop(data, drag_source, DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
+ DoDragDrop(data, drag_source_, DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
MessageLoop::current()->SetNestableTasksAllowed(old_state);
+ drag_source_ = NULL;
+ if (close_tab_after_drag_ends_) {
+ close_tab_timer_.Start(base::TimeDelta::FromMilliseconds(0), this,
+ &TabContentsViewWin::CloseTab);
+ }
+
if (tab_contents()->render_view_host())
tab_contents()->render_view_host()->DragSourceSystemDragEnded();
}
@@ -334,6 +344,19 @@ void TabContentsViewWin::RestoreFocus() {
}
}
+bool TabContentsViewWin::IsDoingDrag() const {
+ return drag_source_.get() != NULL;
+}
+
+void TabContentsViewWin::CancelDragAndCloseTab() {
+ DCHECK(IsDoingDrag());
+ // We can't close the tab while we're in the drag and
+ // |drag_source_->CancelDrag()| is async. Instead, set a flag to cancel
+ // the drag and when the drag nested message loop ends, close the tab.
+ drag_source_->CancelDrag();
+ close_tab_after_drag_ends_ = true;
+}
+
void TabContentsViewWin::UpdateDragCursor(bool is_drop_target) {
drop_target_->set_is_drop_target(is_drop_target);
}
@@ -422,6 +445,10 @@ views::FocusManager* TabContentsViewWin::GetFocusManager() {
return focus_manager_;
}
+void TabContentsViewWin::CloseTab() {
+ tab_contents()->Close(tab_contents()->render_view_host());
+}
+
void TabContentsViewWin::ShowContextMenu(const ContextMenuParams& params) {
// Allow delegates to handle the context menu operation first.
if (tab_contents()->delegate()->HandleContextMenu(params))
diff --git a/chrome/browser/views/tab_contents/tab_contents_view_win.h b/chrome/browser/views/tab_contents/tab_contents_view_win.h
index 4b5ade1..b2739dc 100644
--- a/chrome/browser/views/tab_contents/tab_contents_view_win.h
+++ b/chrome/browser/views/tab_contents/tab_contents_view_win.h
@@ -7,12 +7,14 @@
#include "base/gfx/size.h"
#include "base/scoped_ptr.h"
+#include "base/timer.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "views/widget/widget_win.h"
class RenderViewContextMenuWin;
class SadTabView;
struct WebDropData;
+class WebDragSource;
class WebDropTarget;
// Windows-specific implementation of the TabContentsView. It is a HWND that
@@ -47,6 +49,8 @@ class TabContentsViewWin : public TabContentsView,
virtual void SetInitialFocus();
virtual void StoreFocus();
virtual void RestoreFocus();
+ virtual bool IsDoingDrag() const;
+ virtual void CancelDragAndCloseTab();
// Backend implementation of RenderViewHostDelegate::View.
virtual void ShowContextMenu(const ContextMenuParams& params);
@@ -60,6 +64,9 @@ class TabContentsViewWin : public TabContentsView,
virtual views::FocusManager* GetFocusManager();
private:
+ // A helper method for closing the tab.
+ void CloseTab();
+
// Windows events ------------------------------------------------------------
// Overrides from WidgetWin.
@@ -115,6 +122,18 @@ class TabContentsViewWin : public TabContentsView,
// accessible when unparented.
views::FocusManager* focus_manager_;
+ // |drag_source_| is our callback interface passed to the system when we
+ // want to initiate a drag and drop operation. We use it to tell if a
+ // drag operation is happening.
+ scoped_refptr<WebDragSource> drag_source_;
+
+ // Set to true if we want to close the tab after the system drag operation
+ // has finished.
+ bool close_tab_after_drag_ends_;
+
+ // Used to close the tab after the stack has unwound.
+ base::OneShotTimer<TabContentsViewWin> close_tab_timer_;
+
DISALLOW_COPY_AND_ASSIGN(TabContentsViewWin);
};