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