[Android WebView] Upstream WebViewShell to chromium.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_process_manager.cc
blobc953129c8a4f18d3c8d887b05432bd7e521e4824
1 // Copyright 2014 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.
5 #include "content/browser/service_worker/service_worker_process_manager.h"
7 #include "content/browser/renderer_host/render_process_host_impl.h"
8 #include "content/browser/service_worker/service_worker_context_wrapper.h"
9 #include "content/public/browser/browser_thread.h"
10 #include "content/public/browser/site_instance.h"
11 #include "url/gurl.h"
13 namespace content {
15 namespace {
17 // Functor to sort by the .second element of a struct.
18 struct SecondGreater {
19 template <typename Value>
20 bool operator()(const Value& lhs, const Value& rhs) {
21 return lhs.second > rhs.second;
25 } // namespace
27 static bool IncrementWorkerRefCountByPid(int process_id) {
28 RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
29 if (!rph || rph->FastShutdownStarted())
30 return false;
32 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
33 return true;
36 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
37 const scoped_refptr<SiteInstance>& site_instance)
38 : site_instance(site_instance),
39 process_id(site_instance->GetProcess()->GetID()) {
42 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id)
43 : process_id(process_id) {
46 ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
49 ServiceWorkerProcessManager::ServiceWorkerProcessManager(
50 BrowserContext* browser_context)
51 : browser_context_(browser_context),
52 process_id_for_test_(-1),
53 weak_this_factory_(this) {
54 weak_this_ = weak_this_factory_.GetWeakPtr();
57 ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI);
59 DCHECK(browser_context_ == NULL)
60 << "Call Shutdown() before destroying |this|, so that racing method "
61 << "invocations don't use a destroyed BrowserContext.";
64 void ServiceWorkerProcessManager::Shutdown() {
65 DCHECK_CURRENTLY_ON(BrowserThread::UI);
66 browser_context_ = NULL;
67 for (std::map<int, ProcessInfo>::const_iterator it = instance_info_.begin();
68 it != instance_info_.end();
69 ++it) {
70 RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
71 DCHECK(rph);
72 static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
74 instance_info_.clear();
77 void ServiceWorkerProcessManager::AddProcessReferenceToPattern(
78 const GURL& pattern, int process_id) {
79 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
80 BrowserThread::PostTask(
81 BrowserThread::UI,
82 FROM_HERE,
83 base::Bind(&ServiceWorkerProcessManager::AddProcessReferenceToPattern,
84 weak_this_,
85 pattern,
86 process_id));
87 return;
90 ProcessRefMap& process_refs = pattern_processes_[pattern];
91 ++process_refs[process_id];
94 void ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern(
95 const GURL& pattern, int process_id) {
96 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
97 BrowserThread::PostTask(
98 BrowserThread::UI,
99 FROM_HERE,
100 base::Bind(
101 &ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern,
102 weak_this_,
103 pattern,
104 process_id));
105 return;
108 PatternProcessRefMap::iterator it = pattern_processes_.find(pattern);
109 if (it == pattern_processes_.end()) {
110 NOTREACHED() << "process refrences not found for pattern: " << pattern;
111 return;
113 ProcessRefMap& process_refs = it->second;
114 ProcessRefMap::iterator found = process_refs.find(process_id);
115 if (found == process_refs.end()) {
116 NOTREACHED() << "Releasing unknown process ref " << process_id;
117 return;
119 if (--found->second == 0) {
120 process_refs.erase(found);
121 if (process_refs.empty())
122 pattern_processes_.erase(it);
126 bool ServiceWorkerProcessManager::PatternHasProcessToRun(
127 const GURL& pattern) const {
128 PatternProcessRefMap::const_iterator it = pattern_processes_.find(pattern);
129 if (it == pattern_processes_.end())
130 return false;
131 return !it->second.empty();
134 void ServiceWorkerProcessManager::AllocateWorkerProcess(
135 int embedded_worker_id,
136 const GURL& pattern,
137 const GURL& script_url,
138 const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
139 callback) {
140 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
141 BrowserThread::PostTask(
142 BrowserThread::UI,
143 FROM_HERE,
144 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
145 weak_this_,
146 embedded_worker_id,
147 pattern,
148 script_url,
149 callback));
150 return;
153 if (process_id_for_test_ != -1) {
154 // Let tests specify the returned process ID. Note: We may need to be able
155 // to specify the error code too.
156 BrowserThread::PostTask(
157 BrowserThread::IO,
158 FROM_HERE,
159 base::Bind(callback, SERVICE_WORKER_OK, process_id_for_test_));
160 return;
163 DCHECK(!ContainsKey(instance_info_, embedded_worker_id))
164 << embedded_worker_id << " already has a process allocated";
166 std::vector<int> sorted_candidates = SortProcessesForPattern(pattern);
167 for (std::vector<int>::const_iterator it = sorted_candidates.begin();
168 it != sorted_candidates.end();
169 ++it) {
170 if (!IncrementWorkerRefCountByPid(*it))
171 continue;
172 instance_info_.insert(
173 std::make_pair(embedded_worker_id, ProcessInfo(*it)));
174 BrowserThread::PostTask(BrowserThread::IO,
175 FROM_HERE,
176 base::Bind(callback, SERVICE_WORKER_OK, *it));
177 return;
180 if (!browser_context_) {
181 // Shutdown has started.
182 BrowserThread::PostTask(
183 BrowserThread::IO, FROM_HERE,
184 base::Bind(callback, SERVICE_WORKER_ERROR_ABORT, -1));
185 return;
187 // No existing processes available; start a new one.
188 scoped_refptr<SiteInstance> site_instance =
189 SiteInstance::CreateForURL(browser_context_, script_url);
190 RenderProcessHost* rph = site_instance->GetProcess();
191 // This Init() call posts a task to the IO thread that adds the RPH's
192 // ServiceWorkerDispatcherHost to the
193 // EmbeddedWorkerRegistry::process_sender_map_.
194 if (!rph->Init()) {
195 LOG(ERROR) << "Couldn't start a new process!";
196 BrowserThread::PostTask(
197 BrowserThread::IO, FROM_HERE,
198 base::Bind(callback, SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND, -1));
199 return;
202 instance_info_.insert(
203 std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
205 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
206 BrowserThread::PostTask(
207 BrowserThread::IO,
208 FROM_HERE,
209 base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
212 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id) {
213 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
214 BrowserThread::PostTask(
215 BrowserThread::UI,
216 FROM_HERE,
217 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
218 weak_this_,
219 embedded_worker_id));
220 return;
222 if (process_id_for_test_ != -1) {
223 // Unittests don't increment or decrement the worker refcount of a
224 // RenderProcessHost.
225 return;
227 if (browser_context_ == NULL) {
228 // Shutdown already released all instances.
229 DCHECK(instance_info_.empty());
230 return;
232 std::map<int, ProcessInfo>::iterator info =
233 instance_info_.find(embedded_worker_id);
234 DCHECK(info != instance_info_.end());
235 RenderProcessHost* rph = NULL;
236 if (info->second.site_instance.get()) {
237 rph = info->second.site_instance->GetProcess();
238 DCHECK_EQ(info->second.process_id, rph->GetID())
239 << "A SiteInstance's process shouldn't get destroyed while we're "
240 "holding a reference to it. Was the reference actually held?";
241 } else {
242 rph = RenderProcessHost::FromID(info->second.process_id);
243 DCHECK(rph)
244 << "Process " << info->second.process_id
245 << " was destroyed unexpectedly. Did we actually hold a reference?";
247 static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
248 instance_info_.erase(info);
251 std::vector<int> ServiceWorkerProcessManager::SortProcessesForPattern(
252 const GURL& pattern) const {
253 PatternProcessRefMap::const_iterator it = pattern_processes_.find(pattern);
254 if (it == pattern_processes_.end())
255 return std::vector<int>();
257 std::vector<std::pair<int, int> > counted(
258 it->second.begin(), it->second.end());
259 std::sort(counted.begin(), counted.end(), SecondGreater());
261 std::vector<int> result(counted.size());
262 for (size_t i = 0; i < counted.size(); ++i)
263 result[i] = counted[i].first;
264 return result;
267 } // namespace content
269 namespace base {
270 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
271 // member WeakPtr to safely guard the object's lifetime when used on that
272 // thread.
273 void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
274 content::ServiceWorkerProcessManager* ptr) const {
275 content::BrowserThread::DeleteSoon(
276 content::BrowserThread::UI, FROM_HERE, ptr);
278 } // namespace base