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 "base/bind.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/message_loop/message_loop.h" 10#include "chrome/browser/profiles/profile.h" 11#include "content/public/browser/notification_source.h" 12#include "content/public/browser/notification_types.h" 13#include "content/public/browser/render_process_host.h" 14 15 16namespace { 17 18const int64 kTimerDelaySeconds = 1; 19 20} // namespace 21 22std::vector<ProfileDestroyer*>* ProfileDestroyer::pending_destroyers_ = NULL; 23 24// static 25void ProfileDestroyer::DestroyProfileWhenAppropriate(Profile* const profile) { 26 DCHECK(profile); 27 profile->MaybeSendDestroyedNotification(); 28 29 std::vector<content::RenderProcessHost*> hosts; 30 // Testing profiles can simply be deleted directly. Some tests don't setup 31 // RenderProcessHost correctly and don't necessary run on the UI thread 32 // anyway, so we can't use the AllHostIterator. 33 if (profile->AsTestingProfile() == NULL) { 34 GetHostsForProfile(profile, &hosts); 35 if (!profile->IsOffTheRecord() && profile->HasOffTheRecordProfile()) 36 GetHostsForProfile(profile->GetOffTheRecordProfile(), &hosts); 37 } 38 // Generally, !hosts.empty() means that there is a leak in a render process 39 // host that MUST BE FIXED!!! 40 // 41 // However, off-the-record profiles are destroyed before their 42 // RenderProcessHosts in order to erase private data quickly, and 43 // RenderProcessHostImpl::Release() avoids destroying RenderProcessHosts in 44 // --single-process mode to avoid race conditions. 45 DCHECK(hosts.empty() || profile->IsOffTheRecord() || 46 content::RenderProcessHost::run_renderer_in_process()) << \ 47 "Profile still has " << hosts.size() << " hosts"; 48 // Note that we still test for !profile->IsOffTheRecord here even though we 49 // DCHECK'd above because we want to protect Release builds against this even 50 // we need to identify if there are leaks when we run Debug builds. 51 if (hosts.empty() || !profile->IsOffTheRecord()) { 52 if (profile->IsOffTheRecord()) 53 profile->GetOriginalProfile()->DestroyOffTheRecordProfile(); 54 else 55 delete profile; 56 } else { 57 // The instance will destroy itself once all render process hosts referring 58 // to it are properly terminated. 59 new ProfileDestroyer(profile, hosts); 60 } 61} 62 63// This can be called to cancel any pending destruction and destroy the profile 64// now, e.g., if the parent profile is being destroyed while the incognito one 65// still pending... 66void ProfileDestroyer::DestroyOffTheRecordProfileNow(Profile* const profile) { 67 DCHECK(profile); 68 DCHECK(profile->IsOffTheRecord()); 69 if (pending_destroyers_) { 70 for (size_t i = 0; i < pending_destroyers_->size(); ++i) { 71 if ((*pending_destroyers_)[i]->profile_ == profile) { 72 // We want to signal this in debug builds so that we don't lose sight of 73 // these potential leaks, but we handle it in release so that we don't 74 // crash or corrupt profile data on disk. 75 NOTREACHED() << "A render process host wasn't destroyed early enough."; 76 (*pending_destroyers_)[i]->profile_ = NULL; 77 break; 78 } 79 } 80 } 81 DCHECK(profile->GetOriginalProfile()); 82 profile->GetOriginalProfile()->DestroyOffTheRecordProfile(); 83} 84 85ProfileDestroyer::ProfileDestroyer( 86 Profile* const profile, 87 const std::vector<content::RenderProcessHost*>& hosts) 88 : timer_(false, false), 89 num_hosts_(0), 90 profile_(profile), 91 weak_ptr_factory_(this) { 92 if (pending_destroyers_ == NULL) 93 pending_destroyers_ = new std::vector<ProfileDestroyer*>; 94 pending_destroyers_->push_back(this); 95 for (size_t i = 0; i < hosts.size(); ++i) { 96 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, 97 content::Source<content::RenderProcessHost>(hosts[i])); 98 // For each of the notifications, we bump up our reference count. 99 // It will go back to 0 and free us when all hosts are terminated. 100 ++num_hosts_; 101 } 102 // If we are going to wait for render process hosts, we don't want to do it 103 // for longer than kTimerDelaySeconds. 104 if (num_hosts_) { 105 timer_.Start(FROM_HERE, 106 base::TimeDelta::FromSeconds(kTimerDelaySeconds), 107 base::Bind(&ProfileDestroyer::DestroyProfile, 108 weak_ptr_factory_.GetWeakPtr())); 109 } 110} 111 112ProfileDestroyer::~ProfileDestroyer() { 113 // Check again, in case other render hosts were added while we were 114 // waiting for the previous ones to go away... 115 if (profile_) 116 DestroyProfileWhenAppropriate(profile_); 117 118 // We shouldn't be deleted with pending notifications. 119 DCHECK(registrar_.IsEmpty()); 120 121 DCHECK(pending_destroyers_ != NULL); 122 std::vector<ProfileDestroyer*>::iterator iter = std::find( 123 pending_destroyers_->begin(), pending_destroyers_->end(), this); 124 DCHECK(iter != pending_destroyers_->end()); 125 pending_destroyers_->erase(iter); 126 DCHECK(pending_destroyers_->end() == std::find(pending_destroyers_->begin(), 127 pending_destroyers_->end(), 128 this)); 129 if (pending_destroyers_->empty()) { 130 delete pending_destroyers_; 131 pending_destroyers_ = NULL; 132 } 133} 134 135void ProfileDestroyer::Observe(int type, 136 const content::NotificationSource& source, 137 const content::NotificationDetails& details) { 138 DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED); 139 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, 140 source); 141 DCHECK(num_hosts_ > 0); 142 --num_hosts_; 143 if (num_hosts_ == 0) { 144 // Delay the destruction one step further in case other observers of this 145 // notification need to look at the profile attached to the host. 146 base::MessageLoop::current()->PostTask( 147 FROM_HERE, base::Bind( 148 &ProfileDestroyer::DestroyProfile, weak_ptr_factory_.GetWeakPtr())); 149 } 150} 151 152void ProfileDestroyer::DestroyProfile() { 153 // We might have been cancelled externally before the timer expired. 154 if (profile_ == NULL) 155 return; 156 DCHECK(profile_->IsOffTheRecord()); 157 DCHECK(profile_->GetOriginalProfile()); 158 profile_->GetOriginalProfile()->DestroyOffTheRecordProfile(); 159 profile_ = NULL; 160 161 // Don't wait for pending registrations, if any, these hosts are buggy. 162 // Note: this can happen, but if so, it's better to crash here than wait 163 // for the host to dereference a deleted Profile. http://crbug.com/248625 164 CHECK(registrar_.IsEmpty()) << "Some render process hosts were not " 165 << "destroyed early enough!"; 166 167 // And stop the timer so we can be released early too. 168 timer_.Stop(); 169 170 delete this; 171} 172 173// static 174bool ProfileDestroyer::GetHostsForProfile( 175 Profile* const profile, std::vector<content::RenderProcessHost*>* hosts) { 176 for (content::RenderProcessHost::iterator iter( 177 content::RenderProcessHost::AllHostsIterator()); 178 !iter.IsAtEnd(); iter.Advance()) { 179 content::RenderProcessHost* render_process_host = iter.GetCurrentValue(); 180 if (render_process_host && Profile::FromBrowserContext( 181 render_process_host->GetBrowserContext()) == profile) { 182 hosts->push_back(render_process_host); 183 } 184 } 185 return !hosts->empty(); 186} 187