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
SearchTermsTracker()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
~SearchTermsTracker()30 SearchTermsTracker::~SearchTermsTracker() {
31 }
32
GetSearchTerms(const content::WebContents * contents,base::string16 * search_terms,int * navigation_index) const33 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
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
FindMostRecentSearch(const content::NavigationController * controller,SearchTermsTracker::TabData * tab_data)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 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 (!content::PageTransitionIsWebTriggerable(
116 entry->GetTransitionType())) {
117 break;
118 }
119 }
120
121 return false;
122 }
123
RemoveTabData(const content::WebContents * contents)124 void 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