1 // Copyright (c) 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 "components/web_modal/web_contents_modal_dialog_manager.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/check.h"
11 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
12 #include "content/public/browser/navigation_handle.h"
13 #include "content/public/browser/web_contents.h"
14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
15
16 using content::WebContents;
17
18 namespace web_modal {
19
~WebContentsModalDialogManager()20 WebContentsModalDialogManager::~WebContentsModalDialogManager() {
21 DCHECK(child_dialogs_.empty());
22 }
23
SetDelegate(WebContentsModalDialogManagerDelegate * d)24 void WebContentsModalDialogManager::SetDelegate(
25 WebContentsModalDialogManagerDelegate* d) {
26 delegate_ = d;
27
28 for (const auto& dialog : child_dialogs_) {
29 // Delegate can be null on Views/Win32 during tab drag.
30 dialog.manager->HostChanged(d ? d->GetWebContentsModalDialogHost()
31 : nullptr);
32 }
33 }
34
35 // TODO(gbillock): Maybe "ShowBubbleWithManager"?
ShowDialogWithManager(gfx::NativeWindow dialog,std::unique_ptr<SingleWebContentsDialogManager> manager)36 void WebContentsModalDialogManager::ShowDialogWithManager(
37 gfx::NativeWindow dialog,
38 std::unique_ptr<SingleWebContentsDialogManager> manager) {
39 if (delegate_)
40 manager->HostChanged(delegate_->GetWebContentsModalDialogHost());
41 child_dialogs_.emplace_back(dialog, std::move(manager));
42
43 if (child_dialogs_.size() == 1) {
44 BlockWebContentsInteraction(true);
45 if (delegate_ && delegate_->IsWebContentsVisible(web_contents()))
46 child_dialogs_.back().manager->Show();
47 }
48 }
49
IsDialogActive() const50 bool WebContentsModalDialogManager::IsDialogActive() const {
51 return !child_dialogs_.empty();
52 }
53
FocusTopmostDialog() const54 void WebContentsModalDialogManager::FocusTopmostDialog() const {
55 DCHECK(!child_dialogs_.empty());
56 child_dialogs_.front().manager->Focus();
57 }
58
GetWebContents() const59 content::WebContents* WebContentsModalDialogManager::GetWebContents() const {
60 return web_contents();
61 }
62
WillClose(gfx::NativeWindow dialog)63 void WebContentsModalDialogManager::WillClose(gfx::NativeWindow dialog) {
64 auto dlg = std::find_if(child_dialogs_.begin(), child_dialogs_.end(),
65 [dialog](const DialogState& child_dialog) {
66 return child_dialog.dialog == dialog;
67 });
68
69 // The Views tab contents modal dialog calls WillClose twice. Ignore the
70 // second invocation.
71 if (dlg == child_dialogs_.end())
72 return;
73
74 bool removed_topmost_dialog = dlg == child_dialogs_.begin();
75 child_dialogs_.erase(dlg);
76 if (!closing_all_dialogs_ &&
77 (!child_dialogs_.empty() && removed_topmost_dialog) &&
78 (delegate_ && delegate_->IsWebContentsVisible(web_contents()))) {
79 child_dialogs_.front().manager->Show();
80 }
81
82 BlockWebContentsInteraction(!child_dialogs_.empty());
83 }
84
WebContentsModalDialogManager(content::WebContents * web_contents)85 WebContentsModalDialogManager::WebContentsModalDialogManager(
86 content::WebContents* web_contents)
87 : content::WebContentsObserver(web_contents),
88 delegate_(nullptr),
89 web_contents_is_hidden_(web_contents->GetVisibility() ==
90 content::Visibility::HIDDEN),
91 closing_all_dialogs_(false) {}
92
DialogState(gfx::NativeWindow dialog,std::unique_ptr<SingleWebContentsDialogManager> mgr)93 WebContentsModalDialogManager::DialogState::DialogState(
94 gfx::NativeWindow dialog,
95 std::unique_ptr<SingleWebContentsDialogManager> mgr)
96 : dialog(dialog), manager(std::move(mgr)) {}
97
98 WebContentsModalDialogManager::DialogState::DialogState(DialogState&& state) =
99 default;
100
101 WebContentsModalDialogManager::DialogState::~DialogState() = default;
102
103 // TODO(gbillock): Move this to Views impl within Show()? It would
104 // call WebContents* contents = native_delegate_->GetWebContents(); and
105 // then set the block state. Advantage: could restrict some of the
106 // WCMDM delegate methods, then, and pass them behind the scenes.
BlockWebContentsInteraction(bool blocked)107 void WebContentsModalDialogManager::BlockWebContentsInteraction(bool blocked) {
108 WebContents* contents = web_contents();
109 if (!contents) {
110 // The WebContents has already disconnected.
111 return;
112 }
113
114 contents->SetIgnoreInputEvents(blocked);
115 if (delegate_)
116 delegate_->SetWebContentsBlocked(contents, blocked);
117 }
118
CloseAllDialogs()119 void WebContentsModalDialogManager::CloseAllDialogs() {
120 closing_all_dialogs_ = true;
121
122 // Clear out any dialogs since we are leaving this page entirely.
123 while (!child_dialogs_.empty()) {
124 child_dialogs_.front().manager->Close();
125 }
126
127 closing_all_dialogs_ = false;
128 }
129
DidFinishNavigation(content::NavigationHandle * navigation_handle)130 void WebContentsModalDialogManager::DidFinishNavigation(
131 content::NavigationHandle* navigation_handle) {
132 if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
133 return;
134
135 // Close constrained windows if necessary.
136 if (!net::registry_controlled_domains::SameDomainOrHost(
137 navigation_handle->GetPreviousURL(), navigation_handle->GetURL(),
138 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES))
139 CloseAllDialogs();
140 }
141
DidGetIgnoredUIEvent()142 void WebContentsModalDialogManager::DidGetIgnoredUIEvent() {
143 if (!child_dialogs_.empty()) {
144 child_dialogs_.front().manager->Focus();
145 }
146 }
147
OnVisibilityChanged(content::Visibility visibility)148 void WebContentsModalDialogManager::OnVisibilityChanged(
149 content::Visibility visibility) {
150 const bool web_contents_was_hidden = web_contents_is_hidden_;
151 web_contents_is_hidden_ = visibility == content::Visibility::HIDDEN;
152
153 // Avoid reshowing on transitions between VISIBLE and OCCLUDED.
154 if (child_dialogs_.empty() ||
155 web_contents_is_hidden_ == web_contents_was_hidden) {
156 return;
157 }
158
159 if (web_contents_is_hidden_)
160 child_dialogs_.front().manager->Hide();
161 else
162 child_dialogs_.front().manager->Show();
163 }
164
WebContentsDestroyed()165 void WebContentsModalDialogManager::WebContentsDestroyed() {
166 // First cleanly close all child dialogs.
167 // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
168 // some of these to close. CloseAllDialogs is async, so it might get called
169 // twice before it runs.
170 CloseAllDialogs();
171 }
172
173 WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsModalDialogManager)
174
175 } // namespace web_modal
176