1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_UNLOAD_CONTROLLER_H_
#define CHROME_BROWSER_UI_UNLOAD_CONTROLLER_H_
#include <memory>
#include <set>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/tab_contents/web_contents_collection.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
enum class BrowserClosingStatus;
class Browser;
class TabStripModel;
namespace content {
class WebContents;
} // namespace content
class UnloadController : public WebContentsCollection::Observer,
public TabStripModelObserver {
public:
explicit UnloadController(Browser* browser);
UnloadController(const UnloadController&) = delete;
UnloadController& operator=(const UnloadController&) = delete;
~UnloadController() override;
// Returns true if |contents| can be cleanly closed. When |browser_| is being
// closed, this function will return false to indicate |contents| should not
// be cleanly closed, since the fast shutdown path will just kill its
// renderer.
bool CanCloseContents(content::WebContents* contents);
// Returns true if we need to run unload events for the |contents|.
bool ShouldRunUnloadEventsHelper(content::WebContents* contents);
// Helper function to run beforeunload listeners on a WebContents.
// Returns true if |contents| beforeunload listeners were invoked.
bool RunUnloadEventsHelper(content::WebContents* contents);
// Called when a BeforeUnload handler is fired for |contents|. |proceed|
// indicates the user's response to the Y/N BeforeUnload handler dialog. If
// this parameter is false, any pending attempt to close the whole browser
// will be canceled. Returns true if Unload handlers should be fired. When the
// |browser_| is being closed, Unload handlers for any particular WebContents
// will not be run until every WebContents being closed has a chance to run
// its BeforeUnloadHandler.
bool BeforeUnloadFired(content::WebContents* contents, bool proceed);
bool is_attempting_to_close_browser() const {
return is_attempting_to_close_browser_;
}
// Called in response to a request to close |browser_|'s window. Returns
// `BrowserClosingStatus::kPermitted` if the window can be closed (or other
// enum values if closure is not permitted for a given reason).
BrowserClosingStatus GetBrowserClosingStatus();
// Begins the process of confirming whether the associated browser can be
// closed. Beforeunload events won't be fired if |skip_beforeunload|
// is true.
bool TryToCloseWindow(
bool skip_beforeunload,
const base::RepeatingCallback<void(bool)>& on_close_confirmed);
// Clears the results of any beforeunload confirmation dialogs triggered by a
// TryToCloseWindow call.
void ResetTryToCloseWindow();
// Returns true if |browser_| has any tabs that have BeforeUnload handlers
// that have not been fired. This method is non-const because it builds a list
// of tabs that need their BeforeUnloadHandlers fired.
// TODO(beng): This seems like it could be private but it is used by
// AreAllBrowsersCloseable() in application_lifetime.cc. It seems
// very similar to ShouldCloseWindow() and some consolidation
// could be pursued.
bool TabsNeedBeforeUnloadFired() const;
// Clears all the state associated with processing tabs' beforeunload/unload
// events since the user cancelled closing the window.
void CancelWindowClose();
private:
typedef std::set<raw_ptr<content::WebContents, SetExperimental>>
UnloadListenerSet;
// WebContentsCollection::Observer:
void RenderProcessGone(content::WebContents* web_contents,
base::TerminationStatus status) override;
// Overridden from TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
void TabStripEmpty() override;
void TabAttachedImpl(content::WebContents* contents);
void TabDetachedImpl(content::WebContents* contents);
UnloadListenerSet GetTabsNeedingBeforeUnloadFired() const;
// Processes the next tab that needs it's beforeunload/unload event fired.
void ProcessPendingTabs(bool skip_beforeunload);
// Whether we've completed firing all the tabs' beforeunload/unload events.
bool HasCompletedUnloadProcessing() const;
// Removes |web_contents| from the passed |set|.
// Returns whether the tab was in the set in the first place.
bool RemoveFromSet(UnloadListenerSet* set,
content::WebContents* web_contents);
// Cleans up state appropriately when we are trying to close the browser and
// the tab has finished firing its unload handler. We also use this in the
// cases where a tab crashes or hangs even if the beforeunload/unload haven't
// successfully fired. If |process_now| is true |ProcessPendingTabs| is
// invoked immediately, otherwise it is invoked after a delay (PostTask).
//
// Typically you'll want to pass in true for |process_now|. Passing in true
// may result in deleting |tab|. If you know that shouldn't happen (because of
// the state of the stack), pass in false.
void ClearUnloadState(content::WebContents* web_contents, bool process_now);
bool IsUnclosableApp() const;
bool is_calling_before_unload_handlers() {
return !on_close_confirmed_.is_null();
}
const raw_ptr<Browser> browser_;
WebContentsCollection web_contents_collection_;
// Tracks tabs that need their beforeunload event fired before we can
// close the browser. Only gets populated when we try to close the browser.
UnloadListenerSet tabs_needing_before_unload_fired_;
// Tracks tabs that need their unload event fired before we can
// close the browser. Only gets populated when we try to close the browser.
UnloadListenerSet tabs_needing_unload_fired_;
// Whether we are processing the beforeunload and unload events of each tab
// in preparation for closing the browser. UnloadController owns this state
// rather than Browser because unload handlers are the only reason that a
// Browser window isn't just immediately closed.
bool is_attempting_to_close_browser_;
// A callback to call to report whether the user chose to close all tabs of
// |browser_| that have beforeunload event handlers. This is set only if we
// are currently confirming that the browser is closable. This can be called
// more than once if a user confirms all the beforeunload prompts (at which
// point it will be called with true) but the window close is later aborted
// (at which point it will be called with false). This can happen when
// multiple browser windows are being closed together. See
// BrowserList::TryToCloseBrowserList.
base::RepeatingCallback<void(bool)> on_close_confirmed_;
base::WeakPtrFactory<UnloadController> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_UNLOAD_CONTROLLER_H_
|