Upstream version 8.36.169.0
[platform/framework/web/crosswalk.git] / src / xwalk / extensions / browser / xwalk_extension_service.cc
1 // Copyright (c) 2013 Intel Corporation. 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 "xwalk/extensions/browser/xwalk_extension_service.h"
6
7 #include <set>
8 #include <vector>
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/pickle.h"
12 #include "base/scoped_native_library.h"
13 #include "base/synchronization/lock.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "ipc/ipc_message_macros.h"
19 #include "ipc/message_filter.h"
20 #include "xwalk/extensions/browser/xwalk_extension_data.h"
21 #include "xwalk/extensions/browser/xwalk_extension_process_host.h"
22 #include "xwalk/extensions/common/xwalk_extension.h"
23 #include "xwalk/extensions/common/xwalk_extension_messages.h"
24 #include "xwalk/extensions/common/xwalk_extension_server.h"
25 #include "xwalk/extensions/common/xwalk_extension_switches.h"
26
27 using content::BrowserThread;
28
29 namespace xwalk {
30 namespace extensions {
31
32 namespace {
33
34 XWalkExtensionService::CreateExtensionsCallback
35     g_create_extension_thread_extensions_callback;
36
37 XWalkExtensionService::CreateExtensionsCallback
38     g_create_ui_thread_extensions_callback;
39
40 base::FilePath g_external_extensions_path_for_testing_;
41
42 }  // namespace
43
44 // This object intercepts messages destined to a XWalkExtensionServer and
45 // dispatch them to its task runner. A message loop proxy of a thread is a
46 // task runner. Like other filters, this filter will run in the IO-thread.
47 //
48 // In the case of in process extensions, we will pass the task runner of the
49 // extension thread.
50 class ExtensionServerMessageFilter : public IPC::MessageFilter,
51                                      public IPC::Sender {
52  public:
53   ExtensionServerMessageFilter(
54       scoped_refptr<base::SequencedTaskRunner> task_runner,
55       XWalkExtensionServer* extension_thread_server,
56       XWalkExtensionServer* ui_thread_server)
57       : sender_(NULL),
58         task_runner_(task_runner),
59         extension_thread_server_(extension_thread_server),
60         ui_thread_server_(ui_thread_server) {}
61
62   // Tells the filter to stop dispatching messages to the server.
63   void Invalidate() {
64     base::AutoLock l(lock_);
65     sender_ = NULL;
66     task_runner_ = NULL;
67     extension_thread_server_ = NULL;
68     ui_thread_server_ = NULL;
69   }
70
71   // IPC::Sender implementation.
72   virtual bool Send(IPC::Message* msg_ptr) OVERRIDE {
73     scoped_ptr<IPC::Message> msg(msg_ptr);
74
75     if (!sender_)
76       return false;
77
78     return sender_->Send(msg.release());
79   }
80
81  private:
82   virtual ~ExtensionServerMessageFilter() {}
83
84   int64_t GetInstanceIDFromMessage(const IPC::Message& message) {
85     PickleIterator iter;
86
87     if (message.is_sync())
88       iter = IPC::SyncMessage::GetDataIterator(&message);
89     else
90       iter = PickleIterator(message);
91
92     int64_t instance_id;
93     if (!iter.ReadInt64(&instance_id))
94       return -1;
95
96     return instance_id;
97   }
98
99   void RouteMessageToServer(const IPC::Message& message) {
100     int64_t id = GetInstanceIDFromMessage(message);
101     DCHECK_NE(id, -1);
102
103     XWalkExtensionServer* server;
104     base::TaskRunner* task_runner;
105     scoped_refptr<base::TaskRunner> task_runner_ref;
106
107     if (ContainsKey(extension_thread_instances_ids_, id)) {
108       server = extension_thread_server_;
109       task_runner = task_runner_;
110     } else {
111       server = ui_thread_server_;
112       task_runner_ref =
113           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
114       task_runner = task_runner_ref.get();
115     }
116
117     base::Closure closure = base::Bind(
118         base::IgnoreResult(&XWalkExtensionServer::OnMessageReceived),
119         server->AsWeakPtr(), message);
120
121     task_runner->PostTask(FROM_HERE, closure);
122   }
123
124   void OnCreateInstance(int64_t instance_id, std::string name) {
125     XWalkExtensionServer* server;
126     base::TaskRunner* task_runner;
127     scoped_refptr<base::TaskRunner> task_runner_ref;
128
129     if (extension_thread_server_->ContainsExtension(name)) {
130       extension_thread_instances_ids_.insert(instance_id);
131       server = extension_thread_server_;
132       task_runner = task_runner_;
133     } else {
134       server = ui_thread_server_;
135       task_runner_ref =
136           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
137       task_runner = task_runner_ref.get();
138     }
139
140     base::Closure closure = base::Bind(
141         base::IgnoreResult(&XWalkExtensionServer::OnCreateInstance),
142         server->AsWeakPtr(), instance_id, name);
143
144     task_runner->PostTask(FROM_HERE, closure);
145   }
146
147   void OnGetExtensions(
148       std::vector<XWalkExtensionServerMsg_ExtensionRegisterParams>* reply) {
149     extension_thread_server_->OnGetExtensions(reply);
150     ui_thread_server_->OnGetExtensions(reply);
151   }
152
153   // IPC::ChannelProxy::MessageFilter implementation.
154   virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
155     sender_ = channel;
156   }
157
158   virtual void OnFilterRemoved() OVERRIDE {
159     sender_ = NULL;
160   }
161
162   virtual void OnChannelClosing() OVERRIDE {
163     sender_ = NULL;
164   }
165
166   virtual void OnChannelError() OVERRIDE {
167     sender_ = NULL;
168   }
169
170   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
171     if (IPC_MESSAGE_CLASS(message) != XWalkExtensionClientServerMsgStart)
172       return false;
173
174     base::AutoLock l(lock_);
175
176     if (!extension_thread_server_ || !ui_thread_server_)
177       return false;
178
179     bool handled = true;
180     IPC_BEGIN_MESSAGE_MAP(ExtensionServerMessageFilter, message)
181       IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_CreateInstance,
182                           OnCreateInstance)
183       IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_GetExtensions,
184                           OnGetExtensions)
185       IPC_MESSAGE_UNHANDLED(handled = false)
186     IPC_END_MESSAGE_MAP()
187
188     if (!handled)
189       RouteMessageToServer(message);
190
191     return true;
192   }
193
194   // This lock is used to protect access to filter members.
195   base::Lock lock_;
196
197   IPC::Sender* sender_;
198   scoped_refptr<base::SequencedTaskRunner> task_runner_;
199   XWalkExtensionServer* extension_thread_server_;
200   XWalkExtensionServer* ui_thread_server_;
201   std::set<int64_t> extension_thread_instances_ids_;
202 };
203
204 bool XWalkExtensionService::Delegate::RegisterPermissions(
205     int render_process_id,
206     const std::string& extension_name,
207     const std::string& perm_table) {
208   return false;
209 }
210
211 XWalkExtensionService::XWalkExtensionService(Delegate* delegate)
212     : extension_thread_("XWalkExtensionThread"),
213       delegate_(delegate) {
214   if (!g_external_extensions_path_for_testing_.empty())
215     external_extensions_path_ = g_external_extensions_path_for_testing_;
216   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
217                  content::NotificationService::AllBrowserContextsAndSources());
218
219   // IO main loop is needed by extensions watching file descriptors events.
220   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
221   extension_thread_.StartWithOptions(options);
222 }
223
224 XWalkExtensionService::~XWalkExtensionService() {
225   // This object should have been released and asked to be deleted in the
226   // extension thread.
227   if (!extension_data_map_.empty())
228     VLOG(1) << "The ExtensionData map is not empty!";
229 }
230
231 void XWalkExtensionService::RegisterExternalExtensionsForPath(
232     const base::FilePath& path) {
233   external_extensions_path_ = path;
234 }
235
236 void XWalkExtensionService::OnRenderProcessHostCreatedInternal(
237     content::RenderProcessHost* host,
238     XWalkExtensionVector* ui_thread_extensions,
239     XWalkExtensionVector* extension_thread_extensions,
240     scoped_ptr<base::ValueMap> runtime_variables) {
241   XWalkExtensionData* data = new XWalkExtensionData;
242   data->set_render_process_host(host);
243
244   CreateInProcessExtensionServers(host, data, ui_thread_extensions,
245                                   extension_thread_extensions);
246
247   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
248   if (!cmd_line->HasSwitch(switches::kXWalkDisableExtensionProcess)) {
249     CreateExtensionProcessHost(host, data, runtime_variables.Pass());
250   } else if (!external_extensions_path_.empty()) {
251     RegisterExternalExtensionsInDirectory(
252         data->in_process_ui_thread_server(),
253         external_extensions_path_, runtime_variables.Pass());
254   }
255
256   extension_data_map_[host->GetID()] = data;
257 }
258
259 void XWalkExtensionService::OnRenderProcessWillLaunch(
260     content::RenderProcessHost* host,
261     XWalkExtensionVector* ui_thread_extensions,
262     XWalkExtensionVector* extension_thread_extensions,
263     scoped_ptr<base::ValueMap> runtime_variables) {
264   CHECK(host);
265
266   if (!g_external_extensions_path_for_testing_.empty()) {
267     (*runtime_variables)["runtime_name"] =
268         base::Value::CreateStringValue("xwalk");
269     OnRenderProcessHostCreatedInternal(host, ui_thread_extensions,
270         extension_thread_extensions, runtime_variables.Pass());
271     return;
272   }
273
274   OnRenderProcessHostCreatedInternal(host, ui_thread_extensions,
275       extension_thread_extensions, runtime_variables.Pass());
276 }
277
278 // static
279 void
280 XWalkExtensionService::SetCreateExtensionThreadExtensionsCallbackForTesting(
281     const CreateExtensionsCallback& callback) {
282   g_create_extension_thread_extensions_callback = callback;
283 }
284
285 // static
286 void
287 XWalkExtensionService::SetCreateUIThreadExtensionsCallbackForTesting(
288     const CreateExtensionsCallback& callback) {
289   g_create_ui_thread_extensions_callback = callback;
290 }
291
292 // static
293 void XWalkExtensionService::SetExternalExtensionsPathForTesting(
294     const base::FilePath& path) {
295   g_external_extensions_path_for_testing_ = path;
296 }
297
298 // We use this to keep track of the RenderProcess shutdown events.
299 // This is _very_ important so we can clean up all we need gracefully,
300 // avoiding invalid IPC steps after the IPC channel is gonne.
301 void XWalkExtensionService::Observe(int type,
302                               const content::NotificationSource& source,
303                               const content::NotificationDetails& details) {
304   switch (type) {
305     case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
306     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
307       content::RenderProcessHost* rph =
308           content::Source<content::RenderProcessHost>(source).ptr();
309       OnRenderProcessHostClosed(rph);
310     }
311   }
312 }
313
314 void XWalkExtensionService::OnRenderProcessHostClosed(
315     content::RenderProcessHost* host) {
316   RenderProcessToExtensionDataMap::iterator it =
317       extension_data_map_.find(host->GetID());
318
319   if (it == extension_data_map_.end())
320     return;
321
322   XWalkExtensionData* data = it->second;
323
324   // Invalidate the objects in the different threads so they stop posting
325   // messages to each other. This is important because we'll schedule the
326   // deletion of both objects to their respective threads.
327   ExtensionServerMessageFilter* message_filter =
328       data->in_process_message_filter();
329   CHECK(message_filter);
330
331   message_filter->Invalidate();
332
333   // This will cause the filter to be deleted in the IO-thread.
334   host->GetChannel()->RemoveFilter(message_filter);
335
336   extension_data_map_.erase(it);
337   delete data;
338 }
339
340 namespace {
341
342 void RegisterExtensionsIntoServer(XWalkExtensionVector* extensions,
343                                   XWalkExtensionServer* server) {
344   XWalkExtensionVector::iterator it = extensions->begin();
345   for (; it != extensions->end(); ++it) {
346     std::string name = (*it)->name();
347     if (!server->RegisterExtension(scoped_ptr<XWalkExtension>(*it))) {
348       LOG(WARNING) << "Couldn't register extension with name '"
349                    << name << "'\n";
350     }
351   }
352   extensions->clear();
353 }
354
355 }  // namespace
356
357
358 void XWalkExtensionService::CreateInProcessExtensionServers(
359     content::RenderProcessHost* host, XWalkExtensionData* data,
360     XWalkExtensionVector* ui_thread_extensions,
361     XWalkExtensionVector* extension_thread_extensions) {
362   scoped_ptr<XWalkExtensionServer> extension_thread_server(
363       new XWalkExtensionServer);
364   scoped_ptr<XWalkExtensionServer> ui_thread_server(
365       new XWalkExtensionServer);
366
367   IPC::ChannelProxy* channel = host->GetChannel();
368
369   extension_thread_server->Initialize(channel);
370   ui_thread_server->Initialize(channel);
371
372   RegisterExtensionsIntoServer(extension_thread_extensions,
373                                extension_thread_server.get());
374   RegisterExtensionsIntoServer(ui_thread_extensions, ui_thread_server.get());
375
376   if (!g_create_ui_thread_extensions_callback.is_null()) {
377     XWalkExtensionVector extensions;
378     g_create_ui_thread_extensions_callback.Run(&extensions);
379     RegisterExtensionsIntoServer(&extensions, ui_thread_server.get());
380   }
381
382   if (!g_create_extension_thread_extensions_callback.is_null()) {
383     XWalkExtensionVector extensions;
384     g_create_extension_thread_extensions_callback.Run(&extensions);
385     RegisterExtensionsIntoServer(&extensions, extension_thread_server.get());
386   }
387
388   ExtensionServerMessageFilter* message_filter =
389       new ExtensionServerMessageFilter(extension_thread_.message_loop_proxy(),
390                                        extension_thread_server.get(),
391                                        ui_thread_server.get());
392
393   // The filter is owned by the IPC channel but we keep a reference to remove
394   // it from the Channel later during a RenderProcess shutdown.
395   data->set_in_process_message_filter(message_filter);
396   channel->AddFilter(message_filter);
397
398   data->set_in_process_extension_thread_server(extension_thread_server.Pass());
399   data->set_in_process_ui_thread_server(ui_thread_server.Pass());
400
401   data->set_extension_thread(&extension_thread_);
402 }
403
404 void XWalkExtensionService::CreateExtensionProcessHost(
405     content::RenderProcessHost* host, XWalkExtensionData* data,
406     scoped_ptr<base::ValueMap> runtime_variables) {
407   data->set_extension_process_host(make_scoped_ptr(
408       new XWalkExtensionProcessHost(host, external_extensions_path_, this,
409                                     runtime_variables.Pass())));
410 }
411
412 void XWalkExtensionService::OnExtensionProcessDied(
413     XWalkExtensionProcessHost* eph, int render_process_id) {
414   // When this is called it means that XWalkExtensionProcessHost is about
415   // to be deleted. We should invalidate our reference to it so we avoid a
416   // segfault when trying to delete it within
417   // XWalkExtensionService::OnRenderProcessHostClosed();
418
419   RenderProcessToExtensionDataMap::iterator it =
420       extension_data_map_.find(render_process_id);
421
422   if (it == extension_data_map_.end())
423     return;
424
425   XWalkExtensionData* data = it->second;
426
427   XWalkExtensionProcessHost* stored_eph =
428       data->extension_process_host().release();
429   CHECK_EQ(stored_eph, eph);
430
431   content::RenderProcessHost* rph = data->render_process_host();
432   if (rph) {
433     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
434         base::IgnoreResult(&content::RenderProcessHost::FastShutdownIfPossible),
435         base::Unretained(rph)));
436   }
437
438   extension_data_map_.erase(it);
439   delete data;
440 }
441
442 void XWalkExtensionService::OnRenderProcessDied(
443     content::RenderProcessHost* host) {
444   RenderProcessToExtensionDataMap::iterator it =
445       extension_data_map_.find(host->GetID());
446
447   if (it == extension_data_map_.end())
448     return;
449
450   XWalkExtensionData* data = it->second;
451
452   extension_data_map_.erase(it);
453   delete data;
454 }
455
456 void XWalkExtensionService::OnExtensionProcessCreated(
457       int render_process_id,
458       const IPC::ChannelHandle channel_handle) {
459   CHECK(delegate_);
460   delegate_->ExtensionProcessCreated(render_process_id, channel_handle);
461 }
462
463 void XWalkExtensionService::OnCheckAPIAccessControl(
464     int render_process_id,
465     const std::string& extension_name,
466     const std::string& api_name,
467     const PermissionCallback& callback) {
468   CHECK(delegate_);
469   delegate_->CheckAPIAccessControl(render_process_id, extension_name,
470                                    api_name, callback);
471 }
472
473 bool XWalkExtensionService::OnRegisterPermissions(
474     int render_process_id,
475     const std::string& extension_name,
476     const std::string& perm_table) {
477   CHECK(delegate_);
478   return delegate_->RegisterPermissions(render_process_id,
479                                         extension_name, perm_table);
480 }
481
482 }  // namespace extensions
483 }  // namespace xwalk