Home | History | Annotate | Download | only in web_modal
      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() const {
     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