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.h" 9 #include "chrome/browser/search_engines/template_url_service.h" 10 #include "chrome/browser/search_engines/template_url_service_factory.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 19 namespace chrome { 20 21 SearchTermsTracker::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 30 SearchTermsTracker::~SearchTermsTracker() { 31 } 32 33 bool 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 50 void 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 78 bool 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 TemplateURL* template_url = 88 TemplateURLServiceFactory::GetForProfile(profile)-> 89 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 // This entry is a search results page. Extract the terms only if this 96 // isn't the last (i.e. most recent/current) entry as we don't want to 97 // record the search terms when we're on an SRP. 98 if (i != controller->GetCurrentEntryIndex()) { 99 tab_data->srp_navigation_index = i; 100 template_url->ExtractSearchTermsFromURL(entry->GetURL(), 101 &tab_data->search_terms); 102 return true; 103 } 104 105 // We've found an SRP - stop searching (even if we did not record the 106 // search terms, as anything before this entry will be unrelated). 107 break; 108 } 109 } 110 111 // Do not consider any navigations that precede a non-web-triggerable 112 // navigation as they are not related to those terms. 113 if (!content::PageTransitionIsWebTriggerable( 114 entry->GetTransitionType())) { 115 break; 116 } 117 } 118 119 return false; 120 } 121 122 void SearchTermsTracker::RemoveTabData( 123 const content::WebContents* contents) { 124 TabState::iterator it = tabs_.find(contents); 125 if (it != tabs_.end()) { 126 tabs_.erase(it); 127 } 128 } 129 130 } // namespace chrome 131