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
Esse commit está contido em:
tc@google.com
2009-07-10 23:10:42 +00:00
commit f5a077e1fd
7 arquivos alterados com 79 adições e 10 exclusões
+4 -1
Ver Arquivo
@@ -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;
+10
Ver Arquivo
@@ -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);
};
@@ -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);
@@ -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.
@@ -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() {
@@ -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))
@@ -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);
};