Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / net / sdch_browsertest.cc
1 // 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"
37 #include "net/base/sdch_observer.h"
38 #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
53 namespace {
54
55 typedef std::vector<net::test_server::HttpRequest> RequestVector;
56 typedef std::map<std::string, std::string> HttpRequestHeaderMap;
57
58 // Credit Alfred, Lord Tennyson
59 static 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.
91 static 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
99 static const char kDictionaryURLPath[] = "/dict";
100 static 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.
106 bool 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) {
111     if (!base::strcasecmp(it->first.c_str(), header)) {
112       *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.
121 void 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.
131 class SdchResponseHandler {
132  public:
133   // Do initial preparation so that SDCH requests can be handled.
134   explicit SdchResponseHandler(std::string domain)
135       : cache_sdch_response_(false),
136         weak_ptr_factory_(this) {
137     // Dictionary
138     sdch_dictionary_contents_ = "Domain: ";
139     sdch_dictionary_contents_ += domain;
140     sdch_dictionary_contents_ += "\n\n";
141     sdch_dictionary_contents_ += kDictionaryContents;
142
143     // Dictionary hash for client and server.
144     char binary_hash[32];
145     crypto::SHA256HashString(sdch_dictionary_contents_, binary_hash,
146                              sizeof(binary_hash));
147     SafeBase64Encode(std::string(&binary_hash[0], 6), &dictionary_client_hash_);
148     SafeBase64Encode(std::string(&binary_hash[6], 6), &dictionary_server_hash_);
149
150     // Encoded response.
151     open_vcdiff::HashedDictionary vcdiff_dictionary(
152         kDictionaryContents, strlen(kDictionaryContents));
153     bool result = vcdiff_dictionary.Init();
154     DCHECK(result);
155     open_vcdiff::VCDiffStreamingEncoder encoder(&vcdiff_dictionary, 0, false);
156     encoded_data_ = dictionary_server_hash_;
157     encoded_data_ += '\0';
158     result = encoder.StartEncoding(&encoded_data_);
159     DCHECK(result);
160     result = encoder.EncodeChunk(
161         kSampleData, strlen(kSampleData), &encoded_data_);
162     DCHECK(result);
163     result = encoder.FinishEncoding(&encoded_data_);
164     DCHECK(result);
165   }
166
167   static bool ClientIsAdvertisingSdchEncoding(const HttpRequestHeaderMap& map) {
168     std::string value;
169     if (!GetRequestHeader(map, "accept-encoding", &value))
170       return false;
171     base::StringTokenizer tokenizer(value, " ,");
172     while (tokenizer.GetNext()) {
173       if (base::strcasecmp(tokenizer.token().c_str(), "sdch"))
174         return true;
175     }
176     return false;
177   }
178
179   bool ShouldRespondWithSdchEncoding(const HttpRequestHeaderMap& map) {
180     std::string value;
181     if (!GetRequestHeader(map, "avail-dictionary", &value))
182       return false;
183     return value == dictionary_client_hash_;
184   }
185
186   scoped_ptr<net::test_server::HttpResponse> HandleRequest(
187       const net::test_server::HttpRequest& request) {
188     request_vector_.push_back(request);
189
190     scoped_ptr<net::test_server::BasicHttpResponse> response(
191         new net::test_server::BasicHttpResponse);
192     if (request.relative_url == kDataURLPath) {
193       if (ShouldRespondWithSdchEncoding(request.headers)) {
194         // Note that chrome doesn't advertise accepting SDCH encoding
195         // for POSTs (because the meta-refresh hack would break a POST),
196         // but that's not for the server to enforce.
197         DCHECK_NE(encoded_data_, "");
198         response->set_content_type("text/html");
199         response->set_content(encoded_data_);
200         response->AddCustomHeader("Content-Encoding", "sdch");
201         // We allow tests to set caching on the sdch response,
202         // so that we can force an encoded response with no
203         // dictionary.
204         if (cache_sdch_response_)
205           response->AddCustomHeader("Cache-Control", "max-age=3600");
206         else
207           response->AddCustomHeader("Cache-Control", "no-store");
208       } else {
209         response->set_content_type("text/plain");
210         response->set_content(kSampleData);
211         if (ClientIsAdvertisingSdchEncoding(request.headers))
212           response->AddCustomHeader("Get-Dictionary", kDictionaryURLPath);
213         // We never cache the plain data response, to make it
214         // easy to refresh after we get the dictionary.
215         response->AddCustomHeader("Cache-Control", "no-store");
216       }
217     } else {
218       DCHECK_EQ(request.relative_url, kDictionaryURLPath);
219       DCHECK_NE(sdch_dictionary_contents_, "");
220       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     }
229     return response.Pass();
230   }
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
263 class SdchBrowserTest : public InProcessBrowserTest,
264                         public net::URLFetcherDelegate,
265                         public net::SdchObserver {
266  public:
267   static const char kTestHost[];
268
269   SdchBrowserTest()
270       : response_handler_(kTestHost),
271         url_request_context_getter_(NULL),
272         url_fetch_complete_(false),
273         waiting_(false) {}
274
275   // Helper functions for fetching data.
276
277   void FetchUrlDetailed(GURL url, net::URLRequestContextGetter* getter) {
278     url_fetch_complete_ = false;
279     fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
280     fetcher_->SetRequestContext(getter);
281     fetcher_->Start();
282     if (!url_fetch_complete_) {
283       waiting_ = true;
284       content::RunMessageLoop();
285       waiting_ = false;
286     }
287     CHECK(url_fetch_complete_);
288   }
289
290   void FetchUrl(GURL url) {
291     FetchUrlDetailed(url, url_request_context_getter_.get());
292   }
293
294   const net::URLRequestStatus& FetcherStatus() const {
295     return fetcher_->GetStatus();
296   }
297
298   int FetcherResponseCode() const {
299     return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
300             fetcher_->GetResponseCode() : 0);
301   }
302
303   const net::HttpResponseHeaders* FetcherResponseHeaders() const {
304     return (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS ?
305             fetcher_->GetResponseHeaders() : NULL);
306   }
307
308   std::string FetcherResponseContents() const {
309     std::string contents;
310     if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS)
311       CHECK(fetcher_->GetResponseAsString(&contents));
312     return contents;
313   }
314
315   // Get the data from the server.  Return value is success/failure of the
316   // data operation, |*sdch_encoding_used| indicates whether or not the
317   // data was retrieved with sdch encoding.
318   // This is done through FetchUrl(), so the various helper functions
319   // will have valid status if it returns successfully.
320   bool GetDataDetailed(net::URLRequestContextGetter* getter,
321                        bool* sdch_encoding_used) {
322     FetchUrlDetailed(
323         GURL(base::StringPrintf(
324             "http://%s:%d%s", kTestHost, test_server_port(), kDataURLPath)),
325         getter);
326     EXPECT_EQ(net::URLRequestStatus::SUCCESS, FetcherStatus().status())
327         << "Error code is " << FetcherStatus().error();
328     EXPECT_EQ(200, FetcherResponseCode());
329     EXPECT_EQ(kSampleData, FetcherResponseContents());
330
331     if (net::URLRequestStatus::SUCCESS != FetcherStatus().status() ||
332         200 != FetcherResponseCode()) {
333       *sdch_encoding_used = false;
334       return false;
335     }
336
337     *sdch_encoding_used =
338         FetcherResponseHeaders()->HasHeaderValue("Content-Encoding", "sdch");
339
340     if (FetcherResponseContents() != kSampleData)
341       return false;
342
343     return true;
344   }
345
346   bool GetData(bool* sdch_encoding_used) {
347     return GetDataDetailed(url_request_context_getter_.get(),
348                            sdch_encoding_used);
349   }
350
351   // Client information and control.
352
353   int GetNumberOfDictionaryFetches(Profile* profile) {
354     int fetches = -1;
355     base::RunLoop run_loop;
356     content::BrowserThread::PostTaskAndReply(
357         content::BrowserThread::IO,
358         FROM_HERE,
359         base::Bind(&SdchBrowserTest::GetNumberOfDictionaryFetchesOnIOThread,
360                    base::Unretained(this),
361                    base::Unretained(profile->GetRequestContext()),
362                    &fetches),
363         run_loop.QuitClosure());
364     run_loop.Run();
365     DCHECK_NE(-1, fetches);
366     return fetches;
367   }
368
369   void BrowsingDataRemoveAndWait(int remove_mask) {
370     BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod(
371         browser()->profile(), BrowsingDataRemover::LAST_HOUR);
372     BrowsingDataRemoverCompletionObserver completion_observer(remover);
373     remover->Remove(remove_mask, BrowsingDataHelper::UNPROTECTED_WEB);
374     completion_observer.BlockUntilCompletion();
375   }
376
377   // Something of a cheat; nuke the dictionaries off the SdchManager without
378   // touching the cache (which browsing data remover would do).
379   void NukeSdchDictionaries() {
380     base::RunLoop run_loop;
381     content::BrowserThread::PostTaskAndReply(
382         content::BrowserThread::IO, FROM_HERE,
383         base::Bind(&SdchBrowserTest::NukeSdchDictionariesOnIOThread,
384                    url_request_context_getter_),
385         run_loop.QuitClosure());
386     run_loop.Run();
387   }
388
389   // Create a second browser based on a second profile to work within
390   // multi-profile.
391   bool SetupSecondBrowser() {
392     base::FilePath user_data_dir;
393     PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
394
395     if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir))
396       return false;
397
398     second_profile_ = g_browser_process->profile_manager()->GetProfile(
399         second_profile_data_dir_.path());
400     if (!second_profile_) return false;
401
402     second_browser_ = new Browser(Browser::CreateParams(
403         second_profile_, browser()->host_desktop_type()));
404     if (!second_browser_) return false;
405
406     chrome::AddSelectedTabWithURL(second_browser_,
407                                   GURL(url::kAboutBlankURL),
408                                   ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
409     content::WaitForLoadStop(
410         second_browser_->tab_strip_model()->GetActiveWebContents());
411     second_browser_->window()->Show();
412
413     content::BrowserThread::PostTask(
414         content::BrowserThread::IO,
415         FROM_HERE,
416         base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
417                    base::Unretained(this),
418                    make_scoped_refptr(
419                        second_browser_->profile()->GetRequestContext())));
420
421     return true;
422   }
423
424   bool SetupIncognitoBrowser() {
425     incognito_browser_ = CreateIncognitoBrowser();
426
427     if (!incognito_browser_)
428       return false;
429
430     content::BrowserThread::PostTask(
431         content::BrowserThread::IO,
432         FROM_HERE,
433         base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
434                    base::Unretained(this),
435                    make_scoped_refptr(
436                        incognito_browser_->profile()->GetRequestContext())));
437
438     return true;
439   }
440
441   Browser* second_browser() { return second_browser_; }
442   Browser* incognito_browser() { return incognito_browser_; }
443
444   // Server information and control.
445
446   void WaitAndGetTestVector(int num_requests, RequestVector* result) {
447     base::RunLoop run_loop;
448     content::BrowserThread::PostTask(
449         content::BrowserThread::IO, FROM_HERE,
450         base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
451                    base::Unretained(&response_handler_),
452                    num_requests,
453                    run_loop.QuitClosure(),
454                    result));
455     run_loop.Run();
456   }
457
458   int test_server_port() { return test_server_.port(); }
459
460   void SetSdchCacheability(bool cache_sdch_response) {
461     base::RunLoop run_loop;
462     content::BrowserThread::PostTaskAndReply(
463         content::BrowserThread::IO, FROM_HERE,
464         base::Bind(&SdchResponseHandler::set_cache_sdch_response,
465                    base::Unretained(&response_handler_),
466                    cache_sdch_response),
467         run_loop.QuitClosure());
468     run_loop.Run();
469   }
470
471   // Helper function for common test pattern.
472   //
473   // This function gets the data, confirms that the initial sending of the
474   // data included a dictionary advertisement, that that advertisement
475   // resulted in queueing a dictionary fetch, forces that fetch to
476   // go through, and confirms that a follow-on data load uses SDCH
477   // encoding.  Returns true if the entire sequence of events occurred.
478   bool ForceSdchDictionaryLoad(Browser* browser) {
479     bool sdch_encoding_used = true;
480     bool data_gotten = GetDataDetailed(
481         browser->profile()->GetRequestContext(), &sdch_encoding_used);
482     EXPECT_TRUE(data_gotten);
483     if (!data_gotten) return false;
484     EXPECT_FALSE(sdch_encoding_used);
485
486     // Confirm that we were told to get the dictionary
487     const net::HttpResponseHeaders* headers = FetcherResponseHeaders();
488     std::string value;
489     bool have_dict_header =
490         headers->EnumerateHeader(NULL, "Get-Dictionary", &value);
491     EXPECT_TRUE(have_dict_header);
492     if (!have_dict_header) return false;
493
494     // If the above didn't result in a dictionary fetch being queued, the
495     // rest of the test will time out.  Avoid that.
496     int num_fetches = GetNumberOfDictionaryFetches(browser->profile());
497     EXPECT_EQ(1, num_fetches);
498     if (1 != num_fetches) return false;
499
500     // Wait until the dictionary fetch actually happens.
501     RequestVector request_vector;
502     WaitAndGetTestVector(2, &request_vector);
503     EXPECT_EQ(request_vector[1].relative_url, kDictionaryURLPath);
504     if (request_vector[1].relative_url != kDictionaryURLPath) return false;
505
506     // Do a round trip to the server ignoring the encoding, presuming
507     // that if we've gotten data to this thread, the dictionary's made
508     // it into the SdchManager.
509     data_gotten = GetDataDetailed(
510         browser->profile()->GetRequestContext(), &sdch_encoding_used);
511     EXPECT_TRUE(data_gotten);
512     if (!data_gotten) return false;
513
514     // Now data fetches should be SDCH encoded.
515     sdch_encoding_used = false;
516     data_gotten = GetDataDetailed(
517         browser->profile()->GetRequestContext(), &sdch_encoding_used);
518     EXPECT_TRUE(data_gotten);
519     EXPECT_TRUE(sdch_encoding_used);
520
521     if (!data_gotten || !sdch_encoding_used) return false;
522
523     // Confirm the request vector looks at this point as expected.
524     WaitAndGetTestVector(4, &request_vector);
525     EXPECT_EQ(4u, request_vector.size());
526     EXPECT_EQ(request_vector[2].relative_url, kDataURLPath);
527     EXPECT_EQ(request_vector[3].relative_url, kDataURLPath);
528     return (4u == request_vector.size() &&
529             request_vector[2].relative_url == kDataURLPath &&
530             request_vector[3].relative_url == kDataURLPath);
531   }
532
533  private:
534   static void NukeSdchDictionariesOnIOThread(
535       net::URLRequestContextGetter* context_getter) {
536     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
537     net::SdchManager* sdch_manager =
538         context_getter->GetURLRequestContext()->sdch_manager();
539     DCHECK(sdch_manager);
540     sdch_manager->ClearData();
541   }
542
543   void GetNumberOfDictionaryFetchesOnIOThread(
544       net::URLRequestContextGetter* context_getter,
545       int* result) {
546     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
547
548     net::SdchManager* manager(
549         context_getter->GetURLRequestContext()->sdch_manager());
550     DCHECK(fetch_counts_.end() != fetch_counts_.find(manager));
551
552     *result = fetch_counts_[manager];
553   }
554
555   // InProcessBrowserTest
556   void SetUpCommandLine(base::CommandLine* command_line) override {
557     command_line->AppendSwitchASCII(
558         switches::kHostResolverRules,
559         "MAP " + std::string(kTestHost) + " 127.0.0.1");
560 #if defined(OS_CHROMEOS)
561     command_line->AppendSwitch(
562         chromeos::switches::kIgnoreUserProfileMappingForTests);
563 #endif
564   }
565
566   void SetUpOnMainThread() override {
567     test_server_.RegisterRequestHandler(
568         base::Bind(&SdchResponseHandler::HandleRequest,
569                    base::Unretained(&response_handler_)));
570     CHECK(test_server_.InitializeAndWaitUntilReady());
571     url_request_context_getter_ = browser()->profile()->GetRequestContext();
572
573     content::BrowserThread::PostTask(
574         content::BrowserThread::IO,
575         FROM_HERE,
576         base::Bind(&SdchBrowserTest::SubscribeToSdchNotifications,
577                    base::Unretained(this),
578                    url_request_context_getter_));
579   }
580
581   void TearDownOnMainThread() override {
582     CHECK(test_server_.ShutdownAndWaitUntilComplete());
583
584     content::BrowserThread::PostTask(
585         content::BrowserThread::IO,
586         FROM_HERE,
587         base::Bind(&SdchBrowserTest::UnsubscribeFromAllSdchNotifications,
588                    base::Unretained(this)));
589   }
590
591   void SubscribeToSdchNotifications(
592       net::URLRequestContextGetter* context_getter) {
593     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
594
595     net::SdchManager* manager =
596         context_getter->GetURLRequestContext()->sdch_manager();
597     DCHECK(fetch_counts_.end() == fetch_counts_.find(manager));
598
599     fetch_counts_[manager] = 0;
600     manager->AddObserver(this);
601   }
602
603   void UnsubscribeFromAllSdchNotifications() {
604     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
605
606     for (auto it = fetch_counts_.begin(); it != fetch_counts_.end(); ++it)
607       it->first->RemoveObserver(this);
608
609     fetch_counts_.clear();
610   }
611
612   // SdchObserver
613   void OnGetDictionary(net::SdchManager* manager,
614                        const GURL& request_url,
615                        const GURL& dictionary_url) override {
616     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
617     DLOG(ERROR) << "Retrieving count of notifications from manager " << manager;
618     DCHECK(fetch_counts_.end() != fetch_counts_.find(manager));
619     ++fetch_counts_[manager];
620   }
621   void OnClearDictionaries(net::SdchManager* manager) override {}
622
623   // URLFetcherDelegate
624   void OnURLFetchComplete(const net::URLFetcher* source) override {
625     url_fetch_complete_ = true;
626     if (waiting_)
627       base::MessageLoopForUI::current()->Quit();
628   }
629
630   SdchResponseHandler response_handler_;
631   net::test_server::EmbeddedTestServer test_server_;
632   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
633   scoped_ptr<net::URLFetcher> fetcher_;
634   bool url_fetch_complete_;
635   bool waiting_;
636   base::ScopedTempDir second_profile_data_dir_;
637   Profile* second_profile_;
638   Browser* second_browser_;
639   Browser* incognito_browser_;
640
641   // IO Thread access only.
642   std::map<net::SdchManager*, int> fetch_counts_;
643 };
644
645 const char SdchBrowserTest::kTestHost[] = "our.test.host.com";
646
647 // Confirm that after getting a dictionary, calling the browsing
648 // data remover renders it unusable.  Also (in calling
649 // ForceSdchDictionaryLoad()) servers as a smoke test for SDCH.
650 IN_PROC_BROWSER_TEST_F(SdchBrowserTest, BrowsingDataRemover) {
651   ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
652
653   // Confirm browsing data remover without removing the cache leaves
654   // SDCH alone.
655   BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_ALL &
656                             ~BrowsingDataRemover::REMOVE_CACHE);
657   bool sdch_encoding_used = false;
658   ASSERT_TRUE(GetData(&sdch_encoding_used));
659   EXPECT_TRUE(sdch_encoding_used);
660
661   // Confirm browsing data remover removing the cache clears SDCH state.
662   BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_CACHE);
663   sdch_encoding_used = false;
664   ASSERT_TRUE(GetData(&sdch_encoding_used));
665   EXPECT_FALSE(sdch_encoding_used);
666 }
667
668 // Confirm dictionaries not visible in other profiles.
669 IN_PROC_BROWSER_TEST_F(SdchBrowserTest, Isolation) {
670   ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
671   ASSERT_TRUE(SetupSecondBrowser());
672   ASSERT_TRUE(SetupIncognitoBrowser());
673
674   // Data fetches from incognito or separate profiles should not be SDCH
675   // encoded.
676   bool sdch_encoding_used = true;
677   EXPECT_TRUE(
678       GetDataDetailed(incognito_browser()->profile()->GetRequestContext(),
679                       &sdch_encoding_used));
680   EXPECT_FALSE(sdch_encoding_used);
681
682   sdch_encoding_used = true;
683   EXPECT_TRUE(GetDataDetailed(
684       second_browser()->profile()->GetRequestContext(), &sdch_encoding_used));
685   EXPECT_FALSE(sdch_encoding_used);
686 }
687
688 // Confirm a dictionary loaded in incognito isn't visible in the main profile.
689 IN_PROC_BROWSER_TEST_F(SdchBrowserTest, ReverseIsolation) {
690   ASSERT_TRUE(SetupIncognitoBrowser());
691   ASSERT_TRUE(ForceSdchDictionaryLoad(incognito_browser()));
692
693   // Data fetches on main browser should not be SDCH encoded.
694   bool sdch_encoding_used = true;
695   ASSERT_TRUE(GetData(&sdch_encoding_used));
696   EXPECT_FALSE(sdch_encoding_used);
697 }
698
699 }  // namespace