123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef COMPONENTS_JAVASCRIPT_DIALOGS_TAB_MODAL_DIALOG_MANAGER_H_
- #define COMPONENTS_JAVASCRIPT_DIALOGS_TAB_MODAL_DIALOG_MANAGER_H_
- #include <memory>
- #include "base/callback_forward.h"
- #include "base/memory/weak_ptr.h"
- #include "build/build_config.h"
- #include "components/javascript_dialogs/tab_modal_dialog_manager_delegate.h"
- #include "components/javascript_dialogs/tab_modal_dialog_view.h"
- #include "content/public/browser/javascript_dialog_manager.h"
- #include "content/public/browser/web_contents_observer.h"
- #include "content/public/browser/web_contents_user_data.h"
- namespace javascript_dialogs {
- // A class that serves as the JavaScriptDialogManager for tab modal JavaScript
- // dialogs.
- //
- // This implements two different functionalities for JavaScript dialogs.
- //
- // window.alert() dialogs are tab-modal dialogs. If a tab calls alert() while it
- // is foremost, a dialog is displayed and the renderer is held blocked. When the
- // user switches to a different tab, or if the dialog is shown while the tab is
- // not foremost, while the dialog is shown, the renderer is not held blocked.
- //
- // window.confirm() and window.prompt() dialogs are auto-dismissing,
- // dialogs that close when the user switches away to a different tab. Because
- // JavaScript dialogs are synchronous and block arbitrary sets of renderers,
- // they cannot be made tab-modal. Therefore the next best option is to make them
- // auto-closing, so that they never block the user's access to other renderers.
- //
- // References:
- // http://bit.ly/project-oldspice
- class TabModalDialogManager
- : public content::JavaScriptDialogManager,
- public content::WebContentsObserver,
- public content::WebContentsUserData<TabModalDialogManager> {
- public:
- enum class DismissalCause {
- // This is used for a UMA histogram. Please never alter existing values,
- // only append new ones.
- // The tab helper is destroyed. By current design, the dialog is always torn
- // down before the tab helper is destroyed, so we never see the
- // |kTabHelperDestroyed| enum. However, that might not always be the case.
- // It's better to have a simple rule in the code of "when you close a dialog
- // you must provide a UMA enum reason why" and have some enums that never
- // happen than have haphazard code that enforces no rules.
- kTabHelperDestroyed = 0,
- // Subsequent dialog pops up.
- kSubsequentDialogShown = 1,
- // HandleJavaScriptDialog() is called. In practice, this can happen whenever
- // browser choose to accept/cancel the dialog without user's interaction.
- kHandleDialogCalled = 2,
- // CancelDialogs() is called. In practice, this can happen whenever browser
- // choose to close the dialog without user's interaction. Besides, this can
- // also happen when tab is closed by user on a Mac platform.
- kCancelDialogsCalled = 3,
- // Tab is made hidden by opening a new tab, switching to another tab, etc.
- // Note that only Prompt() and Confirm() can be dismissed for this cause;
- // it won't affect Alert().
- kTabHidden = 4,
- // Another browser instance is made active.
- kBrowserSwitched = 5,
- // Accept/Cancel button is clicked by user.
- kDialogButtonClicked = 6,
- // Navigation occurs.
- kTabNavigated = 7,
- // Tab's contents was replaced.
- kTabSwitchedOut = 8,
- // CloseDialog() is called. In practice, this happens when tab is closed by
- // user on a non-Mac platform.
- kDialogClosed = 9,
- kMaxValue = kDialogClosed,
- };
- TabModalDialogManager(const TabModalDialogManager&) = delete;
- TabModalDialogManager& operator=(const TabModalDialogManager&) = delete;
- ~TabModalDialogManager() override;
- void BrowserActiveStateChanged();
- void CloseDialogWithReason(DismissalCause reason);
- void SetDialogShownCallbackForTesting(base::OnceClosure callback);
- bool IsShowingDialogForTesting() const;
- void ClickDialogButtonForTesting(bool accept,
- const std::u16string& user_input);
- using DialogDismissedCallback = base::OnceCallback<void(DismissalCause)>;
- void SetDialogDismissedCallbackForTesting(DialogDismissedCallback callback);
- // JavaScriptDialogManager:
- void RunJavaScriptDialog(content::WebContents* web_contents,
- content::RenderFrameHost* render_frame_host,
- content::JavaScriptDialogType dialog_type,
- const std::u16string& message_text,
- const std::u16string& default_prompt_text,
- DialogClosedCallback callback,
- bool* did_suppress_message) override;
- void RunBeforeUnloadDialog(content::WebContents* web_contents,
- content::RenderFrameHost* render_frame_host,
- bool is_reload,
- DialogClosedCallback callback) override;
- bool HandleJavaScriptDialog(content::WebContents* web_contents,
- bool accept,
- const std::u16string* prompt_override) override;
- void CancelDialogs(content::WebContents* web_contents,
- bool reset_state) override;
- // WebContentsObserver:
- void OnVisibilityChanged(content::Visibility visibility) override;
- void DidStartNavigation(
- content::NavigationHandle* navigation_handle) override;
- private:
- friend class content::WebContentsUserData<TabModalDialogManager>;
- TabModalDialogManager(
- content::WebContents* web_contents,
- std::unique_ptr<TabModalDialogManagerDelegate> delegate);
- // Logs the cause of a dialog dismissal in UMA.
- void LogDialogDismissalCause(DismissalCause cause);
- // Handles the case when the user switches away from a tab.
- void HandleTabSwitchAway(DismissalCause cause);
- // This closes any open dialog. It is safe to call if there is no currently
- // open dialog.
- void CloseDialog(DismissalCause cause,
- bool success,
- const std::u16string& user_input);
- // There can be at most one dialog (pending or not) being shown at any given
- // time on a tab. Depending on the type of the dialog, the variables
- // |dialog_|, |pending_dialog_|, and |dialog_callback_| can be present in
- // different combinations.
- //
- // No dialog:
- // |dialog_|, |pending_dialog_|, and |dialog_callback_| are null.
- // alert() dialog:
- // Either |dialog_| or |pending_dialog_| is not null. If the dialog is shown
- // while the tab was foremost, the dialog is be created and a weak pointer
- // to it is held in |dialog_|. If the dialog is attempted while the tab
- // is not foremost, the call to create the dialog-to-be is held in
- // |pending_dialog_| until the tab is brought foremost. At that time the
- // callback will be made, |pending_dialog_| will be null, and the dialog
- // will live, referenced by |dialog_|. As for |dialog_callback_|, if the
- // dialog is shown while the tab was foremost, |dialog_callback_| is not
- // null. If the dialog was shown while the tab was not foremost, or if the
- // tab was switched to be non-foremost, the renderer is not held blocked,
- // and |dialog_callback_| will be null (because it will have been called to
- // free up the renderer.)
- // confirm() and prompt() dialogs:
- // Both |dialog_| and |dialog_callback_| are not null. |pending_dialog_| is
- // null, as only alert() dialogs can be in a pending state.
- // The dialog being displayed on the observed WebContents, if any. At any
- // given time at most one of |dialog_| and |pending_dialog_| can be non-null.
- base::WeakPtr<TabModalDialogView> dialog_;
- base::OnceCallback<base::WeakPtr<TabModalDialogView>()> pending_dialog_;
- // The callback to return a result for a dialog. Not null if the renderer is
- // waiting for a result; null if there is no |dialog_| or if the dialog is an
- // alert() dialog and the callback has already been called.
- content::JavaScriptDialogManager::DialogClosedCallback dialog_callback_;
- // The type of dialog being displayed. Only valid when |dialog_| or
- // |pending_dialog_| is non-null.
- content::JavaScriptDialogType dialog_type_ =
- content::JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT;
- // A closure to be fired when a dialog is shown. For testing only.
- base::OnceClosure dialog_shown_;
- // A closure to be fired when a dialog is dismissed. For testing only.
- DialogDismissedCallback dialog_dismissed_;
- std::unique_ptr<TabModalDialogManagerDelegate> delegate_;
- WEB_CONTENTS_USER_DATA_KEY_DECL();
- };
- } // namespace javascript_dialogs
- #endif // COMPONENTS_JAVASCRIPT_DIALOGS_TAB_MODAL_DIALOG_MANAGER_H_
|