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 20 WebContentsModalDialogManager::~WebContentsModalDialogManager() { 21 DCHECK(child_dialogs_.empty()); 22 } 23 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 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"? 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 57 bool WebContentsModalDialogManager::IsDialogActive() const { 58 return !child_dialogs_.empty(); 59 } 60 61 void WebContentsModalDialogManager::FocusTopmostDialog() { 62 DCHECK(!child_dialogs_.empty()); 63 child_dialogs_.front()->manager->Focus(); 64 } 65 66 content::WebContents* WebContentsModalDialogManager::GetWebContents() const { 67 return web_contents(); 68 } 69 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 90 WebContentsModalDialogManager::WebContentsModalDialogManager( 91 content::WebContents* web_contents) 92 : content::WebContentsObserver(web_contents), 93 delegate_(NULL), 94 closing_all_dialogs_(false) { 95 } 96 97 WebContentsModalDialogManager::DialogState::DialogState( 98 NativeWebContentsModalDialog dialog, 99 scoped_ptr<SingleWebContentsDialogManager> mgr) 100 : dialog(dialog), 101 manager(mgr.release()) { 102 } 103 104 WebContentsModalDialogManager::DialogState::~DialogState() {} 105 106 WebContentsModalDialogManager::WebContentsModalDialogList::iterator 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. 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 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 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 158 void WebContentsModalDialogManager::DidGetIgnoredUIEvent() { 159 if (!child_dialogs_.empty()) { 160 child_dialogs_.front()->manager->Focus(); 161 } 162 } 163 164 void WebContentsModalDialogManager::WasShown() { 165 if (!child_dialogs_.empty()) 166 child_dialogs_.front()->manager->Show(); 167 } 168 169 void WebContentsModalDialogManager::WasHidden() { 170 if (!child_dialogs_.empty()) 171 child_dialogs_.front()->manager->Hide(); 172 } 173 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 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