This source file includes following definitions.
- DetachedWebContentsDelegate
- DetachedWebContentsDelegate
- ShouldSuppressDialogs
- CloseContents
- weak_factory_
- CanCloseContents
- ShouldRunUnloadEventsHelper
- RunUnloadEventsHelper
- BeforeUnloadFired
- ShouldCloseWindow
- CallBeforeUnloadHandlers
- ResetBeforeUnloadHandlers
- TabsNeedBeforeUnloadFired
- HasCompletedUnloadProcessing
- CancelWindowClose
- Observe
- TabInsertedAt
- TabDetachedAt
- TabReplacedAt
- TabStripEmpty
- TabAttachedImpl
- TabDetachedImpl
- DetachWebContents
- ProcessPendingTabs
- ClearUnloadState
- PostTaskForProcessPendingTabs
#include "chrome/browser/ui/fast_unload_controller.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
namespace chrome {
class FastUnloadController::DetachedWebContentsDelegate
: public content::WebContentsDelegate {
public:
DetachedWebContentsDelegate() { }
virtual ~DetachedWebContentsDelegate() { }
private:
virtual bool ShouldSuppressDialogs() OVERRIDE {
return true;
}
virtual void CloseContents(content::WebContents* source) OVERRIDE {
delete source;
}
DISALLOW_COPY_AND_ASSIGN(DetachedWebContentsDelegate);
};
FastUnloadController::FastUnloadController(Browser* browser)
: browser_(browser),
tab_needing_before_unload_ack_(NULL),
is_attempting_to_close_browser_(false),
detached_delegate_(new DetachedWebContentsDelegate()),
weak_factory_(this) {
browser_->tab_strip_model()->AddObserver(this);
}
FastUnloadController::~FastUnloadController() {
browser_->tab_strip_model()->RemoveObserver(this);
}
bool FastUnloadController::CanCloseContents(content::WebContents* contents) {
return !is_attempting_to_close_browser_ ||
is_calling_before_unload_handlers();
}
bool FastUnloadController::ShouldRunUnloadEventsHelper(
content::WebContents* contents) {
return DevToolsWindow::GetInstanceForInspectedRenderViewHost(
contents->GetRenderViewHost()) != NULL;
}
bool FastUnloadController::RunUnloadEventsHelper(
content::WebContents* contents) {
if (DevToolsWindow::InterceptPageBeforeUnload(contents)) {
return true;
}
if (contents->NeedToFireBeforeUnload()) {
contents->GetMainFrame()->DispatchBeforeUnload(false);
return true;
}
return false;
}
bool FastUnloadController::BeforeUnloadFired(content::WebContents* contents,
bool proceed) {
if (!proceed)
DevToolsWindow::OnPageCloseCanceled(contents);
if (!is_attempting_to_close_browser_) {
if (!proceed) {
contents->SetClosedByUserGesture(false);
} else {
browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents);
DetachWebContents(contents);
}
return proceed;
}
if (!proceed) {
CancelWindowClose();
contents->SetClosedByUserGesture(false);
return false;
}
if (tab_needing_before_unload_ack_ == contents) {
tab_needing_before_unload_ack_ = NULL;
tabs_needing_unload_.insert(contents);
ProcessPendingTabs();
return false;
}
return true;
}
bool FastUnloadController::ShouldCloseWindow() {
if (HasCompletedUnloadProcessing())
return true;
if (browser_->is_devtools() &&
DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(browser_)) {
return true;
}
is_attempting_to_close_browser_ = true;
bool need_beforeunload_fired = TabsNeedBeforeUnloadFired();
if (need_beforeunload_fired == is_calling_before_unload_handlers())
return !need_beforeunload_fired;
on_close_confirmed_.Reset();
ProcessPendingTabs();
return false;
}
bool FastUnloadController::CallBeforeUnloadHandlers(
const base::Callback<void(bool)>& on_close_confirmed) {
if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired())
return false;
on_close_confirmed_ = on_close_confirmed;
is_attempting_to_close_browser_ = true;
ProcessPendingTabs();
return true;
}
void FastUnloadController::ResetBeforeUnloadHandlers() {
if (!is_calling_before_unload_handlers())
return;
CancelWindowClose();
}
bool FastUnloadController::TabsNeedBeforeUnloadFired() {
if (!tabs_needing_before_unload_.empty() ||
tab_needing_before_unload_ack_ != NULL)
return true;
if (!is_calling_before_unload_handlers() && !tabs_needing_unload_.empty())
return false;
for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) {
content::WebContents* contents =
browser_->tab_strip_model()->GetWebContentsAt(i);
bool should_fire_beforeunload = contents->NeedToFireBeforeUnload() ||
DevToolsWindow::NeedsToInterceptBeforeUnload(contents);
if (!ContainsKey(tabs_needing_unload_, contents) &&
!ContainsKey(tabs_needing_unload_ack_, contents) &&
tab_needing_before_unload_ack_ != contents &&
should_fire_beforeunload)
tabs_needing_before_unload_.insert(contents);
}
return !tabs_needing_before_unload_.empty();
}
bool FastUnloadController::HasCompletedUnloadProcessing() const {
return is_attempting_to_close_browser_ &&
tabs_needing_before_unload_.empty() &&
tab_needing_before_unload_ack_ == NULL &&
tabs_needing_unload_.empty() &&
tabs_needing_unload_ack_.empty();
}
void FastUnloadController::CancelWindowClose() {
DCHECK(is_attempting_to_close_browser_);
tabs_needing_before_unload_.clear();
if (tab_needing_before_unload_ack_ != NULL) {
CoreTabHelper* core_tab_helper =
CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_);
core_tab_helper->OnCloseCanceled();
DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_);
tab_needing_before_unload_ack_ = NULL;
}
for (WebContentsSet::iterator it = tabs_needing_unload_.begin();
it != tabs_needing_unload_.end(); it++) {
content::WebContents* contents = *it;
CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
core_tab_helper->OnCloseCanceled();
DevToolsWindow::OnPageCloseCanceled(contents);
}
tabs_needing_unload_.clear();
if (is_calling_before_unload_handlers()) {
base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_;
on_close_confirmed_.Reset();
on_close_confirmed.Run(false);
}
is_attempting_to_close_browser_ = false;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
content::Source<Browser>(browser_),
content::NotificationService::NoDetails());
}
void FastUnloadController::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: {
registrar_.Remove(this,
content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
source);
content::WebContents* contents =
content::Source<content::WebContents>(source).ptr();
ClearUnloadState(contents);
break;
}
default:
NOTREACHED() << "Got a notification we didn't register for.";
}
}
void FastUnloadController::TabInsertedAt(content::WebContents* contents,
int index,
bool foreground) {
TabAttachedImpl(contents);
}
void FastUnloadController::TabDetachedAt(content::WebContents* contents,
int index) {
TabDetachedImpl(contents);
}
void FastUnloadController::TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) {
TabDetachedImpl(old_contents);
TabAttachedImpl(new_contents);
}
void FastUnloadController::TabStripEmpty() {
is_attempting_to_close_browser_ = true;
}
void FastUnloadController::TabAttachedImpl(content::WebContents* contents) {
registrar_.Add(
this,
content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
content::Source<content::WebContents>(contents));
}
void FastUnloadController::TabDetachedImpl(content::WebContents* contents) {
if (tabs_needing_unload_ack_.find(contents) !=
tabs_needing_unload_ack_.end()) {
return;
}
const content::NotificationSource& source =
content::Source<content::WebContents>(contents);
if (registrar_.IsRegistered(this,
content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
source)) {
registrar_.Remove(this,
content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
source);
}
if (is_attempting_to_close_browser_)
ClearUnloadState(contents);
}
bool FastUnloadController::DetachWebContents(content::WebContents* contents) {
int index = browser_->tab_strip_model()->GetIndexOfWebContents(contents);
if (index != TabStripModel::kNoTab &&
contents->NeedToFireBeforeUnload()) {
tabs_needing_unload_ack_.insert(contents);
browser_->tab_strip_model()->DetachWebContentsAt(index);
contents->SetDelegate(detached_delegate_.get());
CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
core_tab_helper->OnUnloadDetachedStarted();
return true;
}
return false;
}
void FastUnloadController::ProcessPendingTabs() {
if (!is_attempting_to_close_browser_) {
return;
}
if (tab_needing_before_unload_ack_ != NULL) {
return;
}
if (!tabs_needing_before_unload_.empty()) {
WebContentsSet::iterator it = tabs_needing_before_unload_.begin();
content::WebContents* contents = *it;
tabs_needing_before_unload_.erase(it);
if (contents->GetRenderViewHost()) {
tab_needing_before_unload_ack_ = contents;
CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
core_tab_helper->OnCloseStarted();
if (!DevToolsWindow::InterceptPageBeforeUnload(contents))
contents->GetMainFrame()->DispatchBeforeUnload(false);
} else {
ProcessPendingTabs();
}
return;
}
if (is_calling_before_unload_handlers()) {
on_close_confirmed_.Run(true);
return;
}
if (!tabs_needing_unload_.empty()) {
browser_->OnWindowClosing();
WebContentsSet::iterator it = tabs_needing_unload_.begin();
while (it != tabs_needing_unload_.end()) {
WebContentsSet::iterator current = it++;
content::WebContents* contents = *current;
tabs_needing_unload_.erase(current);
if (contents->GetRenderViewHost()) {
CoreTabHelper* core_tab_helper =
CoreTabHelper::FromWebContents(contents);
core_tab_helper->OnUnloadStarted();
DetachWebContents(contents);
contents->GetRenderViewHost()->ClosePage();
}
}
if (browser_->tab_strip_model()->empty()) {
browser_->TabStripEmpty();
} else {
browser_->tab_strip_model()->CloseAllTabs();
}
return;
}
if (HasCompletedUnloadProcessing()) {
browser_->OnWindowClosing();
if (browser_->tab_strip_model()->empty()) {
browser_->TabStripEmpty();
} else {
browser_->tab_strip_model()->CloseAllTabs();
}
return;
}
}
void FastUnloadController::ClearUnloadState(content::WebContents* contents) {
if (tabs_needing_unload_ack_.erase(contents) > 0) {
if (HasCompletedUnloadProcessing())
PostTaskForProcessPendingTabs();
return;
}
if (!is_attempting_to_close_browser_)
return;
if (tab_needing_before_unload_ack_ == contents) {
tab_needing_before_unload_ack_ = NULL;
PostTaskForProcessPendingTabs();
return;
}
if (tabs_needing_before_unload_.erase(contents) > 0 ||
tabs_needing_unload_.erase(contents) > 0) {
if (tab_needing_before_unload_ack_ == NULL)
PostTaskForProcessPendingTabs();
}
}
void FastUnloadController::PostTaskForProcessPendingTabs() {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&FastUnloadController::ProcessPendingTabs,
weak_factory_.GetWeakPtr()));
}
}