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/notification_details.h" 11 #include "content/public/browser/notification_source.h" 12 #include "content/public/browser/notification_types.h" 13 #include "content/public/browser/render_view_host.h" 14 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents_view.h" 16 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 17 18 using content::WebContents; 19 20 DEFINE_WEB_CONTENTS_USER_DATA_KEY(web_modal::WebContentsModalDialogManager); 21 22 namespace web_modal { 23 24 WebContentsModalDialogManager::~WebContentsModalDialogManager() { 25 DCHECK(child_dialogs_.empty()); 26 } 27 28 void WebContentsModalDialogManager::ShowDialog( 29 NativeWebContentsModalDialog dialog) { 30 child_dialogs_.push_back(dialog); 31 32 native_manager_->ManageDialog(dialog); 33 34 if (child_dialogs_.size() == 1) { 35 if (delegate_ && delegate_->IsWebContentsVisible(web_contents())) 36 native_manager_->ShowDialog(dialog); 37 BlockWebContentsInteraction(true); 38 } 39 } 40 41 bool WebContentsModalDialogManager::IsShowingDialog() const { 42 return !child_dialogs_.empty(); 43 } 44 45 void WebContentsModalDialogManager::FocusTopmostDialog() { 46 DCHECK(!child_dialogs_.empty()); 47 native_manager_->FocusDialog(child_dialogs_.front()); 48 } 49 50 content::WebContents* WebContentsModalDialogManager::GetWebContents() const { 51 return web_contents(); 52 } 53 54 void WebContentsModalDialogManager::WillClose( 55 NativeWebContentsModalDialog dialog) { 56 WebContentsModalDialogList::iterator i( 57 std::find(child_dialogs_.begin(), child_dialogs_.end(), dialog)); 58 59 // The Views tab contents modal dialog calls WillClose twice. Ignore the 60 // second invocation. 61 if (i == child_dialogs_.end()) 62 return; 63 64 bool removed_topmost_dialog = i == child_dialogs_.begin(); 65 child_dialogs_.erase(i); 66 if (!child_dialogs_.empty() && removed_topmost_dialog && 67 !closing_all_dialogs_) 68 native_manager_->ShowDialog(child_dialogs_.front()); 69 70 BlockWebContentsInteraction(!child_dialogs_.empty()); 71 } 72 73 void WebContentsModalDialogManager::Observe( 74 int type, 75 const content::NotificationSource& source, 76 const content::NotificationDetails& details) { 77 DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED); 78 79 if (child_dialogs_.empty()) 80 return; 81 82 bool visible = *content::Details<bool>(details).ptr(); 83 if (visible) 84 native_manager_->ShowDialog(child_dialogs_.front()); 85 else 86 native_manager_->HideDialog(child_dialogs_.front()); 87 } 88 89 WebContentsModalDialogManager::WebContentsModalDialogManager( 90 content::WebContents* web_contents) 91 : content::WebContentsObserver(web_contents), 92 delegate_(NULL), 93 native_manager_(CreateNativeManager(this)), 94 closing_all_dialogs_(false) { 95 DCHECK(native_manager_); 96 registrar_.Add(this, 97 content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED, 98 content::Source<content::WebContents>(web_contents)); 99 } 100 101 void WebContentsModalDialogManager::BlockWebContentsInteraction(bool blocked) { 102 WebContents* contents = web_contents(); 103 if (!contents) { 104 // The WebContents has already disconnected. 105 return; 106 } 107 108 // RenderViewHost may be NULL during shutdown. 109 content::RenderViewHost* host = contents->GetRenderViewHost(); 110 if (host) 111 host->SetIgnoreInputEvents(blocked); 112 if (delegate_) 113 delegate_->SetWebContentsBlocked(contents, blocked); 114 } 115 116 void WebContentsModalDialogManager::CloseAllDialogs() { 117 closing_all_dialogs_ = true; 118 119 // Clear out any dialogs since we are leaving this page entirely. 120 while (!child_dialogs_.empty()) 121 native_manager_->CloseDialog(child_dialogs_.front()); 122 123 closing_all_dialogs_ = false; 124 } 125 126 void WebContentsModalDialogManager::DidNavigateMainFrame( 127 const content::LoadCommittedDetails& details, 128 const content::FrameNavigateParams& params) { 129 // Close constrained windows if necessary. 130 if (!net::registry_controlled_domains::SameDomainOrHost( 131 details.previous_url, details.entry->GetURL(), 132 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) 133 CloseAllDialogs(); 134 } 135 136 void WebContentsModalDialogManager::DidGetIgnoredUIEvent() { 137 if (!child_dialogs_.empty()) 138 native_manager_->FocusDialog(child_dialogs_.front()); 139 } 140 141 void WebContentsModalDialogManager::WebContentsDestroyed(WebContents* tab) { 142 // First cleanly close all child dialogs. 143 // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked 144 // some of these to close. CloseAllDialogs is async, so it might get called 145 // twice before it runs. 146 CloseAllDialogs(); 147 } 148 149 } // namespace web_modal 150