• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
8 #include "content/public/browser/navigation_details.h"
9 #include "content/public/browser/navigation_entry.h"
10 #include "content/public/browser/render_view_host.h"
11 #include "content/public/browser/web_contents.h"
12 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
13 
14 using content::WebContents;
15 
16 DEFINE_WEB_CONTENTS_USER_DATA_KEY(web_modal::WebContentsModalDialogManager);
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 (WebContentsModalDialogList::iterator it = child_dialogs_.begin();
29        it != child_dialogs_.end(); it++) {
30     // Delegate can be NULL on Views/Win32 during tab drag.
31     (*it)->manager->HostChanged(d ? d->GetWebContentsModalDialogHost() : NULL);
32   }
33 }
34 
ShowModalDialog(NativeWebContentsModalDialog dialog)35 void WebContentsModalDialogManager::ShowModalDialog(
36     NativeWebContentsModalDialog dialog) {
37   scoped_ptr<SingleWebContentsDialogManager> mgr(
38       CreateNativeWebModalManager(dialog, this));
39   ShowDialogWithManager(dialog, mgr.Pass());
40 }
41 
42 // TODO(gbillock): Maybe "ShowBubbleWithManager"?
ShowDialogWithManager(NativeWebContentsModalDialog dialog,scoped_ptr<SingleWebContentsDialogManager> manager)43 void WebContentsModalDialogManager::ShowDialogWithManager(
44     NativeWebContentsModalDialog dialog,
45     scoped_ptr<SingleWebContentsDialogManager> manager) {
46   if (delegate_)
47     manager->HostChanged(delegate_->GetWebContentsModalDialogHost());
48   child_dialogs_.push_back(new DialogState(dialog, manager.Pass()));
49 
50   if (child_dialogs_.size() == 1) {
51     BlockWebContentsInteraction(true);
52     if (delegate_ && delegate_->IsWebContentsVisible(web_contents()))
53       child_dialogs_.back()->manager->Show();
54   }
55 }
56 
IsDialogActive() const57 bool WebContentsModalDialogManager::IsDialogActive() const {
58   return !child_dialogs_.empty();
59 }
60 
FocusTopmostDialog()61 void WebContentsModalDialogManager::FocusTopmostDialog() {
62   DCHECK(!child_dialogs_.empty());
63   child_dialogs_.front()->manager->Focus();
64 }
65 
GetWebContents() const66 content::WebContents* WebContentsModalDialogManager::GetWebContents() const {
67   return web_contents();
68 }
69 
WillClose(NativeWebContentsModalDialog dialog)70 void WebContentsModalDialogManager::WillClose(
71     NativeWebContentsModalDialog dialog) {
72   WebContentsModalDialogList::iterator dlg = FindDialogState(dialog);
73 
74   // The Views tab contents modal dialog calls WillClose twice.  Ignore the
75   // second invocation.
76   if (dlg == child_dialogs_.end())
77     return;
78 
79   bool removed_topmost_dialog = dlg == child_dialogs_.begin();
80   scoped_ptr<DialogState> deleter(*dlg);
81   child_dialogs_.erase(dlg);
82   if (!child_dialogs_.empty() && removed_topmost_dialog &&
83       !closing_all_dialogs_) {
84     child_dialogs_.front()->manager->Show();
85   }
86 
87   BlockWebContentsInteraction(!child_dialogs_.empty());
88 }
89 
WebContentsModalDialogManager(content::WebContents * web_contents)90 WebContentsModalDialogManager::WebContentsModalDialogManager(
91     content::WebContents* web_contents)
92     : content::WebContentsObserver(web_contents),
93       delegate_(NULL),
94       closing_all_dialogs_(false) {
95 }
96 
DialogState(NativeWebContentsModalDialog dialog,scoped_ptr<SingleWebContentsDialogManager> mgr)97 WebContentsModalDialogManager::DialogState::DialogState(
98     NativeWebContentsModalDialog dialog,
99     scoped_ptr<SingleWebContentsDialogManager> mgr)
100     : dialog(dialog),
101       manager(mgr.release()) {
102 }
103 
~DialogState()104 WebContentsModalDialogManager::DialogState::~DialogState() {}
105 
106 WebContentsModalDialogManager::WebContentsModalDialogList::iterator
FindDialogState(NativeWebContentsModalDialog dialog)107     WebContentsModalDialogManager::FindDialogState(
108         NativeWebContentsModalDialog dialog) {
109   WebContentsModalDialogList::iterator i;
110   for (i = child_dialogs_.begin(); i != child_dialogs_.end(); ++i) {
111     if ((*i)->dialog == dialog)
112       break;
113   }
114 
115   return i;
116 }
117 
118 // TODO(gbillock): Move this to Views impl within Show()? It would
119 // call WebContents* contents = native_delegate_->GetWebContents(); and
120 // then set the block state. Advantage: could restrict some of the
121 // WCMDM delegate methods, then, and pass them behind the scenes.
BlockWebContentsInteraction(bool blocked)122 void WebContentsModalDialogManager::BlockWebContentsInteraction(bool blocked) {
123   WebContents* contents = web_contents();
124   if (!contents) {
125     // The WebContents has already disconnected.
126     return;
127   }
128 
129   // RenderViewHost may be NULL during shutdown.
130   content::RenderViewHost* host = contents->GetRenderViewHost();
131   if (host)
132     host->SetIgnoreInputEvents(blocked);
133   if (delegate_)
134     delegate_->SetWebContentsBlocked(contents, blocked);
135 }
136 
CloseAllDialogs()137 void WebContentsModalDialogManager::CloseAllDialogs() {
138   closing_all_dialogs_ = true;
139 
140   // Clear out any dialogs since we are leaving this page entirely.
141   while (!child_dialogs_.empty()) {
142     child_dialogs_.front()->manager->Close();
143   }
144 
145   closing_all_dialogs_ = false;
146 }
147 
DidNavigateMainFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)148 void WebContentsModalDialogManager::DidNavigateMainFrame(
149     const content::LoadCommittedDetails& details,
150     const content::FrameNavigateParams& params) {
151   // Close constrained windows if necessary.
152   if (!net::registry_controlled_domains::SameDomainOrHost(
153           details.previous_url, details.entry->GetURL(),
154           net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES))
155     CloseAllDialogs();
156 }
157 
DidGetIgnoredUIEvent()158 void WebContentsModalDialogManager::DidGetIgnoredUIEvent() {
159   if (!child_dialogs_.empty()) {
160     child_dialogs_.front()->manager->Focus();
161   }
162 }
163 
WasShown()164 void WebContentsModalDialogManager::WasShown() {
165   if (!child_dialogs_.empty())
166     child_dialogs_.front()->manager->Show();
167 }
168 
WasHidden()169 void WebContentsModalDialogManager::WasHidden() {
170   if (!child_dialogs_.empty())
171     child_dialogs_.front()->manager->Hide();
172 }
173 
WebContentsDestroyed()174 void WebContentsModalDialogManager::WebContentsDestroyed() {
175   // First cleanly close all child dialogs.
176   // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
177   // some of these to close.  CloseAllDialogs is async, so it might get called
178   // twice before it runs.
179   CloseAllDialogs();
180 }
181 
DidAttachInterstitialPage()182 void WebContentsModalDialogManager::DidAttachInterstitialPage() {
183   // TODO(wittman): Test closing on interstitial webui works properly on Mac.
184 #if defined(USE_AURA)
185   CloseAllDialogs();
186 #endif
187 }
188 
189 }  // namespace web_modal
190