1// Copyright 2013 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 "chrome/browser/search/search_terms_tracker.h" 6 7#include "chrome/browser/profiles/profile.h" 8#include "chrome/browser/search_engines/template_url_service_factory.h" 9#include "components/search_engines/template_url.h" 10#include "components/search_engines/template_url_service.h" 11#include "content/public/browser/navigation_controller.h" 12#include "content/public/browser/navigation_entry.h" 13#include "content/public/browser/notification_details.h" 14#include "content/public/browser/notification_service.h" 15#include "content/public/browser/notification_source.h" 16#include "content/public/browser/notification_types.h" 17#include "content/public/browser/web_contents.h" 18 19namespace chrome { 20 21SearchTermsTracker::SearchTermsTracker() { 22 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 23 content::NotificationService::AllSources()); 24 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED, 25 content::NotificationService::AllSources()); 26 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 27 content::NotificationService::AllSources()); 28} 29 30SearchTermsTracker::~SearchTermsTracker() { 31} 32 33bool SearchTermsTracker::GetSearchTerms( 34 const content::WebContents* contents, 35 base::string16* search_terms, 36 int* navigation_index) const { 37 if (contents) { 38 TabState::const_iterator it = tabs_.find(contents); 39 if (it != tabs_.end() && !it->second.search_terms.empty()) { 40 if (search_terms) 41 *search_terms = it->second.search_terms; 42 if (navigation_index) 43 *navigation_index = it->second.srp_navigation_index; 44 return true; 45 } 46 } 47 return false; 48} 49 50void SearchTermsTracker::Observe( 51 int type, 52 const content::NotificationSource& source, 53 const content::NotificationDetails& details) { 54 switch (type) { 55 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: 56 case content::NOTIFICATION_NAV_ENTRY_CHANGED: { 57 content::NavigationController* controller = 58 content::Source<content::NavigationController>(source).ptr(); 59 TabData search; 60 if (FindMostRecentSearch(controller, &search)) 61 tabs_[controller->GetWebContents()] = search; 62 else 63 RemoveTabData(controller->GetWebContents()); 64 break; 65 } 66 67 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { 68 RemoveTabData(content::Source<content::WebContents>(source).ptr()); 69 break; 70 } 71 72 default: 73 NOTREACHED(); 74 return; 75 } 76} 77 78bool SearchTermsTracker::FindMostRecentSearch( 79 const content::NavigationController* controller, 80 SearchTermsTracker::TabData* tab_data) { 81 Profile* profile = 82 Profile::FromBrowserContext(controller->GetBrowserContext()); 83 DCHECK(profile); 84 if (!profile) 85 return false; 86 87 TemplateURLService* service = 88 TemplateURLServiceFactory::GetForProfile(profile); 89 TemplateURL* template_url = service->GetDefaultSearchProvider(); 90 91 for (int i = controller->GetCurrentEntryIndex(); i >= 0; --i) { 92 content::NavigationEntry* entry = controller->GetEntryAtIndex(i); 93 if (entry->GetPageType() == content::PAGE_TYPE_NORMAL) { 94 if (template_url->IsSearchURL(entry->GetURL(), 95 service->search_terms_data())) { 96 // This entry is a search results page. Extract the terms only if this 97 // isn't the last (i.e. most recent/current) entry as we don't want to 98 // record the search terms when we're on an SRP. 99 if (i != controller->GetCurrentEntryIndex()) { 100 tab_data->srp_navigation_index = i; 101 template_url->ExtractSearchTermsFromURL(entry->GetURL(), 102 service->search_terms_data(), 103 &tab_data->search_terms); 104 return true; 105 } 106 107 // We've found an SRP - stop searching (even if we did not record the 108 // search terms, as anything before this entry will be unrelated). 109 break; 110 } 111 } 112 113 // Do not consider any navigations that precede a non-web-triggerable 114 // navigation as they are not related to those terms. 115 if (!ui::PageTransitionIsWebTriggerable( 116 entry->GetTransitionType())) { 117 break; 118 } 119 } 120 121 return false; 122} 123 124void SearchTermsTracker::RemoveTabData( 125 const content::WebContents* contents) { 126 TabState::iterator it = tabs_.find(contents); 127 if (it != tabs_.end()) { 128 tabs_.erase(it); 129 } 130} 131 132} // namespace chrome 133