summaryrefslogtreecommitdiffstats
path: root/chrome/browser/profiles/profile_destroyer.cc
blob: f26180b5718e3d66264095507a51f3e205ab9c0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright (c) 2012 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/profiles/profile_destroyer.h"

#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"


namespace {

const int64 kTimerDelaySeconds = 1;

}  // namespace

std::vector<ProfileDestroyer*>* ProfileDestroyer::pending_destroyers_ = NULL;

// static
void ProfileDestroyer::DestroyProfileWhenAppropriate(Profile* const profile) {
  DCHECK(profile);
  profile->MaybeSendDestroyedNotification();

  std::vector<content::RenderProcessHost*> hosts;
  // Testing profiles can simply be deleted directly. Some tests don't setup
  // RenderProcessHost correctly and don't necessary run on the UI thread
  // anyway, so we can't use the AllHostIterator.
  if (profile->AsTestingProfile() == NULL) {
    GetHostsForProfile(profile, &hosts);
    if (!profile->IsOffTheRecord() && profile->HasOffTheRecordProfile())
      GetHostsForProfile(profile->GetOffTheRecordProfile(), &hosts);
  }
  // Generally, !hosts.empty() means that there is a leak in a render process
  // host that MUST BE FIXED!!!
  //
  // However, off-the-record profiles are destroyed before their
  // RenderProcessHosts in order to erase private data quickly, and
  // RenderProcessHostImpl::Release() avoids destroying RenderProcessHosts in
  // --single-process mode to avoid race conditions.
  DCHECK(hosts.empty() || profile->IsOffTheRecord() ||
         content::RenderProcessHost::run_renderer_in_process());
  // Note that we still test for !profile->IsOffTheRecord here even though we
  // DCHECK'd above because we want to protect Release builds against this even
  // we need to identify if there are leaks when we run Debug builds.
  if (hosts.empty() || !profile->IsOffTheRecord()) {
    if (profile->IsOffTheRecord())
      profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
    else
      delete profile;
  } else {
    // The instance will destroy itself once all render process hosts referring
    // to it are properly terminated.
    new ProfileDestroyer(profile, hosts);
  }
}

// This can be called to cancel any pending destruction and destroy the profile
// now, e.g., if the parent profile is being destroyed while the incognito one
// still pending...
void ProfileDestroyer::DestroyOffTheRecordProfileNow(Profile* const profile) {
  DCHECK(profile);
  DCHECK(profile->IsOffTheRecord());
  if (pending_destroyers_) {
    for (size_t i = 0; i < pending_destroyers_->size(); ++i) {
      if ((*pending_destroyers_)[i]->profile_ == profile) {
        // We want to signal this in debug builds so that we don't lose sight of
        // these potential leaks, but we handle it in release so that we don't
        // crash or corrupt profile data on disk.
        NOTREACHED() << "A render process host wasn't destroyed early enough.";
        (*pending_destroyers_)[i]->profile_ = NULL;
        break;
      }
    }
  }
  DCHECK(profile->GetOriginalProfile());
  profile->GetOriginalProfile()->DestroyOffTheRecordProfile();
}

ProfileDestroyer::ProfileDestroyer(
    Profile* const profile,
    const std::vector<content::RenderProcessHost*>& hosts)
    : timer_(false, false),
      num_hosts_(0),
      profile_(profile),
      weak_ptr_factory_(this) {
  if (pending_destroyers_ == NULL)
    pending_destroyers_ = new std::vector<ProfileDestroyer*>;
  pending_destroyers_->push_back(this);
  for (size_t i = 0; i < hosts.size(); ++i) {
    registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                   content::Source<content::RenderProcessHost>(hosts[i]));
    // For each of the notifications, we bump up our reference count.
    // It will go back to 0 and free us when all hosts are terminated.
    ++num_hosts_;
  }
  // If we are going to wait for render process hosts, we don't want to do it
  // for longer than kTimerDelaySeconds.
  if (num_hosts_) {
    timer_.Start(FROM_HERE,
                 base::TimeDelta::FromSeconds(kTimerDelaySeconds),
                 base::Bind(&ProfileDestroyer::DestroyProfile,
                            weak_ptr_factory_.GetWeakPtr()));
  }
}

ProfileDestroyer::~ProfileDestroyer() {
  // Check again, in case other render hosts were added while we were
  // waiting for the previous ones to go away...
  if (profile_)
    DestroyProfileWhenAppropriate(profile_);

  // We shouldn't be deleted with pending notifications.
  DCHECK(registrar_.IsEmpty());

  DCHECK(pending_destroyers_ != NULL);
  std::vector<ProfileDestroyer*>::iterator iter = std::find(
      pending_destroyers_->begin(), pending_destroyers_->end(), this);
  DCHECK(iter != pending_destroyers_->end());
  pending_destroyers_->erase(iter);
  DCHECK(pending_destroyers_->end() == std::find(pending_destroyers_->begin(),
                                                 pending_destroyers_->end(),
                                                 this));
  if (pending_destroyers_->empty()) {
    delete pending_destroyers_;
    pending_destroyers_ = NULL;
  }
}

void ProfileDestroyer::Observe(int type,
                               const content::NotificationSource& source,
                               const content::NotificationDetails& details) {
  DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED);
  registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                    source);
  DCHECK(num_hosts_ > 0);
  --num_hosts_;
  if (num_hosts_ == 0) {
    // Delay the destruction one step further in case other observers of this
    // notification need to look at the profile attached to the host.
    base::MessageLoop::current()->PostTask(
        FROM_HERE, base::Bind(
            &ProfileDestroyer::DestroyProfile, weak_ptr_factory_.GetWeakPtr()));
  }
}

void ProfileDestroyer::DestroyProfile() {
  // We might have been cancelled externally before the timer expired.
  if (profile_ == NULL)
    return;
  DCHECK(profile_->IsOffTheRecord());
  DCHECK(profile_->GetOriginalProfile());
  profile_->GetOriginalProfile()->DestroyOffTheRecordProfile();
  profile_ = NULL;

  // Don't wait for pending registrations, if any, these hosts are buggy.
  DCHECK(registrar_.IsEmpty()) << "Some render process hosts where not "
                               << "destroyed early enough!";
  registrar_.RemoveAll();

  // And stop the timer so we can be released early too.
  timer_.Stop();

  delete this;
}

// static
bool ProfileDestroyer::GetHostsForProfile(
    Profile* const profile, std::vector<content::RenderProcessHost*>* hosts) {
  for (content::RenderProcessHost::iterator iter(
        content::RenderProcessHost::AllHostsIterator());
      !iter.IsAtEnd(); iter.Advance()) {
    content::RenderProcessHost* render_process_host = iter.GetCurrentValue();
    if (render_process_host && Profile::FromBrowserContext(
          render_process_host->GetBrowserContext()) == profile) {
      hosts->push_back(render_process_host);
    }
  }
  return !hosts->empty();
}