blob: 3180a190442a6d2f0ca280f38b5539010440c7f1 [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
8#include "base/base64.h"
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/command_line.h"
12#include "base/files/scoped_temp_dir.h"
13#include "base/memory/weak_ptr.h"
14#include "base/path_service.h"
15#include "base/run_loop.h"
16#include "base/strings/string_tokenizer.h"
17#include "base/strings/string_util.h"
18#include "base/strings/stringprintf.h"
19#include "chrome/browser/browser_process.h"
20#include "chrome/browser/browsing_data/browsing_data_helper.h"
21#include "chrome/browser/browsing_data/browsing_data_remover.h"
22#include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_manager.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_tabstrip.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/tabs/tab_strip_model.h"
29#include "chrome/common/chrome_paths.h"
30#include "chrome/test/base/in_process_browser_test.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/common/content_switches.h"
33#include "content/public/test/browser_test_utils.h"
34#include "content/public/test/test_utils.h"
35#include "crypto/sha2.h"
36#include "net/base/sdch_manager.h"
rdsmith47c133b2014-11-06 19:02:3037#include "net/base/sdch_observer.h"
[email protected]33acd5b2014-08-19 19:56:2238#include "net/http/http_response_headers.h"
39#include "net/test/embedded_test_server/embedded_test_server.h"
40#include "net/test/embedded_test_server/http_request.h"
41#include "net/test/embedded_test_server/http_response.h"
42#include "net/url_request/url_fetcher.h"
43#include "net/url_request/url_fetcher_delegate.h"
44#include "net/url_request/url_request_context.h"
45#include "net/url_request/url_request_context_getter.h"
46#include "sdch/open-vcdiff/src/google/vcencoder.h"
47#include "testing/gtest/include/gtest/gtest.h"
48
49#if defined(OS_CHROMEOS)
50#include "chromeos/chromeos_switches.h"
51#endif
52
53namespace {
54
55typedef std::vector<net::test_server::HttpRequest> RequestVector;
56typedef std::map<std::string, std::string> HttpRequestHeaderMap;
57
58// Credit Alfred, Lord Tennyson
59static const char kSampleData[] = "<html><body><pre>"
60 "There lies the port; the vessel puffs her sail:\n"
61 "There gloom the dark, broad seas. My mariners,\n"
62 "Souls that have toil'd, and wrought, and thought with me—\n"
63 "That ever with a frolic welcome took\n"
64 "The thunder and the sunshine, and opposed\n"
65 "Free hearts, free foreheads—you and I are old;\n"
66 "Old age hath yet his honour and his toil;\n"
67 "Death closes all: but something ere the end,\n"
68 "Some work of noble note, may yet be done,\n"
69 "Not unbecoming men that strove with Gods.\n"
70 "The lights begin to twinkle from the rocks:\n"
71 "The long day wanes: the slow moon climbs: the deep\n"
72 "Moans round with many voices. Come, my friends,\n"
73 "'T is not too late to seek a newer world.\n"
74 "Push off, and sitting well in order smite\n"
75 "The sounding furrows; for my purpose holds\n"
76 "To sail beyond the sunset, and the baths\n"
77 "Of all the western stars, until I die.\n"
78 "It may be that the gulfs will wash us down:\n"
79 "It may be we shall touch the Happy Isles,\n"
80 "And see the great Achilles, whom we knew.\n"
81 "Tho' much is taken, much abides; and tho'\n"
82 "We are not now that strength which in old days\n"
83 "Moved earth and heaven, that which we are, we are;\n"
84 "One equal temper of heroic hearts,\n"
85 "Made weak by time and fate, but strong in will\n"
86 "To strive, to seek, to find, and not to yield.\n"
87 "</pre></body></html>";
88
89// Random selection of lines from above, to allow some encoding, but
90// not a trivial encoding.
91static const char kDictionaryContents[] =
92 "The thunder and the sunshine, and opposed\n"
93 "To sail beyond the sunset, and the baths\n"
94 "Of all the western stars, until I die.\n"
95 "Made weak by time and fate, but strong in will\n"
96 "Moans round with many voices. Come, my friends,\n"
97 "The lights begin to twinkle from the rocks:";
98
99static const char kDictionaryURLPath[] = "/dict";
100static const char kDataURLPath[] = "/data";
101
102// Scans in a case-insensitive way for |header| in |map|,
103// returning true if found and setting |*value| to the value
104// of that header. Does not handle multiple instances of the same
105// header.
106bool GetRequestHeader(const HttpRequestHeaderMap& map,
107 const char* header,
108 std::string* value) {
109 for (HttpRequestHeaderMap::const_iterator it = map.begin();
110 it != map.end(); ++it) {
brettw8a800902015-07-10 18:28:33111 if (base::EqualsCaseInsensitiveASCII(it->first, header)) {
[email protected]33acd5b2014-08-19 19:56:22112 *value = it->second;
113 return true;
114 }
115 }
116 return false;
117}
118
119// Do a URL-safe base64 encoding. See the SDCH spec "Dictionary Identifier"
120// section, and RFC 3548 section 4.
121void SafeBase64Encode(const std::string& input_value, std::string* output) {
122 DCHECK(output);
123 base::Base64Encode(input_value, output);
124 std::replace(output->begin(), output->end(), '+', '-');
125 std::replace(output->begin(), output->end(), '/', '_');
126}
127
128// Class that bundles responses for an EmbeddedTestServer().
129// Dictionary is at <domain>/dict, data at <domain>/data.
130// The data is sent SDCH encoded if that's allowed by protoocol.
131class SdchResponseHandler {
132 public:
133 // Do initial preparation so that SDCH requests can be handled.
ki.stfuf38f9312015-09-27 14:44:37134 explicit SdchResponseHandler(const std::string& domain)
135 : cache_sdch_response_(false), weak_ptr_factory_(this) {
[email protected]33acd5b2014-08-19 19:56:22136 // Dictionary
137 sdch_dictionary_contents_ = "Domain: ";
138 sdch_dictionary_contents_ += domain;
139 sdch_dictionary_contents_ += "\n\n";
140 sdch_dictionary_contents_ += kDictionaryContents;
141
142 // Dictionary hash for client and server.
143 char binary_hash[32];
144 crypto::SHA256HashString(sdch_dictionary_contents_, binary_hash,
145 sizeof(binary_hash));
146 SafeBase64Encode(std::string(&binary_hash[0], 6), &dictionary_client_hash_);
147 SafeBase64Encode(std::string(&binary_hash[6], 6), &dictionary_server_hash_);
148
149 // Encoded response.
150 open_vcdiff::HashedDictionary vcdiff_dictionary(
151 kDictionaryContents, strlen(kDictionaryContents));
152 bool result = vcdiff_dictionary.Init();
153 DCHECK(result);
154 open_vcdiff::VCDiffStreamingEncoder encoder(&vcdiff_dictionary, 0, false);
155 encoded_data_ = dictionary_server_hash_;
156 encoded_data_ += '\0';
157 result = encoder.StartEncoding(&encoded_data_);
158 DCHECK(result);
159 result = encoder.EncodeChunk(
160 kSampleData, strlen(kSampleData), &encoded_data_);
161 DCHECK(result);
162 result = encoder.FinishEncoding(&encoded_data_);
163 DCHECK(result);
164 }
165
166 static bool ClientIsAdvertisingSdchEncoding(const HttpRequestHeaderMap& map) {
167 std::string value;
168 if (!GetRequestHeader(map, "accept-encoding", &value))
169 return false;
170 base::StringTokenizer tokenizer(value, " ,");
171 while (tokenizer.GetNext()) {
brettw8a800902015-07-10 18:28:33172 if (base::EqualsCaseInsensitiveASCII(tokenizer.token(), "sdch"))
[email protected]33acd5b2014-08-19 19:56:22173 return true;
174 }
175 return false;
176 }
177
178 bool ShouldRespondWithSdchEncoding(const HttpRequestHeaderMap& map) {
179 std::string value;
180 if (!GetRequestHeader(map, "avail-dictionary", &value))
181 return false;
182 return value == dictionary_client_hash_;
183 }
184
185 scoped_ptr<net::test_server::HttpResponse> HandleRequest(
186 const net::test_server::HttpRequest& request) {
187 request_vector_.push_back(request);
188
189 scoped_ptr<net::test_server::BasicHttpResponse> response(
190 new net::test_server::BasicHttpResponse);
191 if (request.relative_url == kDataURLPath) {
192 if (ShouldRespondWithSdchEncoding(request.headers)) {
193 // Note that chrome doesn't advertise accepting SDCH encoding
194 // for POSTs (because the meta-refresh hack would break a POST),
195 // but that's not for the server to enforce.
196 DCHECK_NE(encoded_data_, "");
197 response->set_content_type("text/html");
198 response->set_content(encoded_data_);
199 response->AddCustomHeader("Content-Encoding", "sdch");
200 // We allow tests to set caching on the sdch response,
201 // so that we can force an encoded response with no
202 // dictionary.
203 if (cache_sdch_response_)
204 response->AddCustomHeader("Cache-Control", "max-age=3600");
205 else
206 response->AddCustomHeader("Cache-Control", "no-store");
207 } else {
208 response->set_content_type("text/plain");
209 response->set_content(kSampleData);
210 if (ClientIsAdvertisingSdchEncoding(request.headers))
211 response->AddCustomHeader("Get-Dictionary", kDictionaryURLPath);
212 // We never cache the plain data response, to make it
213 // easy to refresh after we get the dictionary.
214 response->AddCustomHeader("Cache-Control", "no-store");
215 }
216 } else {
217 DCHECK_EQ(request.relative_url, kDictionaryURLPath);
218 DCHECK_NE(sdch_dictionary_contents_, "");
ellyjonesd0ff25592015-03-09 22:32:49219 response->AddCustomHeader("Cache-Control", "max-age=3600");
[email protected]33acd5b2014-08-19 19:56:22220 response->set_content_type("application/x-sdch-dictionary");
221 response->set_content(sdch_dictionary_contents_);
222 }
223 std::vector<base::Closure> callbacks;
224 callbacks.swap(callback_vector_);
225 for (std::vector<base::Closure>::iterator it = callbacks.begin();
226 it != callbacks.end(); ++it) {
227 it->Run();
228 }
dcheng0f3f5252014-10-16 19:36:46229 return response.Pass();
[email protected]33acd5b2014-08-19 19:56:22230 }
231
232 void WaitAndGetRequestVector(int num_requests,
233 base::Closure callback,
234 RequestVector* v) {
235 DCHECK_LT(0, num_requests);
236 if (static_cast<size_t>(num_requests) > request_vector_.size()) {
237 callback_vector_.push_back(
238 base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
239 weak_ptr_factory_.GetWeakPtr(), num_requests,
240 callback, v));
241 return;
242 }
243 *v = request_vector_;
244 content::BrowserThread::PostTask(
245 content::BrowserThread::UI, FROM_HERE, callback);
246 }
247
248 void set_cache_sdch_response(bool cache_sdch_response) {
249 cache_sdch_response_ = cache_sdch_response;
250 }
251
252 private:
253 bool cache_sdch_response_;
254 std::string encoded_data_;
255 std::string sdch_dictionary_contents_;
256 std::string dictionary_client_hash_;
257 std::string dictionary_server_hash_;
258 RequestVector request_vector_;
259 std::vector<base::Closure> callback_vector_;
260 base::WeakPtrFactory<SdchResponseHandler> weak_ptr_factory_;
261};
262
ellyjones887d31b2015-05-14 20:28:55263class TestSdchObserver : public net::SdchObserver {
264 public:
265 TestSdchObserver() : manager_(nullptr), fetch_count_(0) {}
266 ~TestSdchObserver() override {
267 if (manager_) {
268 manager_->RemoveObserver(this);
269 }
270 }
271
272 void Observe(net::SdchManager* manager) {
273 DCHECK(!manager_);
274 manager_ = manager;
275 manager_->AddObserver(this);
276 }
277
278 // SdchObserver
279 void OnDictionaryAdded(const GURL& /* dictionary_url */,
280 const std::string& /* server_hash */) override {}
281 void OnDictionaryRemoved(const std::string& /* server_hash */) override {}
282 void OnGetDictionary(const GURL& /* request_url */,
283 const GURL& /* dictionary_url */) override {
284 fetch_count_++;
285 }
286 void OnDictionaryUsed(const std::string& /* server_hash */) override {}
287 void OnClearDictionaries() override {}
288
289 int fetch_count() const { return fetch_count_; }
290
291 private:
292 net::SdchManager* manager_;
293 int fetch_count_;
294};
295
rdsmith47c133b2014-11-06 19:02:30296class SdchBrowserTest : public InProcessBrowserTest,
ellyjones887d31b2015-05-14 20:28:55297 public net::URLFetcherDelegate {
[email protected]33acd5b2014-08-19 19:56:22298 public:
299 static const char kTestHost[];
300
301 SdchBrowserTest()
302 : response_handler_(kTestHost),
303 url_request_context_getter_(NULL),
304 url_fetch_complete_(false),
305 waiting_(false) {}
306
307 // Helper functions for fetching data.
308
309 void FetchUrlDetailed(GURL url, net::URLRequestContextGetter* getter) {
310 url_fetch_complete_ = false;
dtapuskadafcf892015-05-01 13:58:25311 fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this);
[email protected]33acd5b2014-08-19 19:56:22312 fetcher_->SetRequestContext(getter);
313 fetcher_->Start();
314 if (!url_fetch_complete_) {
315 waiting_ = true;
316 content::RunMessageLoop();
317 waiting_ = false;
318 }
319 CHECK(url_fetch_complete_);
320 }
321
322 void FetchUrl(GURL url) {
dcheng758c95c2014-08-26 22:07:37323 FetchUrlDetailed(url, url_request_context_getter_.get());
[email protected]33acd5b2014-08-19 19:56:22324 }
325
326 const net::URLRequestStatus& FetcherStatus() const {
327 return fetcher_->GetStatus();
328 }
329
330 int FetcherResponseCode() const {
331 return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
332 fetcher_->GetResponseCode() : 0);
333 }
334
335 const net::HttpResponseHeaders* FetcherResponseHeaders() const {
336 return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
337 fetcher_->GetResponseHeaders() : NULL);
338 }
339
340 std::string FetcherResponseContents() const {
341 std::string contents;
342 if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS)
343 CHECK(fetcher_->GetResponseAsString(&contents));
344 return contents;
345 }
346
347 // Get the data from the server. Return value is success/failure of the
348 // data operation, |*sdch_encoding_used| indicates whether or not the
349 // data was retrieved with sdch encoding.
350 // This is done through FetchUrl(), so the various helper functions
351 // will have valid status if it returns successfully.
352 bool GetDataDetailed(net::URLRequestContextGetter* getter,
353 bool* sdch_encoding_used) {
354 FetchUrlDetailed(
355 GURL(base::StringPrintf(
pkastingcba13292014-11-20 03:35:21356 "http://%s:%u%s", kTestHost, test_server_port(), kDataURLPath)),
[email protected]33acd5b2014-08-19 19:56:22357 getter);
358 EXPECT_EQ(net::URLRequestStatus::SUCCESS, FetcherStatus().status())
359 << "Error code is " << FetcherStatus().error();
360 EXPECT_EQ(200, FetcherResponseCode());
361 EXPECT_EQ(kSampleData, FetcherResponseContents());
362
363 if (net::URLRequestStatus::SUCCESS != FetcherStatus().status() ||
364 200 != FetcherResponseCode()) {
365 *sdch_encoding_used = false;
366 return false;
367 }
368
369 *sdch_encoding_used =
370 FetcherResponseHeaders()->HasHeaderValue("Content-Encoding", "sdch");
371
372 if (FetcherResponseContents() != kSampleData)
373 return false;
374
375 return true;
376 }
377
378 bool GetData(bool* sdch_encoding_used) {
dcheng758c95c2014-08-26 22:07:37379 return GetDataDetailed(url_request_context_getter_.get(),
380 sdch_encoding_used);
[email protected]33acd5b2014-08-19 19:56:22381 }
382
383 // Client information and control.
384
385 int GetNumberOfDictionaryFetches(Profile* profile) {
386 int fetches = -1;
387 base::RunLoop run_loop;
388 content::BrowserThread::PostTaskAndReply(
rdsmith47c133b2014-11-06 19:02:30389 content::BrowserThread::IO,
390 FROM_HERE,
[email protected]33acd5b2014-08-19 19:56:22391 base::Bind(&SdchBrowserTest::GetNumberOfDictionaryFetchesOnIOThread,
rdsmith47c133b2014-11-06 19:02:30392 base::Unretained(this),
[email protected]33acd5b2014-08-19 19:56:22393 base::Unretained(profile->GetRequestContext()),
394 &fetches),
395 run_loop.QuitClosure());
396 run_loop.Run();
397 DCHECK_NE(-1, fetches);
398 return fetches;
399 }
400
401 void BrowsingDataRemoveAndWait(int remove_mask) {
402 BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod(
403 browser()->profile(), BrowsingDataRemover::LAST_HOUR);
404 BrowsingDataRemoverCompletionObserver completion_observer(remover);
405 remover->Remove(remove_mask, BrowsingDataHelper::UNPROTECTED_WEB);
406 completion_observer.BlockUntilCompletion();
407 }
408
409 // Something of a cheat; nuke the dictionaries off the SdchManager without
410 // touching the cache (which browsing data remover would do).
411 void NukeSdchDictionaries() {
412 base::RunLoop run_loop;
413 content::BrowserThread::PostTaskAndReply(
414 content::BrowserThread::IO, FROM_HERE,
415 base::Bind(&SdchBrowserTest::NukeSdchDictionariesOnIOThread,
416 url_request_context_getter_),
417 run_loop.QuitClosure());
418 run_loop.Run();
419 }
420
421 // Create a second browser based on a second profile to work within
422 // multi-profile.
423 bool SetupSecondBrowser() {
424 base::FilePath user_data_dir;
425 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
426
427 if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir))
428 return false;
429
430 second_profile_ = g_browser_process->profile_manager()->GetProfile(
431 second_profile_data_dir_.path());
432 if (!second_profile_) return false;
433
434 second_browser_ = new Browser(Browser::CreateParams(
435 second_profile_, browser()->host_desktop_type()));
436 if (!second_browser_) return false;
437
438 chrome::AddSelectedTabWithURL(second_browser_,
439 GURL(url::kAboutBlankURL),
Sylvain Defresnec6ccc77d2014-09-19 10:19:35440 ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
[email protected]33acd5b2014-08-19 19:56:22441 content::WaitForLoadStop(
442 second_browser_->tab_strip_model()->GetActiveWebContents());
443 second_browser_->window()->Show();
444
rdsmith47c133b2014-11-06 19:02:30445 content::BrowserThread::PostTask(
446 content::BrowserThread::IO,
447 FROM_HERE,
448 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
449 base::Unretained(this),
450 make_scoped_refptr(
451 second_browser_->profile()->GetRequestContext())));
452
453 return true;
454 }
455
456 bool SetupIncognitoBrowser() {
457 incognito_browser_ = CreateIncognitoBrowser();
458
459 if (!incognito_browser_)
460 return false;
461
462 content::BrowserThread::PostTask(
463 content::BrowserThread::IO,
464 FROM_HERE,
465 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
466 base::Unretained(this),
467 make_scoped_refptr(
468 incognito_browser_->profile()->GetRequestContext())));
469
[email protected]33acd5b2014-08-19 19:56:22470 return true;
471 }
472
473 Browser* second_browser() { return second_browser_; }
rdsmith47c133b2014-11-06 19:02:30474 Browser* incognito_browser() { return incognito_browser_; }
[email protected]33acd5b2014-08-19 19:56:22475
476 // Server information and control.
477
478 void WaitAndGetTestVector(int num_requests, RequestVector* result) {
479 base::RunLoop run_loop;
480 content::BrowserThread::PostTask(
481 content::BrowserThread::IO, FROM_HERE,
482 base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
483 base::Unretained(&response_handler_),
484 num_requests,
485 run_loop.QuitClosure(),
486 result));
487 run_loop.Run();
488 }
489
pkastingcba13292014-11-20 03:35:21490 uint16 test_server_port() { return test_server_.port(); }
[email protected]33acd5b2014-08-19 19:56:22491
492 void SetSdchCacheability(bool cache_sdch_response) {
493 base::RunLoop run_loop;
494 content::BrowserThread::PostTaskAndReply(
495 content::BrowserThread::IO, FROM_HERE,
496 base::Bind(&SdchResponseHandler::set_cache_sdch_response,
497 base::Unretained(&response_handler_),
498 cache_sdch_response),
499 run_loop.QuitClosure());
500 run_loop.Run();
501 }
502
503 // Helper function for common test pattern.
504 //
505 // This function gets the data, confirms that the initial sending of the
506 // data included a dictionary advertisement, that that advertisement
507 // resulted in queueing a dictionary fetch, forces that fetch to
508 // go through, and confirms that a follow-on data load uses SDCH
509 // encoding. Returns true if the entire sequence of events occurred.
510 bool ForceSdchDictionaryLoad(Browser* browser) {
511 bool sdch_encoding_used = true;
512 bool data_gotten = GetDataDetailed(
513 browser->profile()->GetRequestContext(), &sdch_encoding_used);
514 EXPECT_TRUE(data_gotten);
515 if (!data_gotten) return false;
516 EXPECT_FALSE(sdch_encoding_used);
517
518 // Confirm that we were told to get the dictionary
519 const net::HttpResponseHeaders* headers = FetcherResponseHeaders();
520 std::string value;
521 bool have_dict_header =
522 headers->EnumerateHeader(NULL, "Get-Dictionary", &value);
523 EXPECT_TRUE(have_dict_header);
524 if (!have_dict_header) return false;
525
526 // If the above didn't result in a dictionary fetch being queued, the
527 // rest of the test will time out. Avoid that.
528 int num_fetches = GetNumberOfDictionaryFetches(browser->profile());
529 EXPECT_EQ(1, num_fetches);
530 if (1 != num_fetches) return false;
531
532 // Wait until the dictionary fetch actually happens.
533 RequestVector request_vector;
534 WaitAndGetTestVector(2, &request_vector);
535 EXPECT_EQ(request_vector[1].relative_url, kDictionaryURLPath);
536 if (request_vector[1].relative_url != kDictionaryURLPath) return false;
537
538 // Do a round trip to the server ignoring the encoding, presuming
539 // that if we've gotten data to this thread, the dictionary's made
540 // it into the SdchManager.
541 data_gotten = GetDataDetailed(
542 browser->profile()->GetRequestContext(), &sdch_encoding_used);
543 EXPECT_TRUE(data_gotten);
544 if (!data_gotten) return false;
545
546 // Now data fetches should be SDCH encoded.
547 sdch_encoding_used = false;
548 data_gotten = GetDataDetailed(
549 browser->profile()->GetRequestContext(), &sdch_encoding_used);
550 EXPECT_TRUE(data_gotten);
551 EXPECT_TRUE(sdch_encoding_used);
552
553 if (!data_gotten || !sdch_encoding_used) return false;
554
555 // Confirm the request vector looks at this point as expected.
556 WaitAndGetTestVector(4, &request_vector);
557 EXPECT_EQ(4u, request_vector.size());
558 EXPECT_EQ(request_vector[2].relative_url, kDataURLPath);
559 EXPECT_EQ(request_vector[3].relative_url, kDataURLPath);
560 return (4u == request_vector.size() &&
561 request_vector[2].relative_url == kDataURLPath &&
562 request_vector[3].relative_url == kDataURLPath);
563 }
564
565 private:
566 static void NukeSdchDictionariesOnIOThread(
567 net::URLRequestContextGetter* context_getter) {
anujk.sharma2e02ce162015-04-29 23:10:02568 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
[email protected]33acd5b2014-08-19 19:56:22569 net::SdchManager* sdch_manager =
570 context_getter->GetURLRequestContext()->sdch_manager();
571 DCHECK(sdch_manager);
572 sdch_manager->ClearData();
573 }
574
rdsmith47c133b2014-11-06 19:02:30575 void GetNumberOfDictionaryFetchesOnIOThread(
576 net::URLRequestContextGetter* context_getter,
[email protected]33acd5b2014-08-19 19:56:22577 int* result) {
anujk.sharma2e02ce162015-04-29 23:10:02578 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
rdsmith47c133b2014-11-06 19:02:30579
580 net::SdchManager* manager(
581 context_getter->GetURLRequestContext()->sdch_manager());
ellyjones887d31b2015-05-14 20:28:55582 DCHECK(observers_.end() != observers_.find(manager));
rdsmith47c133b2014-11-06 19:02:30583
ellyjones887d31b2015-05-14 20:28:55584 *result = observers_[manager].fetch_count();
[email protected]33acd5b2014-08-19 19:56:22585 }
586
587 // InProcessBrowserTest
dchengfce29ad2014-10-23 03:47:47588 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]33acd5b2014-08-19 19:56:22589 command_line->AppendSwitchASCII(
590 switches::kHostResolverRules,
591 "MAP " + std::string(kTestHost) + " 127.0.0.1");
592#if defined(OS_CHROMEOS)
593 command_line->AppendSwitch(
594 chromeos::switches::kIgnoreUserProfileMappingForTests);
595#endif
596 }
597
dchengfce29ad2014-10-23 03:47:47598 void SetUpOnMainThread() override {
[email protected]33acd5b2014-08-19 19:56:22599 test_server_.RegisterRequestHandler(
600 base::Bind(&SdchResponseHandler::HandleRequest,
601 base::Unretained(&response_handler_)));
svaldeze2745872015-11-04 23:30:20602 CHECK(test_server_.Start());
[email protected]33acd5b2014-08-19 19:56:22603 url_request_context_getter_ = browser()->profile()->GetRequestContext();
rdsmith47c133b2014-11-06 19:02:30604
605 content::BrowserThread::PostTask(
606 content::BrowserThread::IO,
607 FROM_HERE,
608 base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
609 base::Unretained(this),
610 url_request_context_getter_));
[email protected]33acd5b2014-08-19 19:56:22611 }
612
dchengfce29ad2014-10-23 03:47:47613 void TearDownOnMainThread() override {
[email protected]33acd5b2014-08-19 19:56:22614 CHECK(test_server_.ShutdownAndWaitUntilComplete());
rdsmith47c133b2014-11-06 19:02:30615
616 content::BrowserThread::PostTask(
617 content::BrowserThread::IO,
618 FROM_HERE,
619 base::Bind(&SdchBrowserTest::UnsubscribeFromAllSdchNotifications,
620 base::Unretained(this)));
[email protected]33acd5b2014-08-19 19:56:22621 }
622
rdsmith47c133b2014-11-06 19:02:30623 void SubscribeToSdchNotifications(
624 net::URLRequestContextGetter* context_getter) {
anujk.sharma2e02ce162015-04-29 23:10:02625 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
rdsmith47c133b2014-11-06 19:02:30626
627 net::SdchManager* manager =
628 context_getter->GetURLRequestContext()->sdch_manager();
ellyjones887d31b2015-05-14 20:28:55629 DCHECK(observers_.end() == observers_.find(manager));
rdsmith47c133b2014-11-06 19:02:30630
ellyjones887d31b2015-05-14 20:28:55631 observers_[manager].Observe(manager);
rdsmith47c133b2014-11-06 19:02:30632 }
633
634 void UnsubscribeFromAllSdchNotifications() {
anujk.sharma2e02ce162015-04-29 23:10:02635 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
ellyjones887d31b2015-05-14 20:28:55636 observers_.clear();
rdsmith47c133b2014-11-06 19:02:30637 }
638
[email protected]33acd5b2014-08-19 19:56:22639 // URLFetcherDelegate
dchengfce29ad2014-10-23 03:47:47640 void OnURLFetchComplete(const net::URLFetcher* source) override {
[email protected]33acd5b2014-08-19 19:56:22641 url_fetch_complete_ = true;
642 if (waiting_)
ki.stfuc4f8e242015-10-09 20:40:20643 base::MessageLoopForUI::current()->QuitWhenIdle();
[email protected]33acd5b2014-08-19 19:56:22644 }
645
646 SdchResponseHandler response_handler_;
svaldeze2745872015-11-04 23:30:20647 net::EmbeddedTestServer test_server_;
[email protected]33acd5b2014-08-19 19:56:22648 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
649 scoped_ptr<net::URLFetcher> fetcher_;
650 bool url_fetch_complete_;
651 bool waiting_;
652 base::ScopedTempDir second_profile_data_dir_;
653 Profile* second_profile_;
654 Browser* second_browser_;
rdsmith47c133b2014-11-06 19:02:30655 Browser* incognito_browser_;
656
657 // IO Thread access only.
ellyjones887d31b2015-05-14 20:28:55658 std::map<net::SdchManager*, TestSdchObserver> observers_;
[email protected]33acd5b2014-08-19 19:56:22659};
660
661const char SdchBrowserTest::kTestHost[] = "our.test.host.com";
662
663// Confirm that after getting a dictionary, calling the browsing
664// data remover renders it unusable. Also (in calling
665// ForceSdchDictionaryLoad()) servers as a smoke test for SDCH.
666IN_PROC_BROWSER_TEST_F(SdchBrowserTest, BrowsingDataRemover) {
667 ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
668
669 // Confirm browsing data remover without removing the cache leaves
670 // SDCH alone.
671 BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_ALL &
672 ~BrowsingDataRemover::REMOVE_CACHE);
673 bool sdch_encoding_used = false;
674 ASSERT_TRUE(GetData(&sdch_encoding_used));
675 EXPECT_TRUE(sdch_encoding_used);
676
677 // Confirm browsing data remover removing the cache clears SDCH state.
678 BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_CACHE);
679 sdch_encoding_used = false;
680 ASSERT_TRUE(GetData(&sdch_encoding_used));
681 EXPECT_FALSE(sdch_encoding_used);
682}
683
684// Confirm dictionaries not visible in other profiles.
685IN_PROC_BROWSER_TEST_F(SdchBrowserTest, Isolation) {
686 ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
687 ASSERT_TRUE(SetupSecondBrowser());
rdsmith47c133b2014-11-06 19:02:30688 ASSERT_TRUE(SetupIncognitoBrowser());
[email protected]33acd5b2014-08-19 19:56:22689
690 // Data fetches from incognito or separate profiles should not be SDCH
691 // encoded.
692 bool sdch_encoding_used = true;
rdsmith47c133b2014-11-06 19:02:30693 EXPECT_TRUE(
694 GetDataDetailed(incognito_browser()->profile()->GetRequestContext(),
695 &sdch_encoding_used));
[email protected]33acd5b2014-08-19 19:56:22696 EXPECT_FALSE(sdch_encoding_used);
697
698 sdch_encoding_used = true;
699 EXPECT_TRUE(GetDataDetailed(
700 second_browser()->profile()->GetRequestContext(), &sdch_encoding_used));
701 EXPECT_FALSE(sdch_encoding_used);
702}
703
704// Confirm a dictionary loaded in incognito isn't visible in the main profile.
705IN_PROC_BROWSER_TEST_F(SdchBrowserTest, ReverseIsolation) {
rdsmith47c133b2014-11-06 19:02:30706 ASSERT_TRUE(SetupIncognitoBrowser());
707 ASSERT_TRUE(ForceSdchDictionaryLoad(incognito_browser()));
[email protected]33acd5b2014-08-19 19:56:22708
709 // Data fetches on main browser should not be SDCH encoded.
710 bool sdch_encoding_used = true;
711 ASSERT_TRUE(GetData(&sdch_encoding_used));
712 EXPECT_FALSE(sdch_encoding_used);
713}
714
715} // namespace