blob: 257f7a7832b4172b7c994f280c5be6ac8a25b043 [file] [log] [blame]
[email protected]33acd5b2014-08-19 19:56:221// Copyright 2014 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// End-to-end SDCH tests. Uses the embedded test server to return SDCH
6// results
7
avi6846aef2015-12-26 01:09:388#include <stddef.h>
9#include <stdint.h>
dchenge73d8520c2015-12-27 01:19:0910#include <utility>
avi6846aef2015-12-26 01:09:3811
[email protected]33acd5b2014-08-19 19:56:2212#include "base/base64.h"
13#include "base/bind.h"
14#include "base/callback.h"
15#include "base/command_line.h"
16#include "base/files/scoped_temp_dir.h"
17#include "base/memory/weak_ptr.h"
18#include "base/path_service.h"
19#include "base/run_loop.h"
20#include "base/strings/string_tokenizer.h"
21#include "base/strings/string_util.h"
22#include "base/strings/stringprintf.h"
avi6846aef2015-12-26 01:09:3823#include "build/build_config.h"
[email protected]33acd5b2014-08-19 19:56:2224#include "chrome/browser/browser_process.h"
25#include "chrome/browser/browsing_data/browsing_data_helper.h"
26#include "chrome/browser/browsing_data/browsing_data_remover.h"
bauerb4530f302016-01-12 14:44:4527#include "chrome/browser/browsing_data/browsing_data_remover_factory.h"
[email protected]33acd5b2014-08-19 19:56:2228#include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
29#include "chrome/browser/profiles/profile.h"
30#include "chrome/browser/profiles/profile_manager.h"
31#include "chrome/browser/ui/browser.h"
32#include "chrome/browser/ui/browser_tabstrip.h"
33#include "chrome/browser/ui/browser_window.h"
34#include "chrome/browser/ui/tabs/tab_strip_model.h"
35#include "chrome/common/chrome_paths.h"
36#include "chrome/test/base/in_process_browser_test.h"
msramek1c8e19d2017-01-04 20:04:5337#include "components/browsing_data/core/browsing_data_utils.h"
[email protected]33acd5b2014-08-19 19:56:2238#include "content/public/browser/browser_thread.h"
39#include "content/public/common/content_switches.h"
40#include "content/public/test/browser_test_utils.h"
41#include "content/public/test/test_utils.h"
42#include "crypto/sha2.h"
43#include "net/base/sdch_manager.h"
rdsmith47c133b2014-11-06 19:02:3044#include "net/base/sdch_observer.h"
[email protected]33acd5b2014-08-19 19:56:2245#include "net/http/http_response_headers.h"
46#include "net/test/embedded_test_server/embedded_test_server.h"
47#include "net/test/embedded_test_server/http_request.h"
48#include "net/test/embedded_test_server/http_response.h"
49#include "net/url_request/url_fetcher.h"
50#include "net/url_request/url_fetcher_delegate.h"
51#include "net/url_request/url_request_context.h"
52#include "net/url_request/url_request_context_getter.h"
53#include "sdch/open-vcdiff/src/google/vcencoder.h"
54#include "testing/gtest/include/gtest/gtest.h"
55
56#if defined(OS_CHROMEOS)
57#include "chromeos/chromeos_switches.h"
58#endif
59
60namespace {
61
62typedef std::vector<net::test_server::HttpRequest> RequestVector;
tzike4e4d492015-12-21 08:56:1163typedef net::test_server::HttpRequest::HeaderMap HttpRequestHeaderMap;
[email protected]33acd5b2014-08-19 19:56:2264
65// Credit Alfred, Lord Tennyson
66static const char kSampleData[] = "<html><body><pre>"
67 "There lies the port; the vessel puffs her sail:\n"
68 "There gloom the dark, broad seas. My mariners,\n"
69 "Souls that have toil'd, and wrought, and thought with me—\n"
70 "That ever with a frolic welcome took\n"
71 "The thunder and the sunshine, and opposed\n"
72 "Free hearts, free foreheads—you and I are old;\n"
73 "Old age hath yet his honour and his toil;\n"
74 "Death closes all: but something ere the end,\n"
75 "Some work of noble note, may yet be done,\n"
76 "Not unbecoming men that strove with Gods.\n"
77 "The lights begin to twinkle from the rocks:\n"
78 "The long day wanes: the slow moon climbs: the deep\n"
79 "Moans round with many voices. Come, my friends,\n"
80 "'T is not too late to seek a newer world.\n"
81 "Push off, and sitting well in order smite\n"
82 "The sounding furrows; for my purpose holds\n"
83 "To sail beyond the sunset, and the baths\n"
84 "Of all the western stars, until I die.\n"
85 "It may be that the gulfs will wash us down:\n"
86 "It may be we shall touch the Happy Isles,\n"
87 "And see the great Achilles, whom we knew.\n"
88 "Tho' much is taken, much abides; and tho'\n"
89 "We are not now that strength which in old days\n"
90 "Moved earth and heaven, that which we are, we are;\n"
91 "One equal temper of heroic hearts,\n"
92 "Made weak by time and fate, but strong in will\n"
93 "To strive, to seek, to find, and not to yield.\n"
94 "</pre></body></html>";
95
96// Random selection of lines from above, to allow some encoding, but
97// not a trivial encoding.
98static const char kDictionaryContents[] =
99 "The thunder and the sunshine, and opposed\n"
100 "To sail beyond the sunset, and the baths\n"
101 "Of all the western stars, until I die.\n"
102 "Made weak by time and fate, but strong in will\n"
103 "Moans round with many voices. Come, my friends,\n"
104 "The lights begin to twinkle from the rocks:";
105
106static const char kDictionaryURLPath[] = "/dict";
107static const char kDataURLPath[] = "/data";
108
109// Scans in a case-insensitive way for |header| in |map|,
110// returning true if found and setting |*value| to the value
111// of that header. Does not handle multiple instances of the same
112// header.
113bool GetRequestHeader(const HttpRequestHeaderMap& map,
114 const char* header,
115 std::string* value) {
116 for (HttpRequestHeaderMap::const_iterator it = map.begin();
117 it != map.end(); ++it) {
brettw8a800902015-07-10 18:28:33118 if (base::EqualsCaseInsensitiveASCII(it->first, header)) {
[email protected]33acd5b2014-08-19 19:56:22119 *value = it->second;
120 return true;
121 }
122 }
123 return false;
124}
125
126// Do a URL-safe base64 encoding. See the SDCH spec "Dictionary Identifier"
127// section, and RFC 3548 section 4.
128void SafeBase64Encode(const std::string& input_value, std::string* output) {
129 DCHECK(output);
130 base::Base64Encode(input_value, output);
131 std::replace(output->begin(), output->end(), '+', '-');
132 std::replace(output->begin(), output->end(), '/', '_');
133}
134
135// Class that bundles responses for an EmbeddedTestServer().
136// Dictionary is at <domain>/dict, data at <domain>/data.
137// The data is sent SDCH encoded if that's allowed by protoocol.
138class SdchResponseHandler {
139 public:
140 // Do initial preparation so that SDCH requests can be handled.
ki.stfuf38f9312015-09-27 14:44:37141 explicit SdchResponseHandler(const std::string& domain)
142 : cache_sdch_response_(false), weak_ptr_factory_(this) {
[email protected]33acd5b2014-08-19 19:56:22143 // Dictionary
144 sdch_dictionary_contents_ = "Domain: ";
145 sdch_dictionary_contents_ += domain;
146 sdch_dictionary_contents_ += "\n\n";
147 sdch_dictionary_contents_ += kDictionaryContents;
148
149 // Dictionary hash for client and server.
150 char binary_hash[32];
151 crypto::SHA256HashString(sdch_dictionary_contents_, binary_hash,
152 sizeof(binary_hash));
153 SafeBase64Encode(std::string(&binary_hash[0], 6), &dictionary_client_hash_);
154 SafeBase64Encode(std::string(&binary_hash[6], 6), &dictionary_server_hash_);
155
156 // Encoded response.
157 open_vcdiff::HashedDictionary vcdiff_dictionary(
158 kDictionaryContents, strlen(kDictionaryContents));
159 bool result = vcdiff_dictionary.Init();
160 DCHECK(result);
161 open_vcdiff::VCDiffStreamingEncoder encoder(&vcdiff_dictionary, 0, false);
162 encoded_data_ = dictionary_server_hash_;
163 encoded_data_ += '\0';
164 result = encoder.StartEncoding(&encoded_data_);
165 DCHECK(result);
166 result = encoder.EncodeChunk(
167 kSampleData, strlen(kSampleData), &encoded_data_);
168 DCHECK(result);
169 result = encoder.FinishEncoding(&encoded_data_);
170 DCHECK(result);
171 }
172
173 static bool ClientIsAdvertisingSdchEncoding(const HttpRequestHeaderMap& map) {
174 std::string value;
175 if (!GetRequestHeader(map, "accept-encoding", &value))
176 return false;
177 base::StringTokenizer tokenizer(value, " ,");
178 while (tokenizer.GetNext()) {
brettw8a800902015-07-10 18:28:33179 if (base::EqualsCaseInsensitiveASCII(tokenizer.token(), "sdch"))
[email protected]33acd5b2014-08-19 19:56:22180 return true;
181 }
182 return false;
183 }
184
185 bool ShouldRespondWithSdchEncoding(const HttpRequestHeaderMap& map) {
186 std::string value;
187 if (!GetRequestHeader(map, "avail-dictionary", &value))
188 return false;
189 return value == dictionary_client_hash_;
190 }
191
dcheng4e7c0422016-04-14 00:59:05192 std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
[email protected]33acd5b2014-08-19 19:56:22193 const net::test_server::HttpRequest& request) {
194 request_vector_.push_back(request);
195
dcheng4e7c0422016-04-14 00:59:05196 std::unique_ptr<net::test_server::BasicHttpResponse> response(
[email protected]33acd5b2014-08-19 19:56:22197 new net::test_server::BasicHttpResponse);
198 if (request.relative_url == kDataURLPath) {
199 if (ShouldRespondWithSdchEncoding(request.headers)) {
200 // Note that chrome doesn't advertise accepting SDCH encoding
201 // for POSTs (because the meta-refresh hack would break a POST),
202 // but that's not for the server to enforce.
203 DCHECK_NE(encoded_data_, "");
204 response->set_content_type("text/html");
205 response->set_content(encoded_data_);
206 response->AddCustomHeader("Content-Encoding", "sdch");
207 // We allow tests to set caching on the sdch response,
208 // so that we can force an encoded response with no
209 // dictionary.
210 if (cache_sdch_response_)
211 response->AddCustomHeader("Cache-Control", "max-age=3600");
212 else
213 response->AddCustomHeader("Cache-Control", "no-store");
214 } else {
215 response->set_content_type("text/plain");
216 response->set_content(kSampleData);
217 if (ClientIsAdvertisingSdchEncoding(request.headers))
218 response->AddCustomHeader("Get-Dictionary", kDictionaryURLPath);
219 // We never cache the plain data response, to make it
220 // easy to refresh after we get the dictionary.
221 response->AddCustomHeader("Cache-Control", "no-store");
222 }
223 } else {
224 DCHECK_EQ(request.relative_url, kDictionaryURLPath);
225 DCHECK_NE(sdch_dictionary_contents_, "");
ellyjonesd0ff25592015-03-09 22:32:49226 response->AddCustomHeader("Cache-Control", "max-age=3600");
[email protected]33acd5b2014-08-19 19:56:22227 response->set_content_type("application/x-sdch-dictionary");
228 response->set_content(sdch_dictionary_contents_);
229 }
230 std::vector<base::Closure> callbacks;
231 callbacks.swap(callback_vector_);
232 for (std::vector<base::Closure>::iterator it = callbacks.begin();
233 it != callbacks.end(); ++it) {
234 it->Run();
235 }
dchenge73d8520c2015-12-27 01:19:09236 return std::move(response);
[email protected]33acd5b2014-08-19 19:56:22237 }
238
239 void WaitAndGetRequestVector(int num_requests,
240 base::Closure callback,
241 RequestVector* v) {
242 DCHECK_LT(0, num_requests);
243 if (static_cast<size_t>(num_requests) > request_vector_.size()) {
244 callback_vector_.push_back(
245 base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
246 weak_ptr_factory_.GetWeakPtr(), num_requests,
247 callback, v));
248 return;
249 }
250 *v = request_vector_;
251 content::BrowserThread::PostTask(
252 content::BrowserThread::UI, FROM_HERE, callback);
253 }
254
255 void set_cache_sdch_response(bool cache_sdch_response) {
256 cache_sdch_response_ = cache_sdch_response;
257 }
258
259 private:
260 bool cache_sdch_response_;
261 std::string encoded_data_;
262 std::string sdch_dictionary_contents_;
263 std::string dictionary_client_hash_;
264 std::string dictionary_server_hash_;
265 RequestVector request_vector_;
266 std::vector<base::Closure> callback_vector_;
267 base::WeakPtrFactory<SdchResponseHandler> weak_ptr_factory_;
268};
269
ellyjones887d31b2015-05-14 20:28:55270class TestSdchObserver : public net::SdchObserver {
271 public:
272 TestSdchObserver() : manager_(nullptr), fetch_count_(0) {}
273 ~TestSdchObserver() override {
274 if (manager_) {
275 manager_->RemoveObserver(this);
276 }
277 }
278
279 void Observe(net::SdchManager* manager) {
280 DCHECK(!manager_);
281 manager_ = manager;
282 manager_->AddObserver(this);
283 }
284
285 // SdchObserver
286 void OnDictionaryAdded(const GURL& /* dictionary_url */,
287 const std::string& /* server_hash */) override {}
288 void OnDictionaryRemoved(const std::string& /* server_hash */) override {}
289 void OnGetDictionary(const GURL& /* request_url */,
290 const GURL& /* dictionary_url */) override {
291 fetch_count_++;
292 }
293 void OnDictionaryUsed(const std::string& /* server_hash */) override {}
294 void OnClearDictionaries() override {}
295
296 int fetch_count() const { return fetch_count_; }
297
298 private:
299 net::SdchManager* manager_;
300 int fetch_count_;
301};
302
rdsmith47c133b2014-11-06 19:02:30303class SdchBrowserTest : public InProcessBrowserTest,
ellyjones887d31b2015-05-14 20:28:55304 public net::URLFetcherDelegate {
[email protected]33acd5b2014-08-19 19:56:22305 public:
306 static const char kTestHost[];
307
308 SdchBrowserTest()
309 : response_handler_(kTestHost),
310 url_request_context_getter_(NULL),
311 url_fetch_complete_(false),
312 waiting_(false) {}
313
314 // Helper functions for fetching data.
315
316 void FetchUrlDetailed(GURL url, net::URLRequestContextGetter* getter) {
317 url_fetch_complete_ = false;
dtapuskadafcf892015-05-01 13:58:25318 fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this);
[email protected]33acd5b2014-08-19 19:56:22319 fetcher_->SetRequestContext(getter);
320 fetcher_->Start();
321 if (!url_fetch_complete_) {
322 waiting_ = true;
323 content::RunMessageLoop();
324 waiting_ = false;
325 }
326 CHECK(url_fetch_complete_);
327 }
328
329 void FetchUrl(GURL url) {
dcheng758c95c2014-08-26 22:07:37330 FetchUrlDetailed(url, url_request_context_getter_.get());
[email protected]33acd5b2014-08-19 19:56:22331 }
332
333 const net::URLRequestStatus& FetcherStatus() const {
334 return fetcher_->GetStatus();
335 }
336
337 int FetcherResponseCode() const {
338 return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
339 fetcher_->GetResponseCode() : 0);
340 }
341
342 const net::HttpResponseHeaders* FetcherResponseHeaders() const {
343 return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
344 fetcher_->GetResponseHeaders() : NULL);
345 }
346
347 std::string FetcherResponseContents() const {
348 std::string contents;
349 if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS)
350 CHECK(fetcher_->GetResponseAsString(&contents));
351 return contents;
352 }
353
354 // Get the data from the server. Return value is success/failure of the
355 // data operation, |*sdch_encoding_used| indicates whether or not the
356 // data was retrieved with sdch encoding.
357 // This is done through FetchUrl(), so the various helper functions
358 // will have valid status if it returns successfully.
359 bool GetDataDetailed(net::URLRequestContextGetter* getter,
360 bool* sdch_encoding_used) {
361 FetchUrlDetailed(
362 GURL(base::StringPrintf(
pkastingcba13292014-11-20 03:35:21363 "http://%s:%u%s", kTestHost, test_server_port(), kDataURLPath)),
[email protected]33acd5b2014-08-19 19:56:22364 getter);
365 EXPECT_EQ(net::URLRequestStatus::SUCCESS, FetcherStatus().status())
366 << "Error code is " << FetcherStatus().error();
367 EXPECT_EQ(200, FetcherResponseCode());
368 EXPECT_EQ(kSampleData, FetcherResponseContents());
369
370 if (net::URLRequestStatus::SUCCESS != FetcherStatus().status() ||
371 200 != FetcherResponseCode()) {
372 *sdch_encoding_used = false;
373 return false;
374 }
375
376 *sdch_encoding_used =
377 FetcherResponseHeaders()->HasHeaderValue("Content-Encoding", "sdch");
378
379 if (FetcherResponseContents() != kSampleData)
380 return false;
381
382 return true;
383 }
384
385 bool GetData(bool* sdch_encoding_used) {
dcheng758c95c2014-08-26 22:07:37386 return GetDataDetailed(url_request_context_getter_.get(),
387 sdch_encoding_used);
[email protected]33acd5b2014-08-19 19:56:22388 }
389
390 // Client information and control.
391
392 int GetNumberOfDictionaryFetches(Profile* profile) {
393 int fetches = -1;
394 base::RunLoop run_loop;
395 content::BrowserThread::PostTaskAndReply(
rdsmith47c133b2014-11-06 19:02:30396 content::BrowserThread::IO,
397 FROM_HERE,
[email protected]33acd5b2014-08-19 19:56:22398 base::Bind(&SdchBrowserTest::GetNumberOfDictionaryFetchesOnIOThread,
rdsmith47c133b2014-11-06 19:02:30399 base::Unretained(this),
[email protected]33acd5b2014-08-19 19:56:22400 base::Unretained(profile->GetRequestContext()),
401 &fetches),
402 run_loop.QuitClosure());
403 run_loop.Run();
404 DCHECK_NE(-1, fetches);
405 return fetches;
406 }
407
408 void BrowsingDataRemoveAndWait(int remove_mask) {
bauerb4530f302016-01-12 14:44:45409 BrowsingDataRemover* remover =
410 BrowsingDataRemoverFactory::GetForBrowserContext(browser()->profile());
[email protected]33acd5b2014-08-19 19:56:22411 BrowsingDataRemoverCompletionObserver completion_observer(remover);
msramek6f166832016-08-05 08:44:28412 remover->RemoveAndReply(
msramek1c8e19d2017-01-04 20:04:53413 browsing_data::CalculateBeginDeleteTime(browsing_data::LAST_HOUR),
414 browsing_data::CalculateEndDeleteTime(browsing_data::LAST_HOUR),
415 remove_mask, BrowsingDataHelper::UNPROTECTED_WEB, &completion_observer);
[email protected]33acd5b2014-08-19 19:56:22416 completion_observer.BlockUntilCompletion();
417 }
418
419 // Something of a cheat; nuke the dictionaries off the SdchManager without
420 // touching the cache (which browsing data remover would do).
421 void NukeSdchDictionaries() {
422 base::RunLoop run_loop;
423 content::BrowserThread::PostTaskAndReply(
424 content::BrowserThread::IO, FROM_HERE,
425 base::Bind(&SdchBrowserTest::NukeSdchDictionariesOnIOThread,
vmpstra34d11322016-03-21 20:28:47426 base::RetainedRef(url_request_context_getter_)),
[email protected]33acd5b2014-08-19 19:56:22427 run_loop.QuitClosure());
428 run_loop.Run();
429 }
430
431 // Create a second browser based on a second profile to work within
432 // multi-profile.
433 bool SetupSecondBrowser() {
434 base::FilePath user_data_dir;
435 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
436
437 if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir))
438 return false;
439
440 second_profile_ = g_browser_process->profile_manager()->GetProfile(
vabrb8582322016-09-09 08:05:37441 second_profile_data_dir_.GetPath());
[email protected]33acd5b2014-08-19 19:56:22442 if (!second_profile_) return false;
443
scottmg851949002016-02-09 20:09:44444 second_browser_ = new Browser(Browser::CreateParams(second_profile_));
[email protected]33acd5b2014-08-19 19:56:22445 if (!second_browser_) return false;
446
447 chrome::AddSelectedTabWithURL(second_browser_,
448 GURL(url::kAboutBlankURL),
Sylvain Defresnec6ccc77d2014-09-19 10:19:35449 ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
[email protected]33acd5b2014-08-19 19:56:22450 content::WaitForLoadStop(
451 second_browser_->tab_strip_model()->GetActiveWebContents());
452 second_browser_->window()->Show();
453
rdsmith47c133b2014-11-06 19:02:30454 content::BrowserThread::PostTask(
vmpstra34d11322016-03-21 20:28:47455 content::BrowserThread::IO, FROM_HERE,
rdsmith47c133b2014-11-06 19:02:30456 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
457 base::Unretained(this),
vmpstra34d11322016-03-21 20:28:47458 base::RetainedRef(
rdsmith47c133b2014-11-06 19:02:30459 second_browser_->profile()->GetRequestContext())));
460
461 return true;
462 }
463
464 bool SetupIncognitoBrowser() {
465 incognito_browser_ = CreateIncognitoBrowser();
466
467 if (!incognito_browser_)
468 return false;
469
470 content::BrowserThread::PostTask(
vmpstra34d11322016-03-21 20:28:47471 content::BrowserThread::IO, FROM_HERE,
rdsmith47c133b2014-11-06 19:02:30472 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
473 base::Unretained(this),
vmpstra34d11322016-03-21 20:28:47474 base::RetainedRef(
rdsmith47c133b2014-11-06 19:02:30475 incognito_browser_->profile()->GetRequestContext())));
476
[email protected]33acd5b2014-08-19 19:56:22477 return true;
478 }
479
480 Browser* second_browser() { return second_browser_; }
rdsmith47c133b2014-11-06 19:02:30481 Browser* incognito_browser() { return incognito_browser_; }
[email protected]33acd5b2014-08-19 19:56:22482
483 // Server information and control.
484
485 void WaitAndGetTestVector(int num_requests, RequestVector* result) {
486 base::RunLoop run_loop;
487 content::BrowserThread::PostTask(
488 content::BrowserThread::IO, FROM_HERE,
489 base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
490 base::Unretained(&response_handler_),
491 num_requests,
492 run_loop.QuitClosure(),
493 result));
494 run_loop.Run();
495 }
496
avi6846aef2015-12-26 01:09:38497 uint16_t test_server_port() { return test_server_.port(); }
[email protected]33acd5b2014-08-19 19:56:22498
499 void SetSdchCacheability(bool cache_sdch_response) {
500 base::RunLoop run_loop;
501 content::BrowserThread::PostTaskAndReply(
502 content::BrowserThread::IO, FROM_HERE,
503 base::Bind(&SdchResponseHandler::set_cache_sdch_response,
504 base::Unretained(&response_handler_),
505 cache_sdch_response),
506 run_loop.QuitClosure());
507 run_loop.Run();
508 }
509
510 // Helper function for common test pattern.
511 //
512 // This function gets the data, confirms that the initial sending of the
513 // data included a dictionary advertisement, that that advertisement
514 // resulted in queueing a dictionary fetch, forces that fetch to
515 // go through, and confirms that a follow-on data load uses SDCH
516 // encoding. Returns true if the entire sequence of events occurred.
517 bool ForceSdchDictionaryLoad(Browser* browser) {
518 bool sdch_encoding_used = true;
519 bool data_gotten = GetDataDetailed(
520 browser->profile()->GetRequestContext(), &sdch_encoding_used);
521 EXPECT_TRUE(data_gotten);
522 if (!data_gotten) return false;
523 EXPECT_FALSE(sdch_encoding_used);
524
525 // Confirm that we were told to get the dictionary
526 const net::HttpResponseHeaders* headers = FetcherResponseHeaders();
527 std::string value;
528 bool have_dict_header =
529 headers->EnumerateHeader(NULL, "Get-Dictionary", &value);
530 EXPECT_TRUE(have_dict_header);
531 if (!have_dict_header) return false;
532
533 // If the above didn't result in a dictionary fetch being queued, the
534 // rest of the test will time out. Avoid that.
535 int num_fetches = GetNumberOfDictionaryFetches(browser->profile());
536 EXPECT_EQ(1, num_fetches);
537 if (1 != num_fetches) return false;
538
539 // Wait until the dictionary fetch actually happens.
540 RequestVector request_vector;
541 WaitAndGetTestVector(2, &request_vector);
542 EXPECT_EQ(request_vector[1].relative_url, kDictionaryURLPath);
543 if (request_vector[1].relative_url != kDictionaryURLPath) return false;
544
545 // Do a round trip to the server ignoring the encoding, presuming
546 // that if we've gotten data to this thread, the dictionary's made
547 // it into the SdchManager.
548 data_gotten = GetDataDetailed(
549 browser->profile()->GetRequestContext(), &sdch_encoding_used);
550 EXPECT_TRUE(data_gotten);
551 if (!data_gotten) return false;
552
553 // Now data fetches should be SDCH encoded.
554 sdch_encoding_used = false;
555 data_gotten = GetDataDetailed(
556 browser->profile()->GetRequestContext(), &sdch_encoding_used);
557 EXPECT_TRUE(data_gotten);
558 EXPECT_TRUE(sdch_encoding_used);
559
560 if (!data_gotten || !sdch_encoding_used) return false;
561
562 // Confirm the request vector looks at this point as expected.
563 WaitAndGetTestVector(4, &request_vector);
564 EXPECT_EQ(4u, request_vector.size());
565 EXPECT_EQ(request_vector[2].relative_url, kDataURLPath);
566 EXPECT_EQ(request_vector[3].relative_url, kDataURLPath);
567 return (4u == request_vector.size() &&
568 request_vector[2].relative_url == kDataURLPath &&
569 request_vector[3].relative_url == kDataURLPath);
570 }
571
572 private:
573 static void NukeSdchDictionariesOnIOThread(
574 net::URLRequestContextGetter* context_getter) {
anujk.sharma2e02ce162015-04-29 23:10:02575 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
[email protected]33acd5b2014-08-19 19:56:22576 net::SdchManager* sdch_manager =
577 context_getter->GetURLRequestContext()->sdch_manager();
578 DCHECK(sdch_manager);
579 sdch_manager->ClearData();
580 }
581
rdsmith47c133b2014-11-06 19:02:30582 void GetNumberOfDictionaryFetchesOnIOThread(
583 net::URLRequestContextGetter* context_getter,
[email protected]33acd5b2014-08-19 19:56:22584 int* result) {
anujk.sharma2e02ce162015-04-29 23:10:02585 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
rdsmith47c133b2014-11-06 19:02:30586
587 net::SdchManager* manager(
588 context_getter->GetURLRequestContext()->sdch_manager());
ellyjones887d31b2015-05-14 20:28:55589 DCHECK(observers_.end() != observers_.find(manager));
rdsmith47c133b2014-11-06 19:02:30590
ellyjones887d31b2015-05-14 20:28:55591 *result = observers_[manager].fetch_count();
[email protected]33acd5b2014-08-19 19:56:22592 }
593
594 // InProcessBrowserTest
dchengfce29ad2014-10-23 03:47:47595 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]33acd5b2014-08-19 19:56:22596 command_line->AppendSwitchASCII(
597 switches::kHostResolverRules,
598 "MAP " + std::string(kTestHost) + " 127.0.0.1");
599#if defined(OS_CHROMEOS)
600 command_line->AppendSwitch(
601 chromeos::switches::kIgnoreUserProfileMappingForTests);
602#endif
603 }
604
dchengfce29ad2014-10-23 03:47:47605 void SetUpOnMainThread() override {
[email protected]33acd5b2014-08-19 19:56:22606 test_server_.RegisterRequestHandler(
607 base::Bind(&SdchResponseHandler::HandleRequest,
608 base::Unretained(&response_handler_)));
svaldeze2745872015-11-04 23:30:20609 CHECK(test_server_.Start());
[email protected]33acd5b2014-08-19 19:56:22610 url_request_context_getter_ = browser()->profile()->GetRequestContext();
rdsmith47c133b2014-11-06 19:02:30611
612 content::BrowserThread::PostTask(
vmpstra34d11322016-03-21 20:28:47613 content::BrowserThread::IO, FROM_HERE,
rdsmith47c133b2014-11-06 19:02:30614 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
615 base::Unretained(this),
vmpstra34d11322016-03-21 20:28:47616 base::RetainedRef(url_request_context_getter_)));
[email protected]33acd5b2014-08-19 19:56:22617 }
618
dchengfce29ad2014-10-23 03:47:47619 void TearDownOnMainThread() override {
[email protected]33acd5b2014-08-19 19:56:22620 CHECK(test_server_.ShutdownAndWaitUntilComplete());
rdsmith47c133b2014-11-06 19:02:30621
622 content::BrowserThread::PostTask(
623 content::BrowserThread::IO,
624 FROM_HERE,
625 base::Bind(&SdchBrowserTest::UnsubscribeFromAllSdchNotifications,
626 base::Unretained(this)));
[email protected]33acd5b2014-08-19 19:56:22627 }
628
rdsmith47c133b2014-11-06 19:02:30629 void SubscribeToSdchNotifications(
630 net::URLRequestContextGetter* context_getter) {
anujk.sharma2e02ce162015-04-29 23:10:02631 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
rdsmith47c133b2014-11-06 19:02:30632
633 net::SdchManager* manager =
634 context_getter->GetURLRequestContext()->sdch_manager();
ellyjones887d31b2015-05-14 20:28:55635 DCHECK(observers_.end() == observers_.find(manager));
rdsmith47c133b2014-11-06 19:02:30636
ellyjones887d31b2015-05-14 20:28:55637 observers_[manager].Observe(manager);
rdsmith47c133b2014-11-06 19:02:30638 }
639
640 void UnsubscribeFromAllSdchNotifications() {
anujk.sharma2e02ce162015-04-29 23:10:02641 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
ellyjones887d31b2015-05-14 20:28:55642 observers_.clear();
rdsmith47c133b2014-11-06 19:02:30643 }
644
[email protected]33acd5b2014-08-19 19:56:22645 // URLFetcherDelegate
dchengfce29ad2014-10-23 03:47:47646 void OnURLFetchComplete(const net::URLFetcher* source) override {
[email protected]33acd5b2014-08-19 19:56:22647 url_fetch_complete_ = true;
648 if (waiting_)
ki.stfuc4f8e242015-10-09 20:40:20649 base::MessageLoopForUI::current()->QuitWhenIdle();
[email protected]33acd5b2014-08-19 19:56:22650 }
651
652 SdchResponseHandler response_handler_;
svaldeze2745872015-11-04 23:30:20653 net::EmbeddedTestServer test_server_;
[email protected]33acd5b2014-08-19 19:56:22654 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
dcheng4e7c0422016-04-14 00:59:05655 std::unique_ptr<net::URLFetcher> fetcher_;
[email protected]33acd5b2014-08-19 19:56:22656 bool url_fetch_complete_;
657 bool waiting_;
658 base::ScopedTempDir second_profile_data_dir_;
659 Profile* second_profile_;
660 Browser* second_browser_;
rdsmith47c133b2014-11-06 19:02:30661 Browser* incognito_browser_;
662
663 // IO Thread access only.
ellyjones887d31b2015-05-14 20:28:55664 std::map<net::SdchManager*, TestSdchObserver> observers_;
[email protected]33acd5b2014-08-19 19:56:22665};
666
667const char SdchBrowserTest::kTestHost[] = "our.test.host.com";
668
669// Confirm that after getting a dictionary, calling the browsing
670// data remover renders it unusable. Also (in calling
671// ForceSdchDictionaryLoad()) servers as a smoke test for SDCH.
672IN_PROC_BROWSER_TEST_F(SdchBrowserTest, BrowsingDataRemover) {
673 ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
674
675 // Confirm browsing data remover without removing the cache leaves
676 // SDCH alone.
677 BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_ALL &
678 ~BrowsingDataRemover::REMOVE_CACHE);
679 bool sdch_encoding_used = false;
680 ASSERT_TRUE(GetData(&sdch_encoding_used));
681 EXPECT_TRUE(sdch_encoding_used);
682
683 // Confirm browsing data remover removing the cache clears SDCH state.
684 BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_CACHE);
685 sdch_encoding_used = false;
686 ASSERT_TRUE(GetData(&sdch_encoding_used));
687 EXPECT_FALSE(sdch_encoding_used);
688}
689
690// Confirm dictionaries not visible in other profiles.
691IN_PROC_BROWSER_TEST_F(SdchBrowserTest, Isolation) {
692 ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
693 ASSERT_TRUE(SetupSecondBrowser());
rdsmith47c133b2014-11-06 19:02:30694 ASSERT_TRUE(SetupIncognitoBrowser());
[email protected]33acd5b2014-08-19 19:56:22695
696 // Data fetches from incognito or separate profiles should not be SDCH
697 // encoded.
698 bool sdch_encoding_used = true;
rdsmith47c133b2014-11-06 19:02:30699 EXPECT_TRUE(
700 GetDataDetailed(incognito_browser()->profile()->GetRequestContext(),
701 &sdch_encoding_used));
[email protected]33acd5b2014-08-19 19:56:22702 EXPECT_FALSE(sdch_encoding_used);
703
704 sdch_encoding_used = true;
705 EXPECT_TRUE(GetDataDetailed(
706 second_browser()->profile()->GetRequestContext(), &sdch_encoding_used));
707 EXPECT_FALSE(sdch_encoding_used);
708}
709
710// Confirm a dictionary loaded in incognito isn't visible in the main profile.
711IN_PROC_BROWSER_TEST_F(SdchBrowserTest, ReverseIsolation) {
rdsmith47c133b2014-11-06 19:02:30712 ASSERT_TRUE(SetupIncognitoBrowser());
713 ASSERT_TRUE(ForceSdchDictionaryLoad(incognito_browser()));
[email protected]33acd5b2014-08-19 19:56:22714
715 // Data fetches on main browser should not be SDCH encoded.
716 bool sdch_encoding_used = true;
717 ASSERT_TRUE(GetData(&sdch_encoding_used));
718 EXPECT_FALSE(sdch_encoding_used);
719}
720
721} // namespace