Index: chrome/browser/google/google_url_tracker.cc |
=================================================================== |
--- chrome/browser/google/google_url_tracker.cc (revision 145827) |
+++ chrome/browser/google/google_url_tracker.cc (working copy) |
@@ -23,6 +23,7 @@ |
#include "chrome/common/chrome_switches.h" |
#include "chrome/common/pref_names.h" |
#include "content/public/browser/navigation_controller.h" |
+#include "content/public/browser/navigation_details.h" |
#include "content/public/browser/navigation_entry.h" |
#include "content/public/browser/notification_service.h" |
#include "content/public/browser/web_contents.h" |
@@ -38,11 +39,10 @@ |
GoogleURLTrackerInfoBarDelegate* CreateInfoBar( |
InfoBarTabHelper* infobar_helper, |
- const GURL& search_url, |
GoogleURLTracker* google_url_tracker, |
const GURL& new_google_url) { |
- return new GoogleURLTrackerInfoBarDelegate(infobar_helper, search_url, |
- google_url_tracker, new_google_url); |
+ return new GoogleURLTrackerInfoBarDelegate(infobar_helper, google_url_tracker, |
+ new_google_url); |
} |
string16 GetHost(const GURL& url) { |
@@ -56,15 +56,14 @@ |
GoogleURLTrackerInfoBarDelegate::GoogleURLTrackerInfoBarDelegate( |
InfoBarTabHelper* infobar_helper, |
- const GURL& search_url, |
GoogleURLTracker* google_url_tracker, |
const GURL& new_google_url) |
: ConfirmInfoBarDelegate(infobar_helper), |
map_key_(infobar_helper), |
- search_url_(search_url), |
google_url_tracker_(google_url_tracker), |
new_google_url_(new_google_url), |
- showing_(false) { |
+ showing_(false), |
+ pending_id_(0) { |
} |
bool GoogleURLTrackerInfoBarDelegate::Accept() { |
@@ -92,30 +91,30 @@ |
return false; |
} |
+bool GoogleURLTrackerInfoBarDelegate::ShouldExpireInternal( |
+ const content::LoadCommittedDetails& details) const { |
+ int unique_id = details.entry->GetUniqueID(); |
+ return (unique_id != contents_unique_id()) && (unique_id != pending_id_); |
+} |
+ |
void GoogleURLTrackerInfoBarDelegate::SetGoogleURL(const GURL& new_google_url) { |
DCHECK_EQ(GetHost(new_google_url_), GetHost(new_google_url)); |
new_google_url_ = new_google_url; |
} |
-void GoogleURLTrackerInfoBarDelegate::Show() { |
- showing_ = true; |
- owner()->AddInfoBar(this); // May delete |this| on failure! |
+void GoogleURLTrackerInfoBarDelegate::Show(const GURL& search_url) { |
+ if (!owner()) |
+ return; |
+ StoreActiveEntryUniqueID(owner()); |
+ search_url_ = search_url; |
+ pending_id_ = 0; |
+ if (!showing_) { |
+ showing_ = true; |
+ owner()->AddInfoBar(this); // May delete |this| on failure! |
+ } |
} |
void GoogleURLTrackerInfoBarDelegate::Close(bool redo_search) { |
- if (redo_search && owner()) { |
- // Re-do the user's search on the new domain. |
- url_canon::Replacements<char> replacements; |
- const std::string& host(new_google_url_.host()); |
- replacements.SetHost(host.data(), url_parse::Component(0, host.length())); |
- GURL new_search_url(search_url_.ReplaceComponents(replacements)); |
- if (new_search_url.is_valid()) { |
- content::OpenURLParams params(new_search_url, content::Referrer(), |
- CURRENT_TAB, content::PAGE_TRANSITION_GENERATED, false); |
- owner()->web_contents()->OpenURL(params); |
- } |
- } |
- |
if (!showing_) { |
// We haven't been added to a tab, so just delete ourselves. |
delete this; |
@@ -136,8 +135,24 @@ |
// subsequently reached here due to GoogleURLTracker::CloseAllInfoBars(). |
// This case will no longer happen once infobars are refactored to own their |
// delegates. |
- if (owner()) |
- owner()->RemoveInfoBar(this); |
+ if (!owner()) |
+ return; |
+ |
+ if (redo_search) { |
+ // Re-do the user's search on the new domain. |
+ DCHECK(search_url_.is_valid()); |
+ url_canon::Replacements<char> replacements; |
+ const std::string& host(new_google_url_.host()); |
+ replacements.SetHost(host.data(), url_parse::Component(0, host.length())); |
+ GURL new_search_url(search_url_.ReplaceComponents(replacements)); |
+ if (new_search_url.is_valid()) { |
+ content::OpenURLParams params(new_search_url, content::Referrer(), |
+ CURRENT_TAB, content::PAGE_TRANSITION_GENERATED, false); |
+ owner()->web_contents()->OpenURL(params); |
+ } |
+ } |
+ |
+ owner()->RemoveInfoBar(this); |
} |
GoogleURLTrackerInfoBarDelegate::~GoogleURLTrackerInfoBarDelegate() { |
@@ -160,6 +175,30 @@ |
} |
+// GoogleURLTracker::MapEntry ------------------------------------------------- |
+ |
+// Note that we have to initialize at least the NotificationSources explicitly |
+// lest this not compile, because NotificationSource has no null constructor. |
+GoogleURLTracker::MapEntry::MapEntry() |
+ : navigation_controller_source( |
+ content::Source<content::NavigationController>(NULL)), |
+ tab_contents_source(content::Source<TabContents>(NULL)) { |
+ NOTREACHED(); |
+} |
+ |
+GoogleURLTracker::MapEntry::MapEntry( |
+ GoogleURLTrackerInfoBarDelegate* infobar, |
+ const content::NotificationSource& navigation_controller_source, |
+ const content::NotificationSource& tab_contents_source) |
+ : infobar(infobar), |
+ navigation_controller_source(navigation_controller_source), |
+ tab_contents_source(tab_contents_source) { |
+} |
+ |
+GoogleURLTracker::MapEntry::~MapEntry() { |
+} |
+ |
+ |
// GoogleURLTracker ----------------------------------------------------------- |
const char GoogleURLTracker::kDefaultGoogleHomepage[] = |
@@ -177,7 +216,8 @@ |
in_startup_sleep_(true), |
already_fetched_(false), |
need_to_fetch_(false), |
- need_to_prompt_(false) { |
+ need_to_prompt_(false), |
+ search_committed_(false) { |
net::NetworkChangeNotifier::AddIPAddressObserver(this); |
// Because this function can be called during startup, when kicking off a URL |
@@ -299,7 +339,7 @@ |
} else if (fetched_google_url_ != url) { |
for (InfoBarMap::iterator i(infobar_map_.begin()); |
i != infobar_map_.end(); ++i) |
- i->second->SetGoogleURL(fetched_google_url_); |
+ i->second.infobar->SetGoogleURL(fetched_google_url_); |
} |
} |
} |
@@ -313,29 +353,25 @@ |
content::Source<content::NavigationController>(source).ptr(); |
TabContents* tab_contents = |
TabContents::FromWebContents(controller->GetWebContents()); |
- OnNavigationPending(source, |
- content::Source<TabContents>(tab_contents), |
+ OnNavigationPending(source, content::Source<TabContents>(tab_contents), |
tab_contents->infobar_tab_helper(), |
- controller->GetPendingEntry()->GetURL()); |
+ controller->GetPendingEntry()->GetUniqueID()); |
break; |
} |
case content::NOTIFICATION_NAV_ENTRY_COMMITTED: { |
- TabContents* tab_contents = TabContents::FromWebContents( |
- content::Source<content::NavigationController>(source)-> |
- GetWebContents()); |
- OnNavigationCommittedOrTabClosed(source, |
- content::Source<TabContents>(tab_contents), |
- tab_contents->infobar_tab_helper(), true); |
+ content::NavigationController* controller = |
+ content::Source<content::NavigationController>(source).ptr(); |
+ OnNavigationCommittedOrTabClosed( |
+ TabContents::FromWebContents(controller->GetWebContents())-> |
+ infobar_tab_helper(), |
+ controller->GetActiveEntry()->GetURL()); |
break; |
} |
case chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED: { |
- TabContents* tab_contents = content::Source<TabContents>(source).ptr(); |
OnNavigationCommittedOrTabClosed( |
- content::Source<content::NavigationController>( |
- &tab_contents->web_contents()->GetController()), source, |
- tab_contents->infobar_tab_helper(), false); |
+ content::Source<TabContents>(source)->infobar_tab_helper(), GURL()); |
break; |
} |
@@ -344,12 +380,10 @@ |
content::WebContents* web_contents = tab_contents->web_contents(); |
content::Source<content::NavigationController> source( |
&web_contents->GetController()); |
- content::Source<TabContents> tab_contents_source(tab_contents); |
InfoBarTabHelper* infobar_helper = tab_contents->infobar_tab_helper(); |
- OnNavigationPending(source, tab_contents_source, infobar_helper, |
- web_contents->GetURL()); |
- OnNavigationCommittedOrTabClosed(source, tab_contents_source, |
- infobar_helper, true); |
+ OnNavigationPending(source, content::Source<TabContents>(tab_contents), |
+ infobar_helper, 0); |
+ OnNavigationCommittedOrTabClosed(infobar_helper, web_contents->GetURL()); |
break; |
} |
@@ -393,9 +427,32 @@ |
} |
void GoogleURLTracker::InfoBarClosed(const InfoBarTabHelper* infobar_helper) { |
+ DCHECK(!search_committed_); |
InfoBarMap::iterator i(infobar_map_.find(infobar_helper)); |
DCHECK(i != infobar_map_.end()); |
+ const MapEntry& map_entry = i->second; |
+ |
+ UnregisterForEntrySpecificNotifications(map_entry, false); |
infobar_map_.erase(i); |
+ |
+ // Our global listeners for these other notifications should be in place iff |
+ // we have any non-showing infobars. |
+ for (InfoBarMap::const_iterator i(infobar_map_.begin()); |
+ i != infobar_map_.end(); ++i) { |
+ if (!i->second.infobar->showing()) { |
+ DCHECK(registrar_.IsRegistered(this, |
+ content::NOTIFICATION_NAV_ENTRY_PENDING, |
+ content::NotificationService::AllBrowserContextsAndSources())); |
+ return; |
+ } |
+ } |
+ if (registrar_.IsRegistered(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
+ content::NotificationService::AllBrowserContextsAndSources())) { |
+ registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
+ content::NotificationService::AllBrowserContextsAndSources()); |
+ registrar_.Remove(this, chrome::NOTIFICATION_INSTANT_COMMITTED, |
+ content::NotificationService::AllBrowserContextsAndSources()); |
+ } |
} |
void GoogleURLTracker::SetNeedToFetch() { |
@@ -446,12 +503,16 @@ |
void GoogleURLTracker::SearchCommitted() { |
if (need_to_prompt_) { |
- // This notification will fire a bit later in the same call chain we're |
+ search_committed_ = true; |
+ // These notifications will fire a bit later in the same call chain we're |
// currently in. |
- registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- registrar_.Add(this, chrome::NOTIFICATION_INSTANT_COMMITTED, |
- content::NotificationService::AllBrowserContextsAndSources()); |
+ if (!registrar_.IsRegistered(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
+ content::NotificationService::AllBrowserContextsAndSources())) { |
+ registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
+ content::NotificationService::AllBrowserContextsAndSources()); |
+ registrar_.Add(this, chrome::NOTIFICATION_INSTANT_COMMITTED, |
+ content::NotificationService::AllBrowserContextsAndSources()); |
+ } |
} |
} |
@@ -459,60 +520,110 @@ |
const content::NotificationSource& navigation_controller_source, |
const content::NotificationSource& tab_contents_source, |
InfoBarTabHelper* infobar_helper, |
- const GURL& search_url) { |
- registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING, |
- content::NotificationService::AllBrowserContextsAndSources()); |
- registrar_.Remove(this, chrome::NOTIFICATION_INSTANT_COMMITTED, |
- content::NotificationService::AllBrowserContextsAndSources()); |
+ int pending_id) { |
+ InfoBarMap::iterator i(infobar_map_.find(infobar_helper)); |
- if (registrar_.IsRegistered(this, |
- chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, tab_contents_source)) { |
- // If the previous load hasn't committed and the user triggers a new load, |
- // we don't need to re-register our listeners; just kill the old, |
- // never-shown infobar (to be replaced by a new one below). |
- InfoBarMap::iterator i(infobar_map_.find(infobar_helper)); |
- DCHECK(i != infobar_map_.end()); |
- i->second->Close(false); |
+ if (search_committed_) { |
+ search_committed_ = false; |
+ // Whether there's an existing infobar or not, we need to listen for the |
+ // load to commit, so we can show and/or update the infobar when it does. |
+ // (We may already be registered for this if there is an existing infobar |
+ // that had a previous pending search that hasn't yet committed.) |
+ if (!registrar_.IsRegistered(this, |
+ content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
+ navigation_controller_source)) { |
+ registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
+ navigation_controller_source); |
+ } |
+ if (i == infobar_map_.end()) { |
+ // This is a search on a tab that doesn't have one of our infobars, so add |
+ // one. Note that we only listen for the tab's destruction on this path; |
+ // if there was already an infobar, then either it's not yet showing and |
+ // we're already registered for this, or it is showing and its owner will |
+ // handle tearing it down when the tab is destroyed. |
+ registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, |
+ tab_contents_source); |
+ infobar_map_.insert(std::make_pair(infobar_helper, MapEntry( |
+ (*infobar_creator_)(infobar_helper, this, fetched_google_url_), |
+ navigation_controller_source, tab_contents_source))); |
+ } else { |
+ // This is a new search on a tab where we already have an infobar (which |
+ // may or may not be showing). |
+ i->second.infobar->set_pending_id(pending_id); |
+ } |
+ } else if (i != infobar_map_.end()){ |
+ if (i->second.infobar->showing()) { |
+ // This is a non-search navigation on a tab with a visible infobar. If |
+ // there was a previous pending search on this tab, this means it won't |
+ // commit, so undo anything we did in response to seeing that. Note that |
+ // if there was no pending search on this tab, these statements are |
+ // effectively a no-op. |
+ // |
+ // If this navigation actually commits, that will trigger the infobar's |
+ // owner to expire the infobar if need be. If it doesn't commit, then |
+ // simply leaving the infobar as-is will have been the right thing. |
+ UnregisterForEntrySpecificNotifications(i->second, false); |
+ i->second.infobar->set_pending_id(0); |
+ } else { |
+ // Non-search navigation on a tab with a not-yet-shown infobar. This |
+ // means the original search won't commit, so close the infobar. |
+ i->second.infobar->Close(false); |
+ } |
} else { |
- // Start listening for the commit notification. We also need to listen for |
- // the tab close command since that means the load will never commit. Note |
- // that in this case we don't need to close any previous infobar for this |
- // tab since this navigation will close it. |
- registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
- navigation_controller_source); |
- registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, |
- tab_contents_source); |
+ // Non-search navigation on a tab without one of our infobars. This is |
+ // irrelevant to us. |
} |
- |
- infobar_map_[infobar_helper] = (*infobar_creator_)(infobar_helper, search_url, |
- this, fetched_google_url_); |
} |
void GoogleURLTracker::OnNavigationCommittedOrTabClosed( |
- const content::NotificationSource& navigation_controller_source, |
- const content::NotificationSource& tab_contents_source, |
const InfoBarTabHelper* infobar_helper, |
- bool navigated) { |
- registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
- navigation_controller_source); |
- registrar_.Remove(this, chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, |
- tab_contents_source); |
- |
+ const GURL& search_url) { |
InfoBarMap::iterator i(infobar_map_.find(infobar_helper)); |
DCHECK(i != infobar_map_.end()); |
- DCHECK(need_to_prompt_); |
- if (navigated) |
- i->second->Show(); |
- else |
- i->second->Close(false); // Close manually since it's not added to a tab. |
+ const MapEntry& map_entry = i->second; |
+ |
+ if (!search_url.is_valid()) { |
+ // Tab closed, or we somehow tried to navigate to an invalid URL (?!). |
+ // InfoBarClosed() will take care of unregistering the notifications for |
+ // this tab. |
+ map_entry.infobar->Close(false); |
+ return; |
+ } |
+ |
+ // We're getting called because of a commit notification, so pass true for |
+ // |must_be_listening_for_commit|. |
+ UnregisterForEntrySpecificNotifications(map_entry, true); |
+ map_entry.infobar->Show(search_url); |
} |
void GoogleURLTracker::CloseAllInfoBars(bool redo_searches) { |
// Close all infobars, whether they've been added to tabs or not. |
while (!infobar_map_.empty()) |
- infobar_map_.begin()->second->Close(redo_searches); |
+ infobar_map_.begin()->second.infobar->Close(redo_searches); |
+} |
- // Any registered listeners for NAV_ENTRY_COMMITTED and TAB_CLOSED are now |
- // irrelevant as the associated infobars are gone. |
- registrar_.RemoveAll(); |
+void GoogleURLTracker::UnregisterForEntrySpecificNotifications( |
+ const MapEntry& map_entry, |
+ bool must_be_listening_for_commit) { |
+ // For tabs with non-showing infobars, we should always be listening for both |
+ // these notifications. For tabs with showing infobars, we may be listening |
+ // for NOTIFICATION_NAV_ENTRY_COMMITTED if the user has performed a new search |
+ // on this tab. |
+ if (registrar_.IsRegistered(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
+ map_entry.navigation_controller_source)) { |
+ registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
+ map_entry.navigation_controller_source); |
+ } else { |
+ DCHECK(!must_be_listening_for_commit); |
+ DCHECK(map_entry.infobar->showing()); |
+ } |
+ const bool registered_for_tab_contents_destroyed = |
+ registrar_.IsRegistered(this, chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, |
+ map_entry.tab_contents_source); |
+ DCHECK_NE(registered_for_tab_contents_destroyed, |
+ map_entry.infobar->showing()); |
+ if (registered_for_tab_contents_destroyed) { |
+ registrar_.Remove(this, chrome::NOTIFICATION_TAB_CONTENTS_DESTROYED, |
+ map_entry.tab_contents_source); |
+ } |
} |