text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h"
  
  #include <stdint.h>
  
  #include "base/command_line.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/test/simple_test_clock.h"
  #include "chrome/browser/browsing_data/browsing_data_helper.h"
  #include "chrome/browser/browsing_data/browsing_data_remover.h"
  #include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
  #include "chrome/browser/profiles/profile.h"
  #include "chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h"
  #include "chrome/browser/ui/browser.h"
  #include "chrome/browser/ui/tabs/tab_strip_model.h"
  #include "chrome/common/chrome_switches.h"
  #include "chrome/test/base/in_process_browser_test.h"
  #include "content/public/browser/ssl_host_state_delegate.h"
  #include "content/public/browser/web_contents.h"
  #include "content/public/test/browser_test_utils.h"
  #include "net/base/test_data_directory.h"
  #include "net/test/cert_test_util.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace {
  
  const char kGoogleCertFile[] = "google.single.der";
  
  const char kWWWGoogleHost[] = "www.google.com";
  const char kGoogleHost[] = "google.com";
  const char kExampleHost[] = "example.com";
  
  const char kForgetAtSessionEnd[] = "-1";
  const char kForgetInstantly[] = "0";
  const char kDeltaSecondsString[] = "86400";
  const uint64_t kDeltaOneDayInSeconds = UINT64_C(86400);
  
  scoped_refptr<net::X509Certificate> GetGoogleCert() {
    return net::ImportCertFromFile(net::GetTestCertsDirectory(), kGoogleCertFile);
  }
  
  }  // namespace
  
  class ChromeSSLHostStateDelegateTest : public InProcessBrowserTest {};
  
  // ChromeSSLHostStateDelegateTest tests basic unit test functionality of the
  // SSLHostStateDelegate class.  For example, tests that if a certificate is
  // accepted, then it is added to queryable, and if it is revoked, it is not
  // queryable. Even though it is effectively a unit test, in needs to be an
  // InProcessBrowserTest because the actual functionality is provided by
  // ChromeSSLHostStateDelegate which is provided per-profile.
  //
  // QueryPolicy unit tests the expected behavior of calling QueryPolicy on the
  // SSLHostStateDelegate class after various SSL cert decisions have been made.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, QueryPolicy) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // Verifying that all three of the certs we will be looking at are denied
    // before any action has been taken.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kExampleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  
    // Simulate a user decision to allow an invalid certificate exception for
    // kWWWGoogleHost.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
  
    // Verify that only kWWWGoogleHost is allowed and that the other two certs
    // being tested still are denied.
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kExampleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  
    // Simulate a user decision to allow an invalid certificate exception for
    // kExampleHost.
    state->AllowCert(
        kExampleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
  
    // Verify that both kWWWGoogleHost and kExampleHost have allow exceptions
    // while kGoogleHost still is denied.
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kExampleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  // HasPolicyAndRevoke unit tests the expected behavior of calling
  // HasAllowException before and after calling RevokeUserAllowExceptions on the
  // SSLHostStateDelegate class.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, HasPolicyAndRevoke) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    ChromeSSLHostStateDelegate* state =
        ChromeSSLHostStateDelegateFactory::GetForProfile(profile);
    bool unused_value;
  
    // Simulate a user decision to allow an invalid certificate exception for
    // kWWWGoogleHost and for kExampleHost.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    state->AllowCert(
        kExampleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
  
    // Verify that HasAllowException correctly acknowledges that a user decision
    // has been made about kWWWGoogleHost. Then verify that HasAllowException
    // correctly identifies that the decision has been revoked.
    EXPECT_TRUE(state->HasAllowException(kWWWGoogleHost));
    state->RevokeUserAllowExceptions(kWWWGoogleHost);
    EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  
    // Verify that the revocation of the kWWWGoogleHost decision does not affect
    // the Allow for kExampleHost.
    EXPECT_TRUE(state->HasAllowException(kExampleHost));
  
    // Verify the revocation of the kWWWGoogleHost decision does not affect the
    // non-decision for kGoogleHost. Then verify that a revocation of a URL with
    // no decision has no effect.
    EXPECT_FALSE(state->HasAllowException(kGoogleHost));
    state->RevokeUserAllowExceptions(kGoogleHost);
    EXPECT_FALSE(state->HasAllowException(kGoogleHost));
  }
  
  // Clear unit tests the expected behavior of calling Clear to forget all cert
  // decision state on the SSLHostStateDelegate class.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, Clear) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    ChromeSSLHostStateDelegate* state =
        ChromeSSLHostStateDelegateFactory::GetForProfile(profile);
    bool unused_value;
  
    // Simulate a user decision to allow an invalid certificate exception for
    // kWWWGoogleHost and for kExampleHost.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
  
    // Do a full clear, then make sure that both kWWWGoogleHost, which had a
    // decision made, and kExampleHost, which was untouched, are now in a denied
    // state.
    state->Clear();
    EXPECT_FALSE(state->HasAllowException(kWWWGoogleHost));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
    EXPECT_FALSE(state->HasAllowException(kExampleHost));
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kExampleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  // DidHostRunInsecureContent unit tests the expected behavior of calling
  // DidHostRunInsecureContent as well as HostRanInsecureContent to check if
  // insecure content has been run and to mark it as such.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest,
                         DidHostRunInsecureContent) {
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
  
    EXPECT_FALSE(state->DidHostRunInsecureContent("www.google.com", 42));
    EXPECT_FALSE(state->DidHostRunInsecureContent("www.google.com", 191));
    EXPECT_FALSE(state->DidHostRunInsecureContent("example.com", 42));
  
    state->HostRanInsecureContent("www.google.com", 42);
  
    EXPECT_TRUE(state->DidHostRunInsecureContent("www.google.com", 42));
    EXPECT_FALSE(state->DidHostRunInsecureContent("www.google.com", 191));
    EXPECT_FALSE(state->DidHostRunInsecureContent("example.com", 42));
  
    state->HostRanInsecureContent("example.com", 42);
  
    EXPECT_TRUE(state->DidHostRunInsecureContent("www.google.com", 42));
    EXPECT_FALSE(state->DidHostRunInsecureContent("www.google.com", 191));
    EXPECT_TRUE(state->DidHostRunInsecureContent("example.com", 42));
  }
  
  // QueryPolicyExpired unit tests to make sure that if a certificate decision has
  // expired, the return value from QueryPolicy returns the correct vaule.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, PRE_QueryPolicyExpired) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool expired_previous_decision;
  
    // The certificate has never been seen before, so it should be UNKNOWN and
    // should also indicate that it hasn't expired.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  
    // After allowing the certificate, a query should say that it is allowed and
    // also specify that it hasn't expired.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  }
  
  // Since this is being checked on a browser instance that expires security
  // decisions after restart, the test needs to  wait until after a restart to
  // verify that the expiration state is correct.
  IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, QueryPolicyExpired) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool expired_previous_decision;
  
    // The browser content has restart thus expiring the user decision made above,
    // so it should indicate that the certificate and error are DENIED but also
    // that they expired since the last query.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_TRUE(expired_previous_decision);
  
    // However, with a new query, it should indicate that no new expiration has
    // occurred.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  }
  
  // Tests the basic behavior of cert memory in incognito.
  class IncognitoSSLHostStateDelegateTest
      : public ChromeSSLHostStateDelegateTest {
   protected:
    void SetUpCommandLine(CommandLine* command_line) override {
      ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line);
      command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions,
                                      kDeltaSecondsString);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, PRE_AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // Add a cert exception to the profile and then verify that it still exists
    // in the incognito profile.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
  
    scoped_ptr<Profile> incognito(profile->CreateOffTheRecordProfile());
    content::SSLHostStateDelegate* incognito_state =
        incognito->GetSSLHostStateDelegate();
  
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              incognito_state->QueryPolicy(kWWWGoogleHost,
                                           *google_cert.get(),
                                           net::CERT_STATUS_DATE_INVALID,
                                           &unused_value));
  
    // Add a cert exception to the incognito profile. It will be checked after
    // restart that this exception does not exist. Note the different cert URL and
    // error than above thus mapping to a second exception. Also validate that it
    // was not added as an exception to the regular profile.
    incognito_state->AllowCert(
        kGoogleHost, *google_cert.get(), net::CERT_STATUS_COMMON_NAME_INVALID);
  
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_COMMON_NAME_INVALID,
                                 &unused_value));
  }
  
  // AfterRestart ensures that any cert decisions made in an incognito profile are
  // forgetten after a session restart even if given a command line flag to
  // remember cert decisions after restart.
  IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // Verify that the exception added before restart to the regular
    // (non-incognito) profile still exists and was not cleared after the
    // incognito session ended.
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  
    scoped_ptr<Profile> incognito(profile->CreateOffTheRecordProfile());
    content::SSLHostStateDelegate* incognito_state =
        incognito->GetSSLHostStateDelegate();
  
    // Verify that the exception added before restart to the incognito profile was
    // cleared when the incognito session ended.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              incognito_state->QueryPolicy(kGoogleHost,
                                           *google_cert.get(),
                                           net::CERT_STATUS_COMMON_NAME_INVALID,
                                           &unused_value));
  }
  
  // Tests to make sure that if the remember value is set to -1, any decisions
  // won't be remembered over a restart.
  class ForGetSSLHostStateDelegateTest : public ChromeSSLHostStateDelegateTest {
   protected:
    void SetUpCommandLine(CommandLine* command_line) override {
      ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line);
      command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions,
                                      kForgetAtSessionEnd);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(ForGetSSLHostStateDelegateTest, PRE_AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  IN_PROC_BROWSER_TEST_F(ForGetSSLHostStateDelegateTest, AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // The cert should now be |DENIED| because the profile is set to forget cert
    // exceptions after session end.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  // Tests to make sure that if the remember value is set to 0, any decisions made
  // will be forgetten immediately.
  class ForgetInstantlySSLHostStateDelegateTest
      : public ChromeSSLHostStateDelegateTest {
   protected:
    void SetUpCommandLine(CommandLine* command_line) override {
      ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line);
      command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions,
                                      kForgetInstantly);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(ForgetInstantlySSLHostStateDelegateTest,
                         MakeAndForgetException) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // chrome_state takes ownership of this clock
    base::SimpleTestClock* clock = new base::SimpleTestClock();
    ChromeSSLHostStateDelegate* chrome_state =
        static_cast<ChromeSSLHostStateDelegate*>(state);
    chrome_state->SetClock(scoped_ptr<base::Clock>(clock));
  
    // Start the clock at standard system time but do not advance at all to
    // emphasize that instant forget works.
    clock->SetNow(base::Time::NowFromSystemTime());
  
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  // Tests to make sure that if the remember value is set to a non-zero value,
  // any decisions will be remembered over a restart, but only for the length
  // specified.
  class RememberSSLHostStateDelegateTest : public ChromeSSLHostStateDelegateTest {
   protected:
    void SetUpCommandLine(CommandLine* command_line) override {
      ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line);
      command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions,
                                      kDeltaSecondsString);
    }
  };
  
  IN_PROC_BROWSER_TEST_F(RememberSSLHostStateDelegateTest, PRE_AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  IN_PROC_BROWSER_TEST_F(RememberSSLHostStateDelegateTest, AfterRestart) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // chrome_state takes ownership of this clock
    base::SimpleTestClock* clock = new base::SimpleTestClock();
    ChromeSSLHostStateDelegate* chrome_state =
        static_cast<ChromeSSLHostStateDelegate*>(state);
    chrome_state->SetClock(scoped_ptr<base::Clock>(clock));
  
    // Start the clock at standard system time.
    clock->SetNow(base::Time::NowFromSystemTime());
  
    // This should only pass if the cert was allowed before the test was restart
    // and thus has now been rememebered across browser restarts.
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  
    // Simulate the clock advancing by the specified delta.
    clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneDayInSeconds + 1));
  
    // The cert should now be |DENIED| because the specified delta has passed.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  
  // The same test as ChromeSSLHostStateDelegateTest.QueryPolicyExpired but now
  // applied to a browser context that expires based on time, not restart. This
  // unit tests to make sure that if a certificate decision has expired, the
  // return value from QueryPolicy returns the correct vaule.
  IN_PROC_BROWSER_TEST_F(RememberSSLHostStateDelegateTest, QueryPolicyExpired) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool expired_previous_decision;
  
    // chrome_state takes ownership of this clock
    base::SimpleTestClock* clock = new base::SimpleTestClock();
    ChromeSSLHostStateDelegate* chrome_state =
        static_cast<ChromeSSLHostStateDelegate*>(state);
    chrome_state->SetClock(scoped_ptr<base::Clock>(clock));
  
    // Start the clock at standard system time but do not advance at all to
    // emphasize that instant forget works.
    clock->SetNow(base::Time::NowFromSystemTime());
  
    // The certificate has never been seen before, so it should be UNKONWN and
    // should also indicate that it hasn't expired.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  
    // After allowing the certificate, a query should say that it is allowed and
    // also specify that it hasn't expired.
    state->AllowCert(
        kWWWGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    EXPECT_EQ(content::SSLHostStateDelegate::ALLOWED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  
    // Simulate the clock advancing by the specified delta.
    clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneDayInSeconds + 1));
  
    // The decision expiration time has come, so it should indicate that the
    // certificate and error are DENIED but also that they expired since the last
    // query.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_TRUE(expired_previous_decision);
  
    // However, with a new query, it should indicate that no new expiration has
    // occurred.
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kWWWGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &expired_previous_decision));
    EXPECT_FALSE(expired_previous_decision);
  }
  
  // Tests to make sure that if the user deletes their browser history, SSL
  // exceptions will be deleted as well.
  class RemoveBrowsingHistorySSLHostStateDelegateTest
      : public ChromeSSLHostStateDelegateTest {
   public:
    void RemoveAndWait(Profile* profile) {
      BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod(
          profile, BrowsingDataRemover::LAST_HOUR);
      BrowsingDataRemoverCompletionObserver completion_observer(remover);
      remover->Remove(BrowsingDataRemover::REMOVE_HISTORY,
                      BrowsingDataHelper::UNPROTECTED_WEB);
      completion_observer.BlockUntilCompletion();
    }
  };
  
  IN_PROC_BROWSER_TEST_F(RemoveBrowsingHistorySSLHostStateDelegateTest,
                         DeleteHistory) {
    scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert();
    content::WebContents* tab =
        browser()->tab_strip_model()->GetActiveWebContents();
    Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
    content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate();
    bool unused_value;
  
    // Add an exception for an invalid certificate. Then remove the last hour's
    // worth of browsing history and verify that the exception has been deleted.
    state->AllowCert(
        kGoogleHost, *google_cert.get(), net::CERT_STATUS_DATE_INVALID);
    RemoveAndWait(profile);
    EXPECT_EQ(content::SSLHostStateDelegate::DENIED,
              state->QueryPolicy(kGoogleHost,
                                 *google_cert.get(),
                                 net::CERT_STATUS_DATE_INVALID,
                                 &unused_value));
  }
  