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

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

Issue 2805133004: Local NTP: Deploy strict-dynamic CSP (Closed)
Patch Set: rebase Created 3 years, 8 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 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/search/local_ntp_source.h" 5 #include "chrome/browser/search/local_ntp_source.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include "base/base64.h"
10
11 #include "base/command_line.h" 10 #include "base/command_line.h"
12 #include "base/json/json_string_value_serializer.h" 11 #include "base/json/json_string_value_serializer.h"
13 #include "base/logging.h" 12 #include "base/logging.h"
14 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
15 #include "base/memory/ref_counted_memory.h" 14 #include "base/memory/ref_counted_memory.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/strings/string_util.h" 15 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
19 #include "base/values.h" 17 #include "base/values.h"
20 #include "build/build_config.h" 18 #include "build/build_config.h"
21 #include "chrome/browser/search/instant_io_context.h" 19 #include "chrome/browser/search/instant_io_context.h"
22 #include "chrome/browser/search/local_files_ntp_source.h" 20 #include "chrome/browser/search/local_files_ntp_source.h"
23 #include "chrome/browser/search_engines/template_url_service_factory.h" 21 #include "chrome/browser/search_engines/template_url_service_factory.h"
24 #include "chrome/browser/themes/theme_properties.h" 22 #include "chrome/browser/themes/theme_properties.h"
25 #include "chrome/browser/themes/theme_service.h" 23 #include "chrome/browser/themes/theme_service.h"
26 #include "chrome/browser/themes/theme_service_factory.h" 24 #include "chrome/browser/themes/theme_service_factory.h"
27 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/url_constants.h" 26 #include "chrome/common/url_constants.h"
29 #include "chrome/grit/browser_resources.h" 27 #include "chrome/grit/browser_resources.h"
30 #include "chrome/grit/generated_resources.h" 28 #include "chrome/grit/generated_resources.h"
31 #include "chrome/grit/theme_resources.h" 29 #include "chrome/grit/theme_resources.h"
32 #include "components/search_engines/template_url_service.h" 30 #include "components/search_engines/template_url_service.h"
31 #include "components/search_engines/template_url_service_observer.h"
33 #include "components/strings/grit/components_strings.h" 32 #include "components/strings/grit/components_strings.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "crypto/secure_hash.h"
35 #include "net/base/hash_value.h"
34 #include "net/url_request/url_request.h" 36 #include "net/url_request/url_request.h"
35 #include "third_party/skia/include/core/SkColor.h" 37 #include "third_party/skia/include/core/SkColor.h"
36 #include "ui/base/l10n/l10n_util.h" 38 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h" 39 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/base/webui/web_ui_util.h" 40 #include "ui/base/webui/web_ui_util.h"
39 #include "ui/resources/grit/ui_resources.h" 41 #include "ui/resources/grit/ui_resources.h"
40 #include "url/gurl.h" 42 #include "url/gurl.h"
41 43
42 namespace { 44 namespace {
43 45
44 // Signifies a locally constructed resource, i.e. not from grit/. 46 // Signifies a locally constructed resource, i.e. not from grit/.
45 const int kLocalResource = -1; 47 const int kLocalResource = -1;
46 48
47 const char kConfigDataFilename[] = "config.js"; 49 const char kConfigDataFilename[] = "config.js";
48 const char kThemeCSSFilename[] = "theme.css"; 50 const char kThemeCSSFilename[] = "theme.css";
51 const char kMainHtmlFilename[] = "local-ntp.html";
49 52
50 const struct Resource{ 53 const struct Resource{
51 const char* filename; 54 const char* filename;
52 int identifier; 55 int identifier;
53 const char* mime_type; 56 const char* mime_type;
54 } kResources[] = { 57 } kResources[] = {
55 {"local-ntp.html", IDR_LOCAL_NTP_HTML, "text/html"}, 58 {kMainHtmlFilename, kLocalResource, "text/html"},
56 {"local-ntp.js", IDR_LOCAL_NTP_JS, "application/javascript"}, 59 {"local-ntp.js", IDR_LOCAL_NTP_JS, "application/javascript"},
57 {kConfigDataFilename, kLocalResource, "application/javascript"}, 60 {kConfigDataFilename, kLocalResource, "application/javascript"},
58 {kThemeCSSFilename, kLocalResource, "text/css"}, 61 {kThemeCSSFilename, kLocalResource, "text/css"},
59 {"local-ntp.css", IDR_LOCAL_NTP_CSS, "text/css"}, 62 {"local-ntp.css", IDR_LOCAL_NTP_CSS, "text/css"},
60 {"images/close_3_mask.png", IDR_CLOSE_3_MASK, "image/png"}, 63 {"images/close_3_mask.png", IDR_CLOSE_3_MASK, "image/png"},
61 {"images/close_4_button.png", IDR_CLOSE_4_BUTTON, "image/png"}, 64 {"images/close_4_button.png", IDR_CLOSE_4_BUTTON, "image/png"},
62 {"images/ntp_default_favicon.png", IDR_NTP_DEFAULT_FAVICON, "image/png"}, 65 {"images/ntp_default_favicon.png", IDR_NTP_DEFAULT_FAVICON, "image/png"},
63 }; 66 };
64 67
65 // Strips any query parameters from the specified path. 68 // Strips any query parameters from the specified path.
66 std::string StripParameters(const std::string& path) { 69 std::string StripParameters(const std::string& path) {
67 return path.substr(0, path.find("?")); 70 return path.substr(0, path.find("?"));
68 } 71 }
69 72
70 bool DefaultSearchProviderIsGoogle(Profile* profile) {
71 if (!profile)
72 return false;
73
74 TemplateURLService* template_url_service =
75 TemplateURLServiceFactory::GetForProfile(profile);
76 if (!template_url_service)
77 return false;
78
79 const TemplateURL* default_provider =
80 template_url_service->GetDefaultSearchProvider();
81 return default_provider &&
82 (default_provider->GetEngineType(
83 template_url_service->search_terms_data()) ==
84 SEARCH_ENGINE_GOOGLE);
85 }
86
87 // Adds a localized string keyed by resource id to the dictionary. 73 // Adds a localized string keyed by resource id to the dictionary.
88 void AddString(base::DictionaryValue* dictionary, 74 void AddString(base::DictionaryValue* dictionary,
89 const std::string& key, 75 const std::string& key,
90 int resource_id) { 76 int resource_id) {
91 dictionary->SetString(key, l10n_util::GetStringUTF16(resource_id)); 77 dictionary->SetString(key, l10n_util::GetStringUTF16(resource_id));
92 } 78 }
93 79
94 // Populates |translated_strings| dictionary for the local NTP. |is_google| 80 // Populates |translated_strings| dictionary for the local NTP. |is_google|
95 // indicates that this page is the Google local NTP. 81 // indicates that this page is the Google local NTP.
96 std::unique_ptr<base::DictionaryValue> GetTranslatedStrings(bool is_google) { 82 std::unique_ptr<base::DictionaryValue> GetTranslatedStrings(bool is_google) {
(...skipping 12 matching lines...) Expand all
109 AddString(translated_strings.get(), "title", IDS_NEW_TAB_TITLE); 95 AddString(translated_strings.get(), "title", IDS_NEW_TAB_TITLE);
110 if (is_google) { 96 if (is_google) {
111 AddString(translated_strings.get(), "searchboxPlaceholder", 97 AddString(translated_strings.get(), "searchboxPlaceholder",
112 IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT); 98 IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT);
113 } 99 }
114 100
115 return translated_strings; 101 return translated_strings;
116 } 102 }
117 103
118 // Returns a JS dictionary of configuration data for the local NTP. 104 // Returns a JS dictionary of configuration data for the local NTP.
119 std::string GetConfigData(Profile* profile) { 105 std::string GetConfigData(bool is_google) {
120 base::DictionaryValue config_data; 106 base::DictionaryValue config_data;
121 bool is_google = DefaultSearchProviderIsGoogle(profile);
122 config_data.Set("translatedStrings", GetTranslatedStrings(is_google)); 107 config_data.Set("translatedStrings", GetTranslatedStrings(is_google));
123 config_data.SetBoolean("isGooglePage", is_google); 108 config_data.SetBoolean("isGooglePage", is_google);
124 109
125 // Serialize the dictionary. 110 // Serialize the dictionary.
126 std::string js_text; 111 std::string js_text;
127 JSONStringValueSerializer serializer(&js_text); 112 JSONStringValueSerializer serializer(&js_text);
128 serializer.Serialize(config_data); 113 serializer.Serialize(config_data);
129 114
130 std::string config_data_js; 115 std::string config_data_js;
131 config_data_js.append("var configData = "); 116 config_data_js.append("var configData = ");
132 config_data_js.append(js_text); 117 config_data_js.append(js_text);
133 config_data_js.append(";"); 118 config_data_js.append(";");
134 return config_data_js; 119 return config_data_js;
135 } 120 }
136 121
122 std::string GetIntegritySha256Value(const std::string& data) {
123 // Compute the sha256 hash.
124 net::SHA256HashValue hash_value;
125 std::unique_ptr<crypto::SecureHash> hash(
126 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
127 hash->Update(data.data(), data.size());
128 hash->Finish(&hash_value, sizeof(hash_value));
129
130 // Base64-encode it.
131 base::StringPiece hash_value_str(
132 reinterpret_cast<const char*>(hash_value.data), sizeof(hash_value));
133 std::string result;
134 base::Base64Encode(hash_value_str, &result);
135 return result;
136 }
137
137 std::string GetThemeCSS(Profile* profile) { 138 std::string GetThemeCSS(Profile* profile) {
138 SkColor background_color = 139 SkColor background_color =
139 ThemeService::GetThemeProviderForProfile(profile) 140 ThemeService::GetThemeProviderForProfile(profile)
140 .GetColor(ThemeProperties::COLOR_NTP_BACKGROUND); 141 .GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
141 142
142 return base::StringPrintf("body { background-color: #%02X%02X%02X; }", 143 return base::StringPrintf("body { background-color: #%02X%02X%02X; }",
143 SkColorGetR(background_color), 144 SkColorGetR(background_color),
144 SkColorGetG(background_color), 145 SkColorGetG(background_color),
145 SkColorGetB(background_color)); 146 SkColorGetB(background_color));
146 } 147 }
147 148
148 std::string GetLocalNtpPath() { 149 std::string GetLocalNtpPath() {
149 return std::string(chrome::kChromeSearchScheme) + "://" + 150 return std::string(chrome::kChromeSearchScheme) + "://" +
150 std::string(chrome::kChromeSearchLocalNtpHost) + "/"; 151 std::string(chrome::kChromeSearchLocalNtpHost) + "/";
151 } 152 }
152 153
154 bool DefaultSearchProviderIsGoogleImpl(
155 const TemplateURLService* template_url_service) {
156 const TemplateURL* default_provider =
157 template_url_service->GetDefaultSearchProvider();
158 return default_provider && (default_provider->GetEngineType(
159 template_url_service->search_terms_data()) ==
160 SEARCH_ENGINE_GOOGLE);
161 }
162
153 } // namespace 163 } // namespace
154 164
155 LocalNtpSource::LocalNtpSource(Profile* profile) : profile_(profile) {} 165 class LocalNtpSource::GoogleSearchProviderTracker
166 : public TemplateURLServiceObserver {
167 public:
168 using SearchProviderIsGoogleChangedCallback =
169 base::Callback<void(bool is_google)>;
170
171 GoogleSearchProviderTracker(
172 TemplateURLService* service,
173 const SearchProviderIsGoogleChangedCallback& callback)
174 : service_(service), callback_(callback), is_google_(false) {
175 DCHECK(service_);
176 service_->AddObserver(this);
177 is_google_ = DefaultSearchProviderIsGoogleImpl(service_);
178 }
179
180 ~GoogleSearchProviderTracker() override {
181 if (service_)
182 service_->RemoveObserver(this);
183 }
184
185 bool DefaultSearchProviderIsGoogle() const { return is_google_; }
186
187 private:
188 void OnTemplateURLServiceChanged() override {
189 bool old_is_google = is_google_;
190 is_google_ = DefaultSearchProviderIsGoogleImpl(service_);
191 if (is_google_ != old_is_google)
192 callback_.Run(is_google_);
193 }
194
195 void OnTemplateURLServiceShuttingDown() override {
196 service_->RemoveObserver(this);
197 service_ = nullptr;
198 }
199
200 TemplateURLService* service_;
201 SearchProviderIsGoogleChangedCallback callback_;
202
203 bool is_google_;
204 };
205
206 LocalNtpSource::LocalNtpSource(Profile* profile)
207 : profile_(profile),
208 default_search_provider_is_google_(false),
209 default_search_provider_is_google_io_thread_(false),
210 weak_ptr_factory_(this) {
211 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
212
213 TemplateURLService* template_url_service =
214 TemplateURLServiceFactory::GetForProfile(profile_);
215 if (template_url_service) {
216 google_tracker_ = base::MakeUnique<GoogleSearchProviderTracker>(
217 template_url_service,
218 base::Bind(&LocalNtpSource::DefaultSearchProviderIsGoogleChanged,
219 base::Unretained(this)));
220 DefaultSearchProviderIsGoogleChanged(
221 google_tracker_->DefaultSearchProviderIsGoogle());
222 }
223 }
156 224
157 LocalNtpSource::~LocalNtpSource() = default; 225 LocalNtpSource::~LocalNtpSource() = default;
158 226
159 std::string LocalNtpSource::GetSource() const { 227 std::string LocalNtpSource::GetSource() const {
160 return chrome::kChromeSearchLocalNtpHost; 228 return chrome::kChromeSearchLocalNtpHost;
161 } 229 }
162 230
163 void LocalNtpSource::StartDataRequest( 231 void LocalNtpSource::StartDataRequest(
164 const std::string& path, 232 const std::string& path,
165 const content::ResourceRequestInfo::WebContentsGetter& wc_getter, 233 const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
166 const content::URLDataSource::GotDataCallback& callback) { 234 const content::URLDataSource::GotDataCallback& callback) {
235 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
236
167 std::string stripped_path = StripParameters(path); 237 std::string stripped_path = StripParameters(path);
168 if (stripped_path == kConfigDataFilename) { 238 if (stripped_path == kConfigDataFilename) {
169 std::string config_data_js = GetConfigData(profile_); 239 std::string config_data_js =
240 GetConfigData(default_search_provider_is_google_);
170 callback.Run(base::RefCountedString::TakeString(&config_data_js)); 241 callback.Run(base::RefCountedString::TakeString(&config_data_js));
171 return; 242 return;
172 } 243 }
173 if (stripped_path == kThemeCSSFilename) { 244 if (stripped_path == kThemeCSSFilename) {
174 std::string theme_css = GetThemeCSS(profile_); 245 std::string theme_css = GetThemeCSS(profile_);
175 callback.Run(base::RefCountedString::TakeString(&theme_css)); 246 callback.Run(base::RefCountedString::TakeString(&theme_css));
176 return; 247 return;
177 } 248 }
178 249
179 #if !defined(GOOGLE_CHROME_BUILD) 250 #if !defined(GOOGLE_CHROME_BUILD)
180 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); 251 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
181 if (command_line->HasSwitch(switches::kLocalNtpReload)) { 252 if (command_line->HasSwitch(switches::kLocalNtpReload)) {
182 if (stripped_path == "local-ntp.html" || stripped_path == "local-ntp.js" || 253 if (stripped_path == "local-ntp.html" || stripped_path == "local-ntp.js" ||
183 stripped_path == "local-ntp.css") { 254 stripped_path == "local-ntp.css") {
184 base::ReplaceChars(stripped_path, "-", "_", &stripped_path); 255 base::ReplaceChars(stripped_path, "-", "_", &stripped_path);
185 local_ntp::SendLocalFileResource(stripped_path, callback); 256 local_ntp::SendLocalFileResource(stripped_path, callback);
186 return; 257 return;
187 } 258 }
188 } 259 }
189 #endif // !defined(GOOGLE_CHROME_BUILD) 260 #endif // !defined(GOOGLE_CHROME_BUILD)
190 261
262 if (stripped_path == kMainHtmlFilename) {
263 std::string html = ResourceBundle::GetSharedInstance()
264 .GetRawDataResource(IDR_LOCAL_NTP_HTML)
265 .as_string();
266 std::string config_sha256 =
267 "sha256-" + GetIntegritySha256Value(
268 GetConfigData(default_search_provider_is_google_));
269 base::ReplaceFirstSubstringAfterOffset(&html, 0, "{{CONFIG_INTEGRITY}}",
270 config_sha256);
271 callback.Run(base::RefCountedString::TakeString(&html));
272 return;
273 }
274
191 float scale = 1.0f; 275 float scale = 1.0f;
192 std::string filename; 276 std::string filename;
193 webui::ParsePathAndScale( 277 webui::ParsePathAndScale(
194 GURL(GetLocalNtpPath() + stripped_path), &filename, &scale); 278 GURL(GetLocalNtpPath() + stripped_path), &filename, &scale);
195 ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactor(scale); 279 ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactor(scale);
196 280
197 for (size_t i = 0; i < arraysize(kResources); ++i) { 281 for (size_t i = 0; i < arraysize(kResources); ++i) {
198 if (filename == kResources[i].filename) { 282 if (filename == kResources[i].filename) {
199 scoped_refptr<base::RefCountedMemory> response( 283 scoped_refptr<base::RefCountedMemory> response(
200 ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale( 284 ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
201 kResources[i].identifier, scale_factor)); 285 kResources[i].identifier, scale_factor));
202 callback.Run(response.get()); 286 callback.Run(response.get());
203 return; 287 return;
204 } 288 }
205 } 289 }
206 callback.Run(NULL); 290 callback.Run(nullptr);
207 } 291 }
208 292
209 std::string LocalNtpSource::GetMimeType( 293 std::string LocalNtpSource::GetMimeType(
210 const std::string& path) const { 294 const std::string& path) const {
211 const std::string stripped_path = StripParameters(path); 295 const std::string stripped_path = StripParameters(path);
212 for (size_t i = 0; i < arraysize(kResources); ++i) { 296 for (size_t i = 0; i < arraysize(kResources); ++i) {
213 if (stripped_path == kResources[i].filename) 297 if (stripped_path == kResources[i].filename)
214 return kResources[i].mime_type; 298 return kResources[i].mime_type;
215 } 299 }
216 return std::string(); 300 return std::string();
217 } 301 }
218 302
219 bool LocalNtpSource::AllowCaching() const { 303 bool LocalNtpSource::AllowCaching() const {
220 // Some resources served by LocalNtpSource, i.e. config.js, are dynamically 304 // Some resources served by LocalNtpSource, i.e. config.js, are dynamically
221 // generated and could differ on each access. To avoid using old cached 305 // generated and could differ on each access. To avoid using old cached
222 // content on reload, disallow caching here. Otherwise, it fails to reflect 306 // content on reload, disallow caching here. Otherwise, it fails to reflect
223 // newly revised user configurations in the page. 307 // newly revised user configurations in the page.
224 return false; 308 return false;
225 } 309 }
226 310
227 bool LocalNtpSource::ShouldServiceRequest( 311 bool LocalNtpSource::ShouldServiceRequest(
228 const net::URLRequest* request) const { 312 const net::URLRequest* request) const {
313 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
314
229 DCHECK(request->url().host_piece() == chrome::kChromeSearchLocalNtpHost); 315 DCHECK(request->url().host_piece() == chrome::kChromeSearchLocalNtpHost);
230 if (!InstantIOContext::ShouldServiceRequest(request)) 316 if (!InstantIOContext::ShouldServiceRequest(request))
231 return false; 317 return false;
232 318
233 if (request->url().SchemeIs(chrome::kChromeSearchScheme)) { 319 if (request->url().SchemeIs(chrome::kChromeSearchScheme)) {
234 std::string filename; 320 std::string filename;
235 webui::ParsePathAndScale(request->url(), &filename, NULL); 321 webui::ParsePathAndScale(request->url(), &filename, nullptr);
236 for (size_t i = 0; i < arraysize(kResources); ++i) { 322 for (size_t i = 0; i < arraysize(kResources); ++i) {
237 if (filename == kResources[i].filename) 323 if (filename == kResources[i].filename)
238 return true; 324 return true;
239 } 325 }
240 } 326 }
241 return false; 327 return false;
242 } 328 }
243 329
330 std::string LocalNtpSource::GetContentSecurityPolicyScriptSrc() const {
331 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
332
333 #if !defined(GOOGLE_CHROME_BUILD)
334 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
335 if (command_line->HasSwitch(switches::kLocalNtpReload)) {
336 // While live-editing the local NTP files, turn off CSP.
337 return "script-src *;";
338 }
339 #endif // !defined(GOOGLE_CHROME_BUILD)
340
341 return "script-src 'strict-dynamic' "
342 "'sha256-" +
343 GetIntegritySha256Value(
344 GetConfigData(default_search_provider_is_google_io_thread_)) +
345 "' "
346 "'sha256-g38WaUaxnOIWY7E2LtLZ5ff9r5sn1dBj80jevt/kmx0=';";
347 }
348
244 std::string LocalNtpSource::GetContentSecurityPolicyChildSrc() const { 349 std::string LocalNtpSource::GetContentSecurityPolicyChildSrc() const {
350 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
351
245 // Allow embedding of most visited iframes. 352 // Allow embedding of most visited iframes.
246 return base::StringPrintf("child-src %s;", 353 return base::StringPrintf("child-src %s;",
247 chrome::kChromeSearchMostVisitedUrl); 354 chrome::kChromeSearchMostVisitedUrl);
248 } 355 }
356
357 void LocalNtpSource::DefaultSearchProviderIsGoogleChanged(bool is_google) {
358 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
359
360 default_search_provider_is_google_ = is_google;
361 content::BrowserThread::PostTask(
362 content::BrowserThread::IO, FROM_HERE,
363 base::Bind(&LocalNtpSource::SetDefaultSearchProviderIsGoogleOnIOThread,
364 weak_ptr_factory_.GetWeakPtr(), is_google));
365 }
366
367 void LocalNtpSource::SetDefaultSearchProviderIsGoogleOnIOThread(
368 bool is_google) {
369 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
370
371 default_search_provider_is_google_io_thread_ = is_google;
372 }
OLDNEW
« no previous file with comments | « chrome/browser/search/local_ntp_source.h ('k') | chrome/browser/ui/search/local_ntp_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698