Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Side by Side Diff: chrome/browser/ui/search/search.cc

Issue 12250033: Consolidate search terms extraction and Instant process determination. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Style nit Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/search/search.h ('k') | chrome/browser/ui/search/search_tab_helper.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/search/search.h" 5 #include "chrome/browser/ui/search/search.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/metrics/field_trial.h" 8 #include "base/metrics/field_trial.h"
9 #include "base/string_split.h" 9 #include "base/string_split.h"
10 #include "base/string_util.h"
11 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_number_conversions.h"
11 #include "chrome/browser/instant/instant_service.h"
12 #include "chrome/browser/instant/instant_service_factory.h"
12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/search_engines/template_url_service.h"
15 #include "chrome/browser/search_engines/template_url_service_factory.h"
13 #include "chrome/common/chrome_switches.h" 16 #include "chrome/common/chrome_switches.h"
14 #include "chrome/common/chrome_version_info.h" 17 #include "chrome/common/url_constants.h"
15 #include "content/public/browser/navigation_entry.h" 18 #include "content/public/browser/navigation_entry.h"
16 19 #include "content/public/browser/render_process_host.h"
17 #if !defined(OS_ANDROID) 20 #include "content/public/browser/web_contents.h"
18 #include "chrome/browser/themes/theme_service.h"
19 #include "chrome/browser/themes/theme_service_factory.h"
20 #endif
21 21
22 namespace { 22 namespace {
23 23
24 // Configuration options for Embedded Search. 24 // Configuration options for Embedded Search.
25 // InstantExtended field trials are named in such a way that we can parse out 25 // InstantExtended field trials are named in such a way that we can parse out
26 // the experiment configuration from the trial's group name in order to give 26 // the experiment configuration from the trial's group name in order to give
27 // us maximum flexability in running experiments. 27 // us maximum flexability in running experiments.
28 // Field trials should be named things like "Group7 espv:2 themes:0". 28 // Field trial groups should be named things like "Group7 espv:2 instant:1".
29 // The first token is always GroupN for some integer N, followed by a 29 // The first token is always GroupN for some integer N, followed by a
30 // space-delimited list of key:value pairs which correspond to these flags: 30 // space-delimited list of key:value pairs which correspond to these flags:
31 const char kEnableOnThemesFlagName[] = "themes";
32 const bool kEnableOnThemesDefault = false;
33
34 const char kEmbeddedPageVersionFlagName[] = "espv"; 31 const char kEmbeddedPageVersionFlagName[] = "espv";
35 const int kEmbeddedPageVersionDefault = 1; 32 const int kEmbeddedPageVersionDefault = 1;
36 33
37 const char kInstantExtendedActivationName[] = "instant"; 34 const char kInstantExtendedActivationName[] = "instant";
38 const chrome::search::InstantExtendedDefault kInstantExtendedActivationDefault = 35 const chrome::search::InstantExtendedDefault kInstantExtendedActivationDefault =
39 chrome::search::INSTANT_USE_EXISTING; 36 chrome::search::INSTANT_USE_EXISTING;
40 37
41 // Constants for the field trial name and group prefix. 38 // Constants for the field trial name and group prefix.
42 const char kInstantExtendedFieldTrialName[] = "InstantExtended"; 39 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
43 const char kGroupNumberPrefix[] = "Group"; 40 const char kGroupNumberPrefix[] = "Group";
44 41
45 // If the field trial's group name ends with this string its configuration will 42 // If the field trial's group name ends with this string its configuration will
46 // be ignored and Instant Extended will not be enabled by default. 43 // be ignored and Instant Extended will not be enabled by default.
47 const char kDisablingSuffix[] = "DISABLED"; 44 const char kDisablingSuffix[] = "DISABLED";
48 45
46 chrome::search::InstantExtendedDefault InstantExtendedDefaultFromInt64(
47 int64 default_value) {
48 switch (default_value) {
49 case 0: return chrome::search::INSTANT_DEFAULT_ON;
50 case 1: return chrome::search::INSTANT_USE_EXISTING;
51 case 2: return chrome::search::INSTANT_DEFAULT_OFF;
52 default: return chrome::search::INSTANT_USE_EXISTING;
53 }
54 }
55
56 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
57 TemplateURLService* template_url_service =
58 TemplateURLServiceFactory::GetForProfile(profile);
59 if (template_url_service)
60 return template_url_service->GetDefaultSearchProvider();
61 return NULL;
62 }
63
64 GURL TemplateURLRefToGURL(const TemplateURLRef& ref) {
65 return GURL(
66 ref.ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(string16())));
67 }
68
69 bool MatchesOriginAndPath(const GURL& my_url, const GURL& other_url) {
70 return my_url.host() == other_url.host() &&
71 my_url.port() == other_url.port() &&
72 my_url.path() == other_url.path() &&
73 (my_url.scheme() == other_url.scheme() ||
74 (my_url.SchemeIs(chrome::kHttpsScheme) &&
75 other_url.SchemeIs(chrome::kHttpScheme)));
76 }
77
78 bool IsCommandLineInstantURL(const GURL& url) {
79 const CommandLine* cl = CommandLine::ForCurrentProcess();
80 GURL instant_url(cl->GetSwitchValueASCII(switches::kInstantURL));
81 return instant_url.is_valid() && MatchesOriginAndPath(url, instant_url);
82 }
83
84 // Coerces the commandline Instant URL to look like a template URL, so that we
85 // can extract search terms from it.
86 GURL CoerceCommandLineURLToTemplateURL(const GURL& instant_url,
87 const TemplateURLRef& ref) {
88 GURL search_url = TemplateURLRefToGURL(ref);
89
90 GURL::Replacements replacements;
91 replacements.SetSchemeStr(chrome::kHttpsScheme);
92 replacements.SetHostStr(search_url.host());
93 replacements.SetPortStr(search_url.port());
94 replacements.SetPathStr(search_url.path());
95
96 return instant_url.ReplaceComponents(replacements);
97 }
98
99 bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
100 GURL search_url = TemplateURLRefToGURL(template_url->url_ref());
101 if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
102 return true;
103
104 // "URLCount() - 1" because we already tested url_ref above.
105 for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
106 TemplateURLRef ref(template_url, i);
107 search_url = TemplateURLRefToGURL(ref);
108 if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
109 return true;
110 }
111
112 return false;
113 }
114
49 } // namespace 115 } // namespace
50 116
51 namespace chrome { 117 namespace chrome {
52 namespace search { 118 namespace search {
53 119
54 // static
55 const char kInstantExtendedSearchTermsKey[] = "search_terms"; 120 const char kInstantExtendedSearchTermsKey[] = "search_terms";
56 121
57 InstantExtendedDefault InstantExtendedDefaultFromInt64(int64 default_value) { 122 const char kLocalOmniboxPopupURL[] =
58 switch (default_value) { 123 "chrome://local-omnibox-popup/local-omnibox-popup.html";
59 case 0: return INSTANT_FORCE_ON;
60 case 1: return INSTANT_USE_EXISTING;
61 case 2: return INSTANT_FORCE_OFF;
62 default: return INSTANT_USE_EXISTING;
63 }
64 }
65 124
66 InstantExtendedDefault GetInstantExtendedDefaultSetting() { 125 InstantExtendedDefault GetInstantExtendedDefaultSetting() {
67 InstantExtendedDefault default_setting = INSTANT_USE_EXISTING;
68
69 FieldTrialFlags flags; 126 FieldTrialFlags flags;
70 if (GetFieldTrialInfo( 127 if (GetFieldTrialInfo(
71 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName), 128 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
72 &flags, NULL)) { 129 &flags, NULL)) {
73 uint64 trial_default = GetUInt64ValueForFlagWithDefault( 130 uint64 trial_default = GetUInt64ValueForFlagWithDefault(
74 kInstantExtendedActivationName, 131 kInstantExtendedActivationName,
75 kInstantExtendedActivationDefault, 132 kInstantExtendedActivationDefault,
76 flags); 133 flags);
77 default_setting = InstantExtendedDefaultFromInt64(trial_default); 134 return InstantExtendedDefaultFromInt64(trial_default);
78 } 135 }
79 136
80 return default_setting; 137 return INSTANT_USE_EXISTING;
81 } 138 }
82 139
83 // Check whether or not the Extended API should be used on the given profile. 140 bool IsInstantExtendedAPIEnabled(const Profile* profile) {
84 bool IsInstantExtendedAPIEnabled(Profile* profile) {
85 return EmbeddedSearchPageVersion(profile) != 0; 141 return EmbeddedSearchPageVersion(profile) != 0;
86 } 142 }
87 143
88 // Determine what embedded search page version to request from the user's 144 // Determine what embedded search page version to request from the user's
89 // default search provider. If 0, the embedded search UI should not be enabled. 145 // default search provider. If 0, the embedded search UI should not be enabled.
90 // Note that the profile object here isn't const because we need to determine 146 uint64 EmbeddedSearchPageVersion(const Profile* profile) {
91 // whether or not the user has a theme installed as part of this check, and
92 // that logic requires a non-const profile for whatever reason.
93 uint64 EmbeddedSearchPageVersion(Profile* profile) {
94 // Incognito windows do not currently use the embedded search API.
95 if (!profile || profile->IsOffTheRecord()) 147 if (!profile || profile->IsOffTheRecord())
96 return 0; 148 return 0;
97 149
98 // Check Finch field trials.
99 FieldTrialFlags flags; 150 FieldTrialFlags flags;
100 if (GetFieldTrialInfo( 151 if (GetFieldTrialInfo(
101 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName), 152 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
102 &flags, NULL)) { 153 &flags, NULL)) {
103 uint64 espv = GetUInt64ValueForFlagWithDefault( 154 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
104 kEmbeddedPageVersionFlagName, 155 kEmbeddedPageVersionDefault,
105 kEmbeddedPageVersionDefault, 156 flags);
106 flags); 157 }
107 158
108 // Check for themes. 159 const CommandLine* cl = CommandLine::ForCurrentProcess();
109 bool has_theme = false; 160 if (cl->HasSwitch(switches::kEnableInstantExtendedAPI)) {
110 #if !defined(OS_ANDROID)
111 has_theme =
112 !ThemeServiceFactory::GetForProfile(profile)->UsingDefaultTheme();
113 #endif
114
115 bool enable_for_themes =
116 GetBoolValueForFlagWithDefault(kEnableOnThemesFlagName,
117 kEnableOnThemesDefault,
118 flags);
119 if (!has_theme || enable_for_themes)
120 return espv;
121 }
122
123 if (CommandLine::ForCurrentProcess()->HasSwitch(
124 switches::kEnableInstantExtendedAPI)) {
125 // The user has manually flipped the about:flags switch - give the default 161 // The user has manually flipped the about:flags switch - give the default
126 // UI version. 162 // UI version.
127 return kEmbeddedPageVersionDefault; 163 return kEmbeddedPageVersionDefault;
128 } 164 }
165
129 return 0; 166 return 0;
130 } 167 }
131 168
132 void EnableInstantExtendedAPIForTesting() { 169 bool IsQueryExtractionEnabled(const Profile* profile) {
133 CommandLine::ForCurrentProcess()->AppendSwitch(
134 switches::kEnableInstantExtendedAPI);
135 }
136
137 bool IsQueryExtractionEnabled(Profile* profile) {
138 #if defined(OS_IOS) 170 #if defined(OS_IOS)
139 return CommandLine::ForCurrentProcess()->HasSwitch( 171 const CommandLine* cl = CommandLine::ForCurrentProcess();
140 switches::kEnableQueryExtraction); 172 return cl->HasSwitch(switches::kEnableQueryExtraction);
141 #else
142 if (!profile || profile->IsOffTheRecord())
143 return false;
144
145 // On desktop, query extraction is controlled by the instant-extended-api
146 // flag.
147 bool enabled = IsInstantExtendedAPIEnabled(profile);
148
149 // Running with --enable-query-extraction but not
150 // --enable-instant-extended-api is an error.
151 DCHECK(!(CommandLine::ForCurrentProcess()->HasSwitch(
152 switches::kEnableQueryExtraction) &&
153 !enabled));
154 return enabled;
155 #endif
156 }
157
158 void EnableQueryExtractionForTesting() {
159 #if defined(OS_IOS)
160 CommandLine::ForCurrentProcess()->AppendSwitch(
161 switches::kEnableQueryExtraction);
162 #else 173 #else
163 // On desktop, query extraction is controlled by the instant-extended-api 174 // On desktop, query extraction is controlled by the instant-extended-api
164 // flag. 175 // flag.
165 CommandLine::ForCurrentProcess()->AppendSwitch( 176 return IsInstantExtendedAPIEnabled(profile);
166 switches::kEnableInstantExtendedAPI);
167 #endif 177 #endif
168 } 178 }
169 179
170 string16 GetSearchTermsFromNavigationEntry( 180 string16 GetSearchTermsFromNavigationEntry(
171 const content::NavigationEntry* entry) { 181 const content::NavigationEntry* entry) {
172 string16 search_terms; 182 string16 search_terms;
173 entry->GetExtraData(kInstantExtendedSearchTermsKey, &search_terms); 183 if (entry)
184 entry->GetExtraData(kInstantExtendedSearchTermsKey, &search_terms);
174 return search_terms; 185 return search_terms;
175 } 186 }
176 187
177 bool IsForcedInstantURL(const GURL& url) { 188 string16 GetSearchTerms(const content::WebContents* contents) {
178 CommandLine* command_line = CommandLine::ForCurrentProcess(); 189 if (!contents)
179 if (!command_line->HasSwitch(switches::kInstantURL)) 190 return string16();
180 return false; 191
181 192 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
182 GURL instant_url(command_line->GetSwitchValueASCII(switches::kInstantURL)); 193 if (!IsQueryExtractionEnabled(profile))
183 return url.scheme() == instant_url.scheme() && 194 return string16();
184 url.host() == instant_url.host() && 195
185 url.port() == instant_url.port() && 196 // For security reasons, don't extract search terms if the page is not being
186 url.path() == instant_url.path(); 197 // rendered in the privileged Instant renderer process. This is to protect
198 // against a malicious page somehow scripting the search results page and
199 // faking search terms in the URL. Random pages can't get into the Instant
200 // renderer and scripting doesn't work cross-process, so if the page is in
201 // the Instant process, we know it isn't being exploited.
202 const content::RenderProcessHost* process_host =
203 contents->GetRenderProcessHost();
204 if (!process_host)
205 return string16();
206
207 const InstantService* instant_service =
208 InstantServiceFactory::GetForProfile(profile);
209 if (!instant_service)
210 return string16();
211
212 if (!instant_service->IsInstantProcess(process_host->GetID()))
213 return string16();
214
215 // Check to see if search terms have already been extracted.
216 const content::NavigationEntry* entry =
217 contents->GetController().GetVisibleEntry();
218 if (!entry)
219 return string16();
220
221 string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
222 if (!search_terms.empty())
223 return search_terms;
224
225 // Otherwise, extract from the URL.
226 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
227 if (!template_url)
228 return string16();
229
230 GURL url = entry->GetVirtualURL();
231
232 if (IsCommandLineInstantURL(url))
233 url = CoerceCommandLineURLToTemplateURL(url, template_url->url_ref());
234
235 if (url.SchemeIsSecure() && template_url->HasSearchTermsReplacementKey(url))
236 template_url->ExtractSearchTermsFromURL(url, &search_terms);
237
238 return search_terms;
239 }
240
241 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
242 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
243 if (!template_url)
244 return false;
245
246 GURL effective_url = url;
247
248 if (IsCommandLineInstantURL(url)) {
249 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
250 effective_url = CoerceCommandLineURLToTemplateURL(url, instant_url_ref);
251 }
252
253 return ShouldAssignURLToInstantRendererImpl(
254 effective_url,
255 IsInstantExtendedAPIEnabled(profile),
256 template_url);
257 }
258
259 void EnableInstantExtendedAPIForTesting() {
260 CommandLine* cl = CommandLine::ForCurrentProcess();
261 cl->AppendSwitch(switches::kEnableInstantExtendedAPI);
262 }
263
264 void EnableQueryExtractionForTesting() {
265 #if defined(OS_IOS)
266 CommandLine* cl = CommandLine::ForCurrentProcess();
267 cl->AppendSwitch(switches::kEnableQueryExtraction);
268 #else
269 EnableInstantExtendedAPIForTesting();
270 #endif
271 }
272
273 bool ShouldAssignURLToInstantRendererImpl(const GURL& url,
274 bool extended_api_enabled,
275 TemplateURL* template_url) {
276 if (!url.is_valid())
277 return false;
278
279 if (url.SchemeIs(chrome::kChromeSearchScheme))
280 return true;
281
282 if (extended_api_enabled && url == GURL(kLocalOmniboxPopupURL))
283 return true;
284
285 if (extended_api_enabled && !url.SchemeIsSecure())
286 return false;
287
288 if (extended_api_enabled && !template_url->HasSearchTermsReplacementKey(url))
289 return false;
290
291 GURL instant_url = TemplateURLRefToGURL(template_url->instant_url_ref());
292 if (!instant_url.is_valid())
293 return false;
294
295 if (MatchesOriginAndPath(url, instant_url))
296 return true;
297
298 if (extended_api_enabled && MatchesAnySearchURL(url, template_url))
299 return true;
300
301 return false;
187 } 302 }
188 303
189 bool GetFieldTrialInfo(const std::string& group_name, 304 bool GetFieldTrialInfo(const std::string& group_name,
190 FieldTrialFlags* flags, 305 FieldTrialFlags* flags,
191 uint64* group_number) { 306 uint64* group_number) {
192 if (EndsWith(group_name, kDisablingSuffix, true) || 307 if (EndsWith(group_name, kDisablingSuffix, true) ||
193 !StartsWithASCII(group_name, kGroupNumberPrefix, true)) { 308 !StartsWithASCII(group_name, kGroupNumberPrefix, true))
194 return false; 309 return false;
195 }
196 310
197 // We have a valid trial that starts with "Group" and isn't disabled. 311 // We have a valid trial that starts with "Group" and isn't disabled.
198 // First extract the flags. 312 // First extract the flags.
199 std::string group_prefix(group_name); 313 std::string group_prefix(group_name);
200 314
201 size_t first_space = group_name.find(" "); 315 size_t first_space = group_name.find(" ");
202 if (first_space != std::string::npos) { 316 if (first_space != std::string::npos) {
203 // There is a flags section of the group name. Split that out and parse 317 // There is a flags section of the group name. Split that out and parse it.
204 // it.
205 group_prefix = group_name.substr(0, first_space); 318 group_prefix = group_name.substr(0, first_space);
206 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space), 319 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
207 ':', ' ', flags)) { 320 ':', ' ', flags)) {
208 // Failed to parse the flags section. Assume the whole group name is 321 // Failed to parse the flags section. Assume the whole group name is
209 // invalid. 322 // invalid.
210 return false; 323 return false;
211 } 324 }
212 } 325 }
213 326
214 // Now extract the group number, making sure we get a non-zero value. 327 // Now extract the group number, making sure we get a non-zero value.
215 uint64 temp_group_number = 0; 328 uint64 temp_group_number = 0;
216 if (!base::StringToUint64(group_prefix.substr(strlen(kGroupNumberPrefix)), 329 std::string group_suffix = group_prefix.substr(strlen(kGroupNumberPrefix));
217 &temp_group_number) || 330 if (!base::StringToUint64(group_suffix, &temp_group_number) ||
218 temp_group_number == 0) { 331 temp_group_number == 0)
219 return false; 332 return false;
220 }
221 333
222 if (group_number) 334 if (group_number)
223 *group_number = temp_group_number; 335 *group_number = temp_group_number;
224 336
225 return true; 337 return true;
226 } 338 }
227 339
228 // Given a FieldTrialFlags object, returns the string value of the provided 340 // Given a FieldTrialFlags object, returns the string value of the provided
229 // flag. 341 // flag.
230 std::string GetStringValueForFlagWithDefault( 342 std::string GetStringValueForFlagWithDefault(const std::string& flag,
231 const std::string& flag, 343 const std::string& default_value,
232 const std::string& default_value, 344 const FieldTrialFlags& flags) {
233 FieldTrialFlags& flags) {
234 FieldTrialFlags::const_iterator i; 345 FieldTrialFlags::const_iterator i;
235 for (i = flags.begin(); i != flags.end(); i++) { 346 for (i = flags.begin(); i != flags.end(); i++) {
236 if (i->first == flag) 347 if (i->first == flag)
237 return i->second; 348 return i->second;
238 } 349 }
239 return default_value; 350 return default_value;
240 } 351 }
241 352
242 // Given a FieldTrialFlags object, returns the uint64 value of the provided 353 // Given a FieldTrialFlags object, returns the uint64 value of the provided
243 // flag. 354 // flag.
244 uint64 GetUInt64ValueForFlagWithDefault( 355 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
245 const std::string& flag, uint64 default_value, FieldTrialFlags& flags) { 356 uint64 default_value,
357 const FieldTrialFlags& flags) {
246 uint64 value; 358 uint64 value;
247 if (!base::StringToUint64(GetStringValueForFlagWithDefault(flag, "", flags), 359 std::string str_value = GetStringValueForFlagWithDefault(flag, "", flags);
248 &value)) 360 if (base::StringToUint64(str_value, &value))
249 return default_value; 361 return value;
250 return value; 362 return default_value;
251 } 363 }
252 364
253 // Given a FieldTrialFlags object, returns the boolean value of the provided 365 // Given a FieldTrialFlags object, returns the boolean value of the provided
254 // flag. 366 // flag.
255 bool GetBoolValueForFlagWithDefault( 367 bool GetBoolValueForFlagWithDefault(const std::string& flag,
256 const std::string& flag, bool default_value, FieldTrialFlags& flags) { 368 bool default_value,
369 const FieldTrialFlags& flags) {
257 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags); 370 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
258 } 371 }
259 372
260 } // namespace search 373 } // namespace search
261 } // namespace chrome 374 } // namespace chrome
OLDNEW
« no previous file with comments | « chrome/browser/ui/search/search.h ('k') | chrome/browser/ui/search/search_tab_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698