unload_controller.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/unload_controller.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
79ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/devtools/devtools_window.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_tabstrip.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace chrome {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// UnloadController, public:
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)UnloadController::UnloadController(Browser* browser)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : browser_(browser),
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_attempting_to_close_browser_(false),
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_factory_(this) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser_->tab_strip_model()->AddObserver(this);
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)UnloadController::~UnloadController() {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser_->tab_strip_model()->RemoveObserver(this);
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UnloadController::CanCloseContents(content::WebContents* contents) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't try to close the tab when the whole browser is being closed, since
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // that avoids the fast shutdown path where we just kill all the renderers.
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (is_attempting_to_close_browser_)
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    ClearUnloadState(contents, true);
403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return !is_attempting_to_close_browser_ ||
413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      is_calling_before_unload_handlers();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool UnloadController::RunUnloadEventsHelper(content::WebContents* contents) {
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // If the WebContents is not connected yet, then there's no unload
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // handler we can fire even if the WebContents has an unload listener.
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // One case where we hit this is in a tab that has an infinite loop
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // before load.
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (contents->NeedToFireBeforeUnload()) {
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // If the page has unload listeners, then we tell the renderer to fire
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // them. Once they have fired, we'll get a message back saying whether
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // to proceed closing the page or not, which sends us back to this method
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // with the NeedToFireBeforeUnload bit cleared.
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    contents->GetRenderViewHost()->FirePageBeforeUnload(false);
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return true;
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return false;
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UnloadController::BeforeUnloadFired(content::WebContents* contents,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         bool proceed) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_attempting_to_close_browser_) {
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!proceed)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      contents->SetClosedByUserGesture(false);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return proceed;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!proceed) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CancelWindowClose();
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    contents->SetClosedByUserGesture(false);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) {
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Now that beforeunload has fired, put the tab on the queue to fire
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // unload.
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    tabs_needing_unload_fired_.insert(contents);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProcessPendingTabs();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We want to handle firing the unload event ourselves since we want to
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // fire all the beforeunload events before attempting to fire the unload
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // events should the user cancel closing the browser.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UnloadController::ShouldCloseWindow() {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (HasCompletedUnloadProcessing())
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // The behavior followed here varies based on the current phase of the
943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // operation and whether a batched shutdown is in progress.
953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  //
963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // If there are tabs with outstanding beforeunload handlers:
973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // 1. If a batched shutdown is in progress: return false.
983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  //    This is to prevent interference with batched shutdown already in
993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  //    progress.
1003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // 2. Otherwise: start sending beforeunload events and return false.
1013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  //
1023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Otherwise, If there are no tabs with outstanding beforeunload handlers:
1033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // 3. If a batched shutdown is in progress: start sending unload events and
1043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  //    return false.
1053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // 4. Otherwise: return true.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_attempting_to_close_browser_ = true;
1073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Cases 1 and 4.
1083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  bool need_beforeunload_fired = TabsNeedBeforeUnloadFired();
1093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (need_beforeunload_fired == is_calling_before_unload_handlers())
1103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return !need_beforeunload_fired;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Cases 2 and 3.
1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  on_close_confirmed_.Reset();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProcessPendingTabs();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool UnloadController::CallBeforeUnloadHandlers(
1193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    const base::Callback<void(bool)>& on_close_confirmed) {
1203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (HasCompletedUnloadProcessing() || !TabsNeedBeforeUnloadFired())
1213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return false;
1223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  is_attempting_to_close_browser_ = true;
1243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  on_close_confirmed_ = on_close_confirmed;
1253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ProcessPendingTabs();
1273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return true;
1283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
1293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void UnloadController::ResetBeforeUnloadHandlers() {
1313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (!is_calling_before_unload_handlers())
1323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return;
1333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  CancelWindowClose();
1343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
1353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UnloadController::TabsNeedBeforeUnloadFired() {
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (tabs_needing_before_unload_fired_.empty()) {
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      content::WebContents* contents =
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          browser_->tab_strip_model()->GetWebContentsAt(i);
1413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      if (!ContainsKey(tabs_needing_unload_fired_, contents) &&
1423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          contents->NeedToFireBeforeUnload()) {
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        tabs_needing_before_unload_fired_.insert(contents);
1443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      }
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return !tabs_needing_before_unload_fired_.empty();
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// UnloadController, content::NotificationObserver implementation:
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::Observe(int type,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               const content::NotificationSource& source,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               const content::NotificationDetails& details) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED:
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (is_attempting_to_close_browser_) {
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        ClearUnloadState(content::Source<content::WebContents>(source).ptr(),
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         false);  // See comment for ClearUnloadState().
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "Got a notification we didn't register for.";
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// UnloadController, TabStripModelObserver implementation:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabInsertedAt(content::WebContents* contents,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     int index,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     bool foreground) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabAttachedImpl(contents);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabDetachedAt(content::WebContents* contents,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     int index) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabDetachedImpl(contents);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabReplacedAt(TabStripModel* tab_strip_model,
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     content::WebContents* old_contents,
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     content::WebContents* new_contents,
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     int index) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabDetachedImpl(old_contents);
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabAttachedImpl(new_contents);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabStripEmpty() {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set is_attempting_to_close_browser_ here, so that extensions, etc, do not
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // attempt to add tabs to the browser before it closes.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_attempting_to_close_browser_ = true;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// UnloadController, private:
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabAttachedImpl(content::WebContents* contents) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the tab crashes in the beforeunload or unload handler, it won't be
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // able to ack. But we know we can close it.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<content::WebContents>(contents));
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::TabDetachedImpl(content::WebContents* contents) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_attempting_to_close_browser_)
210eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    ClearUnloadState(contents, false);
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  registrar_.Remove(this,
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    content::Source<content::WebContents>(contents));
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::ProcessPendingTabs() {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_attempting_to_close_browser_) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Because we might invoke this after a delay it's possible for the value of
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // is_attempting_to_close_browser_ to have changed since we scheduled the
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // task.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
224eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (HasCompletedUnloadProcessing()) {
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // We've finished all the unload events and can proceed to close the
226eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // browser.
227eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    browser_->OnWindowClosing();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
231eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Process beforeunload tabs first. When that queue is empty, process
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // unload tabs.
233eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!tabs_needing_before_unload_fired_.empty()) {
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    content::WebContents* web_contents =
235eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        *(tabs_needing_before_unload_fired_.begin());
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Null check render_view_host here as this gets called on a PostTask and
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the tab's render_view_host may have been nulled out.
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (web_contents->GetRenderViewHost()) {
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ClearUnloadState(web_contents, true);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  } else if (is_calling_before_unload_handlers()) {
2443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    on_close_confirmed_.Run(true);
245eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  } else if (!tabs_needing_unload_fired_.empty()) {
246eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // We've finished firing all beforeunload events and can proceed with unload
247eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // events.
248eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // somewhere around here so that we have accurate measurements of shutdown
250eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // time.
251eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // TODO(ojan): We can probably fire all the unload events in parallel and
252eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // get a perf benefit from that in the cases where the tab hangs in it's
253eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // unload handler or takes a long time to page in.
254eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin());
255eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Null check render_view_host here as this gets called on a PostTask and
256eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // the tab's render_view_host may have been nulled out.
257eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (web_contents->GetRenderViewHost()) {
258eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      web_contents->GetRenderViewHost()->ClosePage();
259868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else {
260eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ClearUnloadState(web_contents, true);
261868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
262eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  } else {
263eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    NOTREACHED();
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool UnloadController::HasCompletedUnloadProcessing() const {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return is_attempting_to_close_browser_ &&
269eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      tabs_needing_before_unload_fired_.empty() &&
270eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      tabs_needing_unload_fired_.empty();
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void UnloadController::CancelWindowClose() {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Closing of window can be canceled from a beforeunload handler.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(is_attempting_to_close_browser_);
276eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  tabs_needing_before_unload_fired_.clear();
277eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  tabs_needing_unload_fired_.clear();
2783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (is_calling_before_unload_handlers()) {
2793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_;
2803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    on_close_confirmed_.Reset();
2813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    on_close_confirmed.Run(false);
2823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_attempting_to_close_browser_ = false;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<Browser>(browser_),
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NotificationService::NoDetails());
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
291eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool UnloadController::RemoveFromSet(UnloadListenerSet* set,
292eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                     content::WebContents* web_contents) {
293eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(is_attempting_to_close_browser_);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  UnloadListenerSet::iterator iter =
296eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      std::find(set->begin(), set->end(), web_contents);
297eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (iter != set->end()) {
298eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    set->erase(iter);
299eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
301eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
304eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid UnloadController::ClearUnloadState(content::WebContents* web_contents,
305eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                        bool process_now) {
306eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (is_attempting_to_close_browser_) {
307eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents);
308eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    RemoveFromSet(&tabs_needing_unload_fired_, web_contents);
309eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (process_now) {
310eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      ProcessPendingTabs();
311eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    } else {
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::MessageLoop::current()->PostTask(
313eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          FROM_HERE,
314eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          base::Bind(&UnloadController::ProcessPendingTabs,
315eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                     weak_factory_.GetWeakPtr()));
316eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
317eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
318868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
319868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace chrome
321