// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/search/search_terms_tracker.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_service.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/web_contents.h" namespace chrome { SearchTermsTracker::SearchTermsTracker() { registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED, content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::NotificationService::AllSources()); } SearchTermsTracker::~SearchTermsTracker() { } bool SearchTermsTracker::GetSearchTerms( const content::WebContents* contents, base::string16* search_terms, int* navigation_index) const { if (contents) { TabState::const_iterator it = tabs_.find(contents); if (it != tabs_.end() && !it->second.search_terms.empty()) { if (search_terms) *search_terms = it->second.search_terms; if (navigation_index) *navigation_index = it->second.srp_navigation_index; return true; } } return false; } void SearchTermsTracker::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { case content::NOTIFICATION_NAV_ENTRY_COMMITTED: case content::NOTIFICATION_NAV_ENTRY_CHANGED: { content::NavigationController* controller = content::Source(source).ptr(); TabData search; if (FindMostRecentSearch(controller, &search)) tabs_[controller->GetWebContents()] = search; else RemoveTabData(controller->GetWebContents()); break; } case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { RemoveTabData(content::Source(source).ptr()); break; } default: NOTREACHED(); return; } } bool SearchTermsTracker::FindMostRecentSearch( const content::NavigationController* controller, SearchTermsTracker::TabData* tab_data) { Profile* profile = Profile::FromBrowserContext(controller->GetBrowserContext()); DCHECK(profile); if (!profile) return false; TemplateURLService* service = TemplateURLServiceFactory::GetForProfile(profile); TemplateURL* template_url = service->GetDefaultSearchProvider(); for (int i = controller->GetCurrentEntryIndex(); i >= 0; --i) { content::NavigationEntry* entry = controller->GetEntryAtIndex(i); if (entry->GetPageType() == content::PAGE_TYPE_NORMAL) { if (template_url->IsSearchURL(entry->GetURL(), service->search_terms_data())) { // This entry is a search results page. Extract the terms only if this // isn't the last (i.e. most recent/current) entry as we don't want to // record the search terms when we're on an SRP. if (i != controller->GetCurrentEntryIndex()) { tab_data->srp_navigation_index = i; template_url->ExtractSearchTermsFromURL(entry->GetURL(), service->search_terms_data(), &tab_data->search_terms); return true; } // We've found an SRP - stop searching (even if we did not record the // search terms, as anything before this entry will be unrelated). break; } } // Do not consider any navigations that precede a non-web-triggerable // navigation as they are not related to those terms. if (!content::PageTransitionIsWebTriggerable( entry->GetTransitionType())) { break; } } return false; } void SearchTermsTracker::RemoveTabData( const content::WebContents* contents) { TabState::iterator it = tabs_.find(contents); if (it != tabs_.end()) { tabs_.erase(it); } } } // namespace chrome