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 "chrome/browser/profiles/profile_destroyer.h"
6
7#include "chrome/test/base/browser_with_test_window_test.h"
8#include "chrome/test/base/testing_profile.h"
9#include "content/public/browser/render_process_host.h"
10#include "content/public/browser/site_instance.h"
11
12class TestingOffTheRecordDestructionProfile : public TestingProfile {
13 public:
14  TestingOffTheRecordDestructionProfile()
15      : TestingProfile(base::FilePath(),
16                       NULL,
17                       scoped_refptr<ExtensionSpecialStoragePolicy>()
18                       scoped_ptr<PrefServiceSyncable>(),
19                       true,
20                       TestingFactories()),
21        destroyed_otr_profile_(false) {
22    set_incognito(true);
23  }
24  virtual void DestroyOffTheRecordProfile() OVERRIDE {
25    destroyed_otr_profile_ = true;
26  }
27  bool destroyed_otr_profile_;
28
29  DISALLOW_COPY_AND_ASSIGN(TestingOffTheRecordDestructionProfile);
30};
31
32class TestingOriginalDestructionProfile : public TestingProfile {
33 public:
34  TestingOriginalDestructionProfile() : destroyed_otr_profile_(false) {
35    DCHECK_EQ(kNull, living_instance_);
36    living_instance_ = this;
37  }
38  virtual ~TestingOriginalDestructionProfile() {
39    DCHECK_EQ(this, living_instance_);
40    living_instance_ = NULL;
41  }
42  virtual void DestroyOffTheRecordProfile() OVERRIDE {
43    SetOffTheRecordProfile(NULL);
44    destroyed_otr_profile_ = true;
45  }
46  bool destroyed_otr_profile_;
47  static TestingOriginalDestructionProfile* living_instance_;
48
49  // This is to avoid type casting in DCHECK_EQ & EXPECT_NE.
50  static const TestingOriginalDestructionProfile* kNull;
51
52  DISALLOW_COPY_AND_ASSIGN(TestingOriginalDestructionProfile);
53};
54const TestingOriginalDestructionProfile*
55    TestingOriginalDestructionProfile::kNull = NULL;
56
57TestingOriginalDestructionProfile*
58    TestingOriginalDestructionProfile::living_instance_ = NULL;
59
60class ProfileDestroyerTest : public BrowserWithTestWindowTest {
61 public:
62  ProfileDestroyerTest() : off_the_record_profile_(NULL) {}
63
64 protected:
65  virtual TestingProfile* CreateProfile() OVERRIDE {
66    if (off_the_record_profile_ == NULL)
67      off_the_record_profile_ = new TestingOffTheRecordDestructionProfile();
68    return off_the_record_profile_;
69  }
70  TestingOffTheRecordDestructionProfile* off_the_record_profile_;
71
72  DISALLOW_COPY_AND_ASSIGN(ProfileDestroyerTest);
73};
74
75TEST_F(ProfileDestroyerTest, DelayProfileDestruction) {
76  scoped_refptr<content::SiteInstance> instance1(
77      content::SiteInstance::Create(off_the_record_profile_));
78  scoped_ptr<content::RenderProcessHost> render_process_host1;
79  render_process_host1.reset(instance1->GetProcess());
80  ASSERT_TRUE(render_process_host1.get() != NULL);
81
82  scoped_refptr<content::SiteInstance> instance2(
83      content::SiteInstance::Create(off_the_record_profile_));
84  scoped_ptr<content::RenderProcessHost> render_process_host2;
85  render_process_host2.reset(instance2->GetProcess());
86  ASSERT_TRUE(render_process_host2.get() != NULL);
87
88  // destroying the browser should not destroy the off the record profile...
89  set_browser(NULL);
90  EXPECT_FALSE(off_the_record_profile_->destroyed_otr_profile_);
91
92  // until we destroy the render process host holding on to it...
93  render_process_host1.release()->Cleanup();
94
95  // And asynchronicity kicked in properly.
96  base::MessageLoop::current()->RunUntilIdle();
97  EXPECT_FALSE(off_the_record_profile_->destroyed_otr_profile_);
98
99  // I meant, ALL the render process hosts... :-)
100  render_process_host2.release()->Cleanup();
101  base::MessageLoop::current()->RunUntilIdle();
102  EXPECT_TRUE(off_the_record_profile_->destroyed_otr_profile_);
103}
104
105TEST_F(ProfileDestroyerTest, DelayOriginalProfileDestruction) {
106  TestingOriginalDestructionProfile* original_profile =
107      new TestingOriginalDestructionProfile;
108
109  TestingOffTheRecordDestructionProfile* off_the_record_profile =
110      new TestingOffTheRecordDestructionProfile;
111
112  original_profile->SetOffTheRecordProfile(off_the_record_profile);
113
114  scoped_refptr<content::SiteInstance> instance1(
115      content::SiteInstance::Create(off_the_record_profile));
116  scoped_ptr<content::RenderProcessHost> render_process_host1;
117  render_process_host1.reset(instance1->GetProcess());
118  ASSERT_TRUE(render_process_host1.get() != NULL);
119
120  // Trying to destroy the original profile should be delayed until associated
121  // off the record profile is released by all render process hosts.
122  ProfileDestroyer::DestroyProfileWhenAppropriate(original_profile);
123  EXPECT_NE(TestingOriginalDestructionProfile::kNull,
124            TestingOriginalDestructionProfile::living_instance_);
125  EXPECT_FALSE(original_profile->destroyed_otr_profile_);
126
127  render_process_host1.release()->Cleanup();
128  base::MessageLoop::current()->RunUntilIdle();
129  EXPECT_EQ(NULL, TestingOriginalDestructionProfile::living_instance_);
130
131  // And the same protection should apply to the main profile.
132  TestingOriginalDestructionProfile* main_profile =
133      new TestingOriginalDestructionProfile;
134  scoped_refptr<content::SiteInstance> instance2(
135      content::SiteInstance::Create(main_profile));
136  scoped_ptr<content::RenderProcessHost> render_process_host2;
137  render_process_host2.reset(instance2->GetProcess());
138  ASSERT_TRUE(render_process_host2.get() != NULL);
139
140  ProfileDestroyer::DestroyProfileWhenAppropriate(main_profile);
141  EXPECT_EQ(main_profile, TestingOriginalDestructionProfile::living_instance_);
142  render_process_host2.release()->Cleanup();
143  base::MessageLoop::current()->RunUntilIdle();
144  EXPECT_EQ(NULL, TestingOriginalDestructionProfile::living_instance_);
145}
146