Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_process_manager.cc
blob7fa03a5132afef8a8c6ccaf9f97f6f92d05acdb5
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,
184 FROM_HERE,
185 base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
186 return;
188 // No existing processes available; start a new one.
189 scoped_refptr<SiteInstance> site_instance =
190 SiteInstance::CreateForURL(browser_context_, script_url);
191 RenderProcessHost* rph = site_instance->GetProcess();
192 // This Init() call posts a task to the IO thread that adds the RPH's
193 // ServiceWorkerDispatcherHost to the
194 // EmbeddedWorkerRegistry::process_sender_map_.
195 if (!rph->Init()) {
196 LOG(ERROR) << "Couldn't start a new process!";
197 BrowserThread::PostTask(
198 BrowserThread::IO,
199 FROM_HERE,
200 base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
201 return;
204 instance_info_.insert(
205 std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
207 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
208 BrowserThread::PostTask(
209 BrowserThread::IO,
210 FROM_HERE,
211 base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
214 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id) {
215 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
216 BrowserThread::PostTask(
217 BrowserThread::UI,
218 FROM_HERE,
219 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
220 weak_this_,
221 embedded_worker_id));
222 return;
224 if (process_id_for_test_ != -1) {
225 // Unittests don't increment or decrement the worker refcount of a
226 // RenderProcessHost.
227 return;
229 if (browser_context_ == NULL) {
230 // Shutdown already released all instances.
231 DCHECK(instance_info_.empty());
232 return;
234 std::map<int, ProcessInfo>::iterator info =
235 instance_info_.find(embedded_worker_id);
236 DCHECK(info != instance_info_.end());
237 RenderProcessHost* rph = NULL;
238 if (info->second.site_instance.get()) {
239 rph = info->second.site_instance->GetProcess();
240 DCHECK_EQ(info->second.process_id, rph->GetID())
241 << "A SiteInstance's process shouldn't get destroyed while we're "
242 "holding a reference to it. Was the reference actually held?";
243 } else {
244 rph = RenderProcessHost::FromID(info->second.process_id);
245 DCHECK(rph)
246 << "Process " << info->second.process_id
247 << " was destroyed unexpectedly. Did we actually hold a reference?";
249 static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
250 instance_info_.erase(info);
253 std::vector<int> ServiceWorkerProcessManager::SortProcessesForPattern(
254 const GURL& pattern) const {
255 PatternProcessRefMap::const_iterator it = pattern_processes_.find(pattern);
256 if (it == pattern_processes_.end())
257 return std::vector<int>();
259 std::vector<std::pair<int, int> > counted(
260 it->second.begin(), it->second.end());
261 std::sort(counted.begin(), counted.end(), SecondGreater());
263 std::vector<int> result(counted.size());
264 for (size_t i = 0; i < counted.size(); ++i)
265 result[i] = counted[i].first;
266 return result;
269 } // namespace content
271 namespace base {
272 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
273 // member WeakPtr to safely guard the object's lifetime when used on that
274 // thread.
275 void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
276 content::ServiceWorkerProcessManager* ptr) const {
277 content::BrowserThread::DeleteSoon(
278 content::BrowserThread::UI, FROM_HERE, ptr);
280 } // namespace base