Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_process_manager.cc
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.
4
5 #include "content/browser/service_worker/service_worker_process_manager.h"
6
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"
12
13 namespace content {
14
15 namespace {
16
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;
22   }
23 };
24
25 }  // namespace
26
27 static bool IncrementWorkerRefCountByPid(int process_id) {
28   RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
29   if (!rph || rph->FastShutdownStarted())
30     return false;
31
32   static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
33   return true;
34 }
35
36 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
37     const scoped_refptr<SiteInstance>& site_instance)
38     : site_instance(site_instance),
39       process_id(site_instance->GetProcess()->GetID()) {
40 }
41
42 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id)
43     : process_id(process_id) {
44 }
45
46 ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
47 }
48
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()) {
55 }
56
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.";
62 }
63
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();
73   }
74   instance_info_.clear();
75 }
76
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;
88   }
89
90   ProcessRefMap& process_refs = pattern_processes_[pattern];
91   ++process_refs[process_id];
92 }
93
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;
106   }
107
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;
112   }
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;
118   }
119   if (--found->second == 0) {
120     process_refs.erase(found);
121     if (process_refs.empty())
122       pattern_processes_.erase(it);
123   }
124 }
125
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();
132 }
133
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;
151   }
152
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;
161   }
162
163   DCHECK(!ContainsKey(instance_info_, embedded_worker_id))
164       << embedded_worker_id << " already has a process allocated";
165
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;
178   }
179
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;
187   }
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;
202   }
203
204   instance_info_.insert(
205       std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
206
207   static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
208   BrowserThread::PostTask(
209       BrowserThread::IO,
210       FROM_HERE,
211       base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
212 }
213
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;
223   }
224   if (process_id_for_test_ != -1) {
225     // Unittests don't increment or decrement the worker refcount of a
226     // RenderProcessHost.
227     return;
228   }
229   if (browser_context_ == NULL) {
230     // Shutdown already released all instances.
231     DCHECK(instance_info_.empty());
232     return;
233   }
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?";
248   }
249   static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
250   instance_info_.erase(info);
251 }
252
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>();
258
259   std::vector<std::pair<int, int> > counted(
260       it->second.begin(), it->second.end());
261   std::sort(counted.begin(), counted.end(), SecondGreater());
262
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;
267 }
268
269 }  // namespace content
270
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);
279 }
280 }  // namespace base