1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/search/instant_unload_handler.h" 6 7#include <algorithm> 8 9#include "base/message_loop/message_loop.h" 10#include "chrome/browser/ui/browser_navigator.h" 11#include "content/public/browser/render_view_host.h" 12#include "content/public/browser/web_contents.h" 13#include "content/public/browser/web_contents_delegate.h" 14 15class InstantUnloadHandler::WebContentsDelegateImpl 16 : public content::WebContentsDelegate { 17 public: 18 WebContentsDelegateImpl(InstantUnloadHandler* handler, 19 scoped_ptr<content::WebContents> contents, 20 int index) 21 : handler_(handler), 22 contents_(contents.Pass()), 23 index_(index) { 24 contents_->SetDelegate(this); 25 contents_->GetRenderViewHost()->FirePageBeforeUnload(false); 26 } 27 28 // Overridden from content::WebContentsDelegate: 29 virtual void CloseContents(content::WebContents* source) OVERRIDE { 30 DCHECK_EQ(contents_, source); 31 // Remove ourselves as the delegate, so that CloseContents() won't be 32 // called twice, leading to double deletion (http://crbug.com/155848). 33 contents_->SetDelegate(NULL); 34 handler_->Destroy(this); 35 } 36 37 virtual void WillRunBeforeUnloadConfirm() OVERRIDE { 38 contents_->SetDelegate(NULL); 39 handler_->Activate(this, contents_.Pass(), index_); 40 } 41 42 virtual bool ShouldSuppressDialogs() OVERRIDE { 43 return true; 44 } 45 46 private: 47 InstantUnloadHandler* const handler_; 48 scoped_ptr<content::WebContents> contents_; 49 50 // The tab strip index |contents_| was originally at. If we add the tab back 51 // to the tabstrip, we add it at this index. 52 const int index_; 53 54 DISALLOW_COPY_AND_ASSIGN(WebContentsDelegateImpl); 55}; 56 57InstantUnloadHandler::InstantUnloadHandler(Browser* browser) 58 : browser_(browser) { 59} 60 61InstantUnloadHandler::~InstantUnloadHandler() { 62} 63 64void InstantUnloadHandler::RunUnloadListenersOrDestroy( 65 scoped_ptr<content::WebContents> contents, 66 int index) { 67 DCHECK(!contents->GetDelegate()); 68 69 if (!contents->NeedToFireBeforeUnload()) { 70 // Tab doesn't have any beforeunload listeners and can be safely deleted. 71 // However, the tab object should not be deleted immediately because when we 72 // get here from BrowserInstantController::TabDeactivated, other tab 73 // observers may still expect to interact with the tab before the event has 74 // finished propagating. 75 base::MessageLoop::current()->DeleteSoon(FROM_HERE, contents.release()); 76 return; 77 } 78 79 // Tab has beforeunload listeners. Install a delegate to run them. 80 delegates_.push_back( 81 new WebContentsDelegateImpl(this, contents.Pass(), index)); 82} 83 84void InstantUnloadHandler::Activate(WebContentsDelegateImpl* delegate, 85 scoped_ptr<content::WebContents> contents, 86 int index) { 87 // Remove (and delete) the delegate. 88 Destroy(delegate); 89 90 // Add the tab back in. 91 chrome::NavigateParams params(browser_, contents.release()); 92 params.disposition = NEW_FOREGROUND_TAB; 93 params.tabstrip_index = index; 94 chrome::Navigate(¶ms); 95} 96 97void InstantUnloadHandler::Destroy(WebContentsDelegateImpl* delegate) { 98 ScopedVector<WebContentsDelegateImpl>::iterator i = 99 std::find(delegates_.begin(), delegates_.end(), delegate); 100 DCHECK(i != delegates_.end()); 101 102 // The delegate's method is a caller on the stack, so schedule the deletion 103 // for later. 104 delegates_.weak_erase(i); 105 base::MessageLoop::current()->DeleteSoon(FROM_HERE, delegate); 106} 107