1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6
7#include "base/bind.h"
8#include "base/pickle.h"
9#include "base/run_loop.h"
10#include "base/time/time.h"
11#include "chrome/browser/history/history_backend.h"
12#include "chrome/browser/history/history_service.h"
13#include "chrome/browser/history/history_service_factory.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/safe_browsing/malware_details.h"
16#include "chrome/browser/safe_browsing/malware_details_history.h"
17#include "chrome/browser/safe_browsing/report.pb.h"
18#include "chrome/browser/safe_browsing/safe_browsing_service.h"
19#include "chrome/browser/safe_browsing/ui_manager.h"
20#include "chrome/common/render_messages.h"
21#include "chrome/common/safe_browsing/safebrowsing_messages.h"
22#include "chrome/test/base/chrome_render_view_host_test_harness.h"
23#include "chrome/test/base/testing_profile.h"
24#include "content/public/browser/render_process_host.h"
25#include "content/public/browser/web_contents.h"
26#include "net/base/io_buffer.h"
27#include "net/base/net_errors.h"
28#include "net/base/test_completion_callback.h"
29#include "net/disk_cache/disk_cache.h"
30#include "net/http/http_cache.h"
31#include "net/http/http_response_headers.h"
32#include "net/http/http_response_info.h"
33#include "net/http/http_util.h"
34#include "net/url_request/url_request_context.h"
35#include "net/url_request/url_request_context_getter.h"
36
37static const char* kOriginalLandingURL = "http://www.originallandingpage.com/";
38static const char* kHttpsURL = "https://www.url.com/";
39static const char* kDOMChildURL = "http://www.domparent.com/";
40static const char* kDOMParentURL = "http://www.domchild.com/";
41static const char* kFirstRedirectURL = "http://redirectone.com/";
42static const char* kSecondRedirectURL = "http://redirecttwo.com/";
43
44static const char* kMalwareURL = "http://www.malware.com/";
45static const char* kMalwareHeaders =
46    "HTTP/1.1 200 OK\n"
47    "Content-Type: image/jpeg\n";
48static const char* kMalwareData = "exploit();";
49
50static const char* kLandingURL = "http://www.landingpage.com/";
51static const char* kLandingHeaders =
52    "HTTP/1.1 200 OK\n"
53    "Content-Type: text/html\n"
54    "Content-Length: 1024\n"
55    "Set-Cookie: tastycookie\n";  // This header is stripped.
56static const char* kLandingData = "<iframe src='http://www.malware.com'>";
57
58using content::BrowserThread;
59using content::WebContents;
60using safe_browsing::ClientMalwareReportRequest;
61
62namespace {
63
64void WriteHeaders(disk_cache::Entry* entry, const std::string& headers) {
65  net::HttpResponseInfo responseinfo;
66  std::string raw_headers = net::HttpUtil::AssembleRawHeaders(
67      headers.c_str(), headers.size());
68  responseinfo.socket_address = net::HostPortPair("1.2.3.4", 80);
69  responseinfo.headers = new net::HttpResponseHeaders(raw_headers);
70
71  Pickle pickle;
72  responseinfo.Persist(&pickle, false, false);
73
74  scoped_refptr<net::WrappedIOBuffer> buf(new net::WrappedIOBuffer(
75      reinterpret_cast<const char*>(pickle.data())));
76  int len = static_cast<int>(pickle.size());
77
78  net::TestCompletionCallback cb;
79  int rv = entry->WriteData(0, 0, buf.get(), len, cb.callback(), true);
80  ASSERT_EQ(len, cb.GetResult(rv));
81}
82
83void WriteData(disk_cache::Entry* entry, const std::string& data) {
84  if (data.empty())
85    return;
86
87  int len = data.length();
88  scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(len));
89  memcpy(buf->data(), data.data(), data.length());
90
91  net::TestCompletionCallback cb;
92  int rv = entry->WriteData(1, 0, buf.get(), len, cb.callback(), true);
93  ASSERT_EQ(len, cb.GetResult(rv));
94}
95
96void WriteToEntry(disk_cache::Backend* cache, const std::string& key,
97                  const std::string& headers, const std::string& data) {
98  net::TestCompletionCallback cb;
99  disk_cache::Entry* entry;
100  int rv = cache->CreateEntry(key, &entry, cb.callback());
101  rv = cb.GetResult(rv);
102  if (rv != net::OK) {
103    rv = cache->OpenEntry(key, &entry, cb.callback());
104    ASSERT_EQ(net::OK, cb.GetResult(rv));
105  }
106
107  WriteHeaders(entry, headers);
108  WriteData(entry, data);
109  entry->Close();
110}
111
112void FillCache(net::URLRequestContextGetter* context_getter) {
113  net::TestCompletionCallback cb;
114  disk_cache::Backend* cache;
115  int rv =
116      context_getter->GetURLRequestContext()->http_transaction_factory()->
117      GetCache()->GetBackend(&cache, cb.callback());
118  ASSERT_EQ(net::OK, cb.GetResult(rv));
119
120  WriteToEntry(cache, kMalwareURL, kMalwareHeaders, kMalwareData);
121  WriteToEntry(cache, kLandingURL, kLandingHeaders, kLandingData);
122}
123
124// Lets us provide a MockURLRequestContext with an HTTP Cache we pre-populate.
125// Also exposes the constructor.
126class MalwareDetailsWrap : public MalwareDetails {
127 public:
128  MalwareDetailsWrap(
129      SafeBrowsingUIManager* ui_manager,
130      WebContents* web_contents,
131      const SafeBrowsingUIManager::UnsafeResource& unsafe_resource,
132      net::URLRequestContextGetter* request_context_getter)
133      : MalwareDetails(ui_manager, web_contents, unsafe_resource) {
134
135    request_context_getter_ = request_context_getter;
136  }
137
138 private:
139  virtual ~MalwareDetailsWrap() {}
140};
141
142class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
143 public:
144  base::RunLoop* run_loop_;
145  // The safe browsing UI manager does not need a service for this test.
146  MockSafeBrowsingUIManager()
147      : SafeBrowsingUIManager(NULL), run_loop_(NULL) {}
148
149  // When the MalwareDetails is done, this is called.
150  virtual void SendSerializedMalwareDetails(
151      const std::string& serialized) OVERRIDE {
152    DVLOG(1) << "SendSerializedMalwareDetails";
153    run_loop_->Quit();
154    run_loop_ = NULL;
155    serialized_ = serialized;
156  }
157
158  // Used to synchronize SendSerializedMalwareDetails() with
159  // WaitForSerializedReport(). RunLoop::RunUntilIdle() is not sufficient
160  // because the MessageLoop task queue completely drains at some point
161  // between the send and the wait.
162  void SetRunLoopToQuit(base::RunLoop* run_loop) {
163    DCHECK(run_loop_ == NULL);
164    run_loop_ = run_loop;
165  }
166
167  const std::string& GetSerialized() {
168    return serialized_;
169  }
170
171 private:
172  virtual ~MockSafeBrowsingUIManager() {}
173
174  std::string serialized_;
175  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
176};
177
178}  // namespace.
179
180class MalwareDetailsTest : public ChromeRenderViewHostTestHarness {
181 public:
182  typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
183
184  MalwareDetailsTest()
185      : ui_manager_(new MockSafeBrowsingUIManager()) {
186  }
187
188  virtual void SetUp() OVERRIDE {
189    ChromeRenderViewHostTestHarness::SetUp();
190    ASSERT_TRUE(profile()->CreateHistoryService(
191        true /* delete_file */, false /* no_db */));
192  }
193
194  virtual void TearDown() OVERRIDE {
195    profile()->DestroyHistoryService();
196    ChromeRenderViewHostTestHarness::TearDown();
197  }
198
199  static bool ResourceLessThan(
200      const ClientMalwareReportRequest::Resource* lhs,
201      const ClientMalwareReportRequest::Resource* rhs) {
202    return lhs->id() < rhs->id();
203  }
204
205  std::string WaitForSerializedReport(MalwareDetails* report) {
206    BrowserThread::PostTask(
207        BrowserThread::IO,
208        FROM_HERE,
209        base::Bind(&MalwareDetails::FinishCollection, report));
210    // Wait for the callback (SendSerializedMalwareDetails).
211    DVLOG(1) << "Waiting for SendSerializedMalwareDetails";
212    base::RunLoop run_loop;
213    ui_manager_->SetRunLoopToQuit(&run_loop);
214    run_loop.Run();
215    return ui_manager_->GetSerialized();
216  }
217
218  HistoryService* history_service() {
219    return HistoryServiceFactory::GetForProfile(profile(),
220                                                Profile::EXPLICIT_ACCESS);
221  }
222
223 protected:
224  void InitResource(UnsafeResource* resource,
225                    bool is_subresource,
226                    const GURL& url) {
227    resource->url = url;
228    resource->is_subresource = is_subresource;
229    resource->threat_type = SB_THREAT_TYPE_URL_MALWARE;
230    resource->render_process_host_id =
231        web_contents()->GetRenderProcessHost()->GetID();
232    resource->render_view_id =
233        web_contents()->GetRenderViewHost()->GetRoutingID();
234  }
235
236  void VerifyResults(const ClientMalwareReportRequest& report_pb,
237                     const ClientMalwareReportRequest& expected_pb) {
238    EXPECT_EQ(expected_pb.malware_url(), report_pb.malware_url());
239    EXPECT_EQ(expected_pb.page_url(), report_pb.page_url());
240    EXPECT_EQ(expected_pb.referrer_url(), report_pb.referrer_url());
241
242    ASSERT_EQ(expected_pb.resources_size(), report_pb.resources_size());
243    // Sort the resources, to make the test deterministic
244    std::vector<const ClientMalwareReportRequest::Resource*> resources;
245    for (int i = 0; i < report_pb.resources_size(); ++i) {
246      const ClientMalwareReportRequest::Resource& resource =
247          report_pb.resources(i);
248      resources.push_back(&resource);
249    }
250    std::sort(resources.begin(), resources.end(),
251              &MalwareDetailsTest::ResourceLessThan);
252
253    std::vector<const ClientMalwareReportRequest::Resource*> expected;
254    for (int i = 0; i < report_pb.resources_size(); ++i) {
255      const ClientMalwareReportRequest::Resource& resource =
256          expected_pb.resources(i);
257      expected.push_back(&resource);
258    }
259    std::sort(expected.begin(), expected.end(),
260              &MalwareDetailsTest::ResourceLessThan);
261
262    for (uint32 i = 0; i < expected.size(); ++i) {
263      VerifyResource(resources[i], expected[i]);
264    }
265
266    EXPECT_EQ(expected_pb.complete(), report_pb.complete());
267  }
268
269  void VerifyResource(const ClientMalwareReportRequest::Resource* resource,
270                      const ClientMalwareReportRequest::Resource* expected) {
271    EXPECT_EQ(expected->id(), resource->id());
272    EXPECT_EQ(expected->url(), resource->url());
273    EXPECT_EQ(expected->parent_id(), resource->parent_id());
274    ASSERT_EQ(expected->child_ids_size(), resource->child_ids_size());
275    for (int i = 0; i < expected->child_ids_size(); i++) {
276      EXPECT_EQ(expected->child_ids(i), resource->child_ids(i));
277    }
278
279    // Verify HTTP Responses
280    if (expected->has_response()) {
281      ASSERT_TRUE(resource->has_response());
282      EXPECT_EQ(expected->response().firstline().code(),
283                resource->response().firstline().code());
284
285      ASSERT_EQ(expected->response().headers_size(),
286                resource->response().headers_size());
287      for (int i = 0; i < expected->response().headers_size(); ++i) {
288        EXPECT_EQ(expected->response().headers(i).name(),
289                  resource->response().headers(i).name());
290        EXPECT_EQ(expected->response().headers(i).value(),
291                  resource->response().headers(i).value());
292      }
293
294      EXPECT_EQ(expected->response().body(), resource->response().body());
295      EXPECT_EQ(expected->response().bodylength(),
296                resource->response().bodylength());
297      EXPECT_EQ(expected->response().bodydigest(),
298                resource->response().bodydigest());
299    }
300
301    // Verify IP:port pair
302    EXPECT_EQ(expected->response().remote_ip(),
303              resource->response().remote_ip());
304  }
305
306  // Adds a page to history.
307  // The redirects is the redirect url chain leading to the url.
308  void AddPageToHistory(const GURL& url,
309                        history::RedirectList* redirects) {
310    // The last item of the redirect chain has to be the final url when adding
311    // to history backend.
312    redirects->push_back(url);
313    history_service()->AddPage(
314        url, base::Time::Now(), reinterpret_cast<history::ContextID>(1), 0,
315        GURL(), *redirects, ui::PAGE_TRANSITION_TYPED,
316        history::SOURCE_BROWSED, false);
317  }
318
319  scoped_refptr<MockSafeBrowsingUIManager> ui_manager_;
320};
321
322// Tests creating a simple malware report.
323TEST_F(MalwareDetailsTest, MalwareSubResource) {
324  // Start a load.
325  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
326                       ui::PAGE_TRANSITION_TYPED, std::string());
327
328  UnsafeResource resource;
329  InitResource(&resource, true, GURL(kMalwareURL));
330
331  scoped_refptr<MalwareDetailsWrap> report =
332      new MalwareDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
333
334  std::string serialized = WaitForSerializedReport(report.get());
335
336  ClientMalwareReportRequest actual;
337  actual.ParseFromString(serialized);
338
339  ClientMalwareReportRequest expected;
340  expected.set_malware_url(kMalwareURL);
341  expected.set_page_url(kLandingURL);
342  expected.set_referrer_url("");
343
344  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
345  pb_resource->set_id(0);
346  pb_resource->set_url(kLandingURL);
347  pb_resource = expected.add_resources();
348  pb_resource->set_id(1);
349  pb_resource->set_url(kMalwareURL);
350
351  VerifyResults(actual, expected);
352}
353
354// Tests creating a simple malware report where the subresource has a
355// different original_url.
356TEST_F(MalwareDetailsTest, MalwareSubResourceWithOriginalUrl) {
357  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
358                       ui::PAGE_TRANSITION_TYPED, std::string());
359
360  UnsafeResource resource;
361  InitResource(&resource, true, GURL(kMalwareURL));
362  resource.original_url = GURL(kOriginalLandingURL);
363
364  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
365      ui_manager_.get(), web_contents(), resource, NULL);
366
367  std::string serialized = WaitForSerializedReport(report.get());
368
369  ClientMalwareReportRequest actual;
370  actual.ParseFromString(serialized);
371
372  ClientMalwareReportRequest expected;
373  expected.set_malware_url(kMalwareURL);
374  expected.set_page_url(kLandingURL);
375  expected.set_referrer_url("");
376
377  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
378  pb_resource->set_id(0);
379  pb_resource->set_url(kLandingURL);
380
381  pb_resource = expected.add_resources();
382  pb_resource->set_id(1);
383  pb_resource->set_url(kOriginalLandingURL);
384
385  pb_resource = expected.add_resources();
386  pb_resource->set_id(2);
387  pb_resource->set_url(kMalwareURL);
388  // The Resource for kMmalwareUrl should have the Resource for
389  // kOriginalLandingURL (with id 1) as parent.
390  pb_resource->set_parent_id(1);
391
392  VerifyResults(actual, expected);
393}
394
395// Tests creating a malware report with data from the renderer.
396TEST_F(MalwareDetailsTest, MalwareDOMDetails) {
397  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
398                       ui::PAGE_TRANSITION_TYPED, std::string());
399
400  UnsafeResource resource;
401  InitResource(&resource, true, GURL(kMalwareURL));
402
403  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
404      ui_manager_.get(), web_contents(), resource, NULL);
405
406  // Send a message from the DOM, with 2 nodes, a parent and a child.
407  std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
408  SafeBrowsingHostMsg_MalwareDOMDetails_Node child_node;
409  child_node.url = GURL(kDOMChildURL);
410  child_node.tag_name = "iframe";
411  child_node.parent = GURL(kDOMParentURL);
412  params.push_back(child_node);
413  SafeBrowsingHostMsg_MalwareDOMDetails_Node parent_node;
414  parent_node.url = GURL(kDOMParentURL);
415  parent_node.children.push_back(GURL(kDOMChildURL));
416  params.push_back(parent_node);
417  report->OnReceivedMalwareDOMDetails(params);
418
419  std::string serialized = WaitForSerializedReport(report.get());
420  ClientMalwareReportRequest actual;
421  actual.ParseFromString(serialized);
422
423  ClientMalwareReportRequest expected;
424  expected.set_malware_url(kMalwareURL);
425  expected.set_page_url(kLandingURL);
426  expected.set_referrer_url("");
427
428  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
429  pb_resource->set_id(0);
430  pb_resource->set_url(kLandingURL);
431
432  pb_resource = expected.add_resources();
433  pb_resource->set_id(1);
434  pb_resource->set_url(kMalwareURL);
435
436  pb_resource = expected.add_resources();
437  pb_resource->set_id(2);
438  pb_resource->set_url(kDOMChildURL);
439  pb_resource->set_parent_id(3);
440
441  pb_resource = expected.add_resources();
442  pb_resource->set_id(3);
443  pb_resource->set_url(kDOMParentURL);
444  pb_resource->add_child_ids(2);
445  expected.set_complete(false);  // Since the cache was missing.
446
447  VerifyResults(actual, expected);
448}
449
450// Verify that https:// urls are dropped.
451TEST_F(MalwareDetailsTest, NotPublicUrl) {
452  controller().LoadURL(GURL(kHttpsURL), content::Referrer(),
453                       ui::PAGE_TRANSITION_TYPED, std::string());
454  UnsafeResource resource;
455  InitResource(&resource, true, GURL(kMalwareURL));
456  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
457      ui_manager_.get(), web_contents(), resource, NULL);
458
459  std::string serialized = WaitForSerializedReport(report.get());
460  ClientMalwareReportRequest actual;
461  actual.ParseFromString(serialized);
462
463  ClientMalwareReportRequest expected;
464  expected.set_malware_url(kMalwareURL);  // No page_url
465  expected.set_referrer_url("");
466
467  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
468  pb_resource->set_url(kMalwareURL);  // Only one resource
469
470  VerifyResults(actual, expected);
471}
472
473// Tests creating a malware report where there are redirect urls to an unsafe
474// resource url
475TEST_F(MalwareDetailsTest, MalwareWithRedirectUrl) {
476  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
477                       ui::PAGE_TRANSITION_TYPED, std::string());
478
479  UnsafeResource resource;
480  InitResource(&resource, true, GURL(kMalwareURL));
481  resource.original_url = GURL(kOriginalLandingURL);
482
483  // add some redirect urls
484  resource.redirect_urls.push_back(GURL(kFirstRedirectURL));
485  resource.redirect_urls.push_back(GURL(kSecondRedirectURL));
486  resource.redirect_urls.push_back(GURL(kMalwareURL));
487
488  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
489      ui_manager_.get(), web_contents(), resource, NULL);
490
491  std::string serialized = WaitForSerializedReport(report.get());
492  ClientMalwareReportRequest actual;
493  actual.ParseFromString(serialized);
494
495  ClientMalwareReportRequest expected;
496  expected.set_malware_url(kMalwareURL);
497  expected.set_page_url(kLandingURL);
498  expected.set_referrer_url("");
499
500  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
501  pb_resource->set_id(0);
502  pb_resource->set_url(kLandingURL);
503
504  pb_resource = expected.add_resources();
505  pb_resource->set_id(1);
506  pb_resource->set_url(kOriginalLandingURL);
507
508  pb_resource = expected.add_resources();
509  pb_resource->set_id(2);
510  pb_resource->set_url(kMalwareURL);
511  pb_resource->set_parent_id(4);
512
513  pb_resource = expected.add_resources();
514  pb_resource->set_id(3);
515  pb_resource->set_url(kFirstRedirectURL);
516  pb_resource->set_parent_id(1);
517
518  pb_resource = expected.add_resources();
519  pb_resource->set_id(4);
520  pb_resource->set_url(kSecondRedirectURL);
521  pb_resource->set_parent_id(3);
522
523  VerifyResults(actual, expected);
524}
525
526// Tests the interaction with the HTTP cache.
527TEST_F(MalwareDetailsTest, HTTPCache) {
528  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
529                       ui::PAGE_TRANSITION_TYPED, std::string());
530
531  UnsafeResource resource;
532  InitResource(&resource, true, GURL(kMalwareURL));
533
534  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
535      ui_manager_.get(), web_contents(), resource,
536      profile()->GetRequestContext());
537
538  BrowserThread::PostTask(
539      BrowserThread::IO, FROM_HERE,
540      base::Bind(&FillCache,
541                 make_scoped_refptr(profile()->GetRequestContext())));
542
543  // The cache collection starts after the IPC from the DOM is fired.
544  std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
545  report->OnReceivedMalwareDOMDetails(params);
546
547  // Let the cache callbacks complete.
548  base::RunLoop().RunUntilIdle();
549
550  DVLOG(1) << "Getting serialized report";
551  std::string serialized = WaitForSerializedReport(report.get());
552  ClientMalwareReportRequest actual;
553  actual.ParseFromString(serialized);
554
555  ClientMalwareReportRequest expected;
556  expected.set_malware_url(kMalwareURL);
557  expected.set_page_url(kLandingURL);
558  expected.set_referrer_url("");
559
560  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
561  pb_resource->set_id(0);
562  pb_resource->set_url(kLandingURL);
563  safe_browsing::ClientMalwareReportRequest::HTTPResponse* pb_response =
564      pb_resource->mutable_response();
565  pb_response->mutable_firstline()->set_code(200);
566  safe_browsing::ClientMalwareReportRequest::HTTPHeader* pb_header =
567      pb_response->add_headers();
568  pb_header->set_name("Content-Type");
569  pb_header->set_value("text/html");
570  pb_header = pb_response->add_headers();
571  pb_header->set_name("Content-Length");
572  pb_header->set_value("1024");
573  pb_header = pb_response->add_headers();
574  pb_header->set_name("Set-Cookie");
575  pb_header->set_value("");  // The cookie is dropped.
576  pb_response->set_body(kLandingData);
577  pb_response->set_bodylength(37);
578  pb_response->set_bodydigest("9ca97475598a79bc1e8fc9bd6c72cd35");
579  pb_response->set_remote_ip("1.2.3.4:80");
580
581  pb_resource = expected.add_resources();
582  pb_resource->set_id(1);
583  pb_resource->set_url(kMalwareURL);
584  pb_response = pb_resource->mutable_response();
585  pb_response->mutable_firstline()->set_code(200);
586  pb_header = pb_response->add_headers();
587  pb_header->set_name("Content-Type");
588  pb_header->set_value("image/jpeg");
589  pb_response->set_body(kMalwareData);
590  pb_response->set_bodylength(10);
591  pb_response->set_bodydigest("581373551c43d4cf33bfb3b26838ff95");
592  pb_response->set_remote_ip("1.2.3.4:80");
593  expected.set_complete(true);
594
595  VerifyResults(actual, expected);
596}
597
598// Tests the interaction with the HTTP cache (where the cache is empty).
599TEST_F(MalwareDetailsTest, HTTPCacheNoEntries) {
600  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
601                       ui::PAGE_TRANSITION_TYPED, std::string());
602
603  UnsafeResource resource;
604  InitResource(&resource, true, GURL(kMalwareURL));
605
606  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
607      ui_manager_.get(), web_contents(), resource,
608      profile()->GetRequestContext());
609
610  // No call to FillCache
611
612  // The cache collection starts after the IPC from the DOM is fired.
613  std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
614  report->OnReceivedMalwareDOMDetails(params);
615
616  // Let the cache callbacks complete.
617  base::RunLoop().RunUntilIdle();
618
619  DVLOG(1) << "Getting serialized report";
620  std::string serialized = WaitForSerializedReport(report.get());
621  ClientMalwareReportRequest actual;
622  actual.ParseFromString(serialized);
623
624  ClientMalwareReportRequest expected;
625  expected.set_malware_url(kMalwareURL);
626  expected.set_page_url(kLandingURL);
627  expected.set_referrer_url("");
628
629  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
630  pb_resource->set_id(0);
631  pb_resource->set_url(kLandingURL);
632  pb_resource = expected.add_resources();
633  pb_resource->set_id(1);
634  pb_resource->set_url(kMalwareURL);
635  expected.set_complete(true);
636
637  VerifyResults(actual, expected);
638}
639
640// Test getting redirects from history service.
641TEST_F(MalwareDetailsTest, HistoryServiceUrls) {
642  // Add content to history service.
643  // There are two redirect urls before reacing malware url:
644  // kFirstRedirectURL -> kSecondRedirectURL -> kMalwareURL
645  GURL baseurl(kMalwareURL);
646  history::RedirectList redirects;
647  redirects.push_back(GURL(kFirstRedirectURL));
648  redirects.push_back(GURL(kSecondRedirectURL));
649  AddPageToHistory(baseurl, &redirects);
650  // Wait for history service operation finished.
651  profile()->BlockUntilHistoryProcessesPendingRequests();
652
653  controller().LoadURL(GURL(kLandingURL), content::Referrer(),
654                       ui::PAGE_TRANSITION_TYPED, std::string());
655
656  UnsafeResource resource;
657  InitResource(&resource, true, GURL(kMalwareURL));
658  scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
659      ui_manager_.get(), web_contents(), resource, NULL);
660
661  // The redirects collection starts after the IPC from the DOM is fired.
662  std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
663  report->OnReceivedMalwareDOMDetails(params);
664
665  // Let the redirects callbacks complete.
666  base::RunLoop().RunUntilIdle();
667
668  std::string serialized = WaitForSerializedReport(report.get());
669  ClientMalwareReportRequest actual;
670  actual.ParseFromString(serialized);
671
672  ClientMalwareReportRequest expected;
673  expected.set_malware_url(kMalwareURL);
674  expected.set_page_url(kLandingURL);
675  expected.set_referrer_url("");
676
677  ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
678  pb_resource->set_id(0);
679  pb_resource->set_url(kLandingURL);
680  pb_resource = expected.add_resources();
681  pb_resource->set_id(1);
682  pb_resource->set_parent_id(2);
683  pb_resource->set_url(kMalwareURL);
684  pb_resource = expected.add_resources();
685  pb_resource->set_id(2);
686  pb_resource->set_parent_id(3);
687  pb_resource->set_url(kSecondRedirectURL);
688  pb_resource = expected.add_resources();
689  pb_resource->set_id(3);
690  pb_resource->set_url(kFirstRedirectURL);
691
692  VerifyResults(actual, expected);
693}
694