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.
5 #include "xwalk/extensions/browser/xwalk_extension_service.h"
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"
26 using content::BrowserThread;
29 namespace extensions {
33 XWalkExtensionService::CreateExtensionsCallback
34 g_create_extension_thread_extensions_callback;
36 XWalkExtensionService::CreateExtensionsCallback
37 g_create_ui_thread_extensions_callback;
39 base::FilePath g_external_extensions_path_for_testing_;
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.
47 // In the case of in process extensions, we will pass the task runner of the
49 class ExtensionServerMessageFilter : public IPC::ChannelProxy::MessageFilter,
52 ExtensionServerMessageFilter(
53 scoped_refptr<base::SequencedTaskRunner> task_runner,
54 XWalkExtensionServer* extension_thread_server,
55 XWalkExtensionServer* ui_thread_server)
57 task_runner_(task_runner),
58 extension_thread_server_(extension_thread_server),
59 ui_thread_server_(ui_thread_server) {}
61 // Tells the filter to stop dispatching messages to the server.
63 base::AutoLock l(lock_);
66 extension_thread_server_ = NULL;
67 ui_thread_server_ = NULL;
70 // IPC::Sender implementation.
71 virtual bool Send(IPC::Message* msg_ptr) OVERRIDE {
72 scoped_ptr<IPC::Message> msg(msg_ptr);
77 return sender_->Send(msg.release());
81 virtual ~ExtensionServerMessageFilter() {}
83 int64_t GetInstanceIDFromMessage(const IPC::Message& message) {
86 if (message.is_sync())
87 iter = IPC::SyncMessage::GetDataIterator(&message);
89 iter = PickleIterator(message);
92 if (!iter.ReadInt64(&instance_id))
98 void RouteMessageToServer(const IPC::Message& message) {
99 int64_t id = GetInstanceIDFromMessage(message);
102 XWalkExtensionServer* server;
103 base::TaskRunner* task_runner;
104 scoped_refptr<base::TaskRunner> task_runner_ref;
106 if (ContainsKey(extension_thread_instances_ids_, id)) {
107 server = extension_thread_server_;
108 task_runner = task_runner_;
110 server = ui_thread_server_;
112 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
113 task_runner = task_runner_ref.get();
116 base::Closure closure = base::Bind(
117 base::IgnoreResult(&XWalkExtensionServer::OnMessageReceived),
118 base::Unretained(server), message);
120 task_runner->PostTask(FROM_HERE, closure);
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;
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_;
133 server = ui_thread_server_;
135 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
136 task_runner = task_runner_ref.get();
139 base::Closure closure = base::Bind(
140 base::IgnoreResult(&XWalkExtensionServer::OnCreateInstance),
141 base::Unretained(server), instance_id, name);
143 task_runner->PostTask(FROM_HERE, closure);
146 void OnGetExtensions(
147 std::vector<XWalkExtensionServerMsg_ExtensionRegisterParams>* reply) {
148 extension_thread_server_->OnGetExtensions(reply);
149 ui_thread_server_->OnGetExtensions(reply);
152 // IPC::ChannelProxy::MessageFilter implementation.
153 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
157 virtual void OnFilterRemoved() OVERRIDE {
161 virtual void OnChannelClosing() OVERRIDE {
165 virtual void OnChannelError() OVERRIDE {
169 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
170 if (IPC_MESSAGE_CLASS(message) != XWalkExtensionClientServerMsgStart)
173 base::AutoLock l(lock_);
175 if (!extension_thread_server_ || !ui_thread_server_)
179 IPC_BEGIN_MESSAGE_MAP(ExtensionServerMessageFilter, message)
180 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_CreateInstance,
182 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_GetExtensions,
184 IPC_MESSAGE_UNHANDLED(handled = false)
185 IPC_END_MESSAGE_MAP()
188 RouteMessageToServer(message);
193 // This lock is used to protect access to filter members.
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_;
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());
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);
216 XWalkExtensionService::~XWalkExtensionService() {
217 // This object should have been released and asked to be deleted in the
219 if (!extension_data_map_.empty())
220 VLOG(1) << "The ExtensionData map is not empty!";
223 void XWalkExtensionService::RegisterExternalExtensionsForPath(
224 const base::FilePath& path) {
225 external_extensions_path_ = path;
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);
236 CreateInProcessExtensionServers(host, data, ui_thread_extensions,
237 extension_thread_extensions);
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);
248 extension_data_map_[host->GetID()] = data;
251 void XWalkExtensionService::OnRenderProcessWillLaunch(
252 content::RenderProcessHost* host,
253 XWalkExtensionVector* ui_thread_extensions,
254 XWalkExtensionVector* extension_thread_extensions,
255 const base::ValueMap& runtime_variables) {
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);
266 OnRenderProcessHostCreatedInternal(host, ui_thread_extensions,
267 extension_thread_extensions, runtime_variables);
272 XWalkExtensionService::SetCreateExtensionThreadExtensionsCallbackForTesting(
273 const CreateExtensionsCallback& callback) {
274 g_create_extension_thread_extensions_callback = callback;
279 XWalkExtensionService::SetCreateUIThreadExtensionsCallbackForTesting(
280 const CreateExtensionsCallback& callback) {
281 g_create_ui_thread_extensions_callback = callback;
285 void XWalkExtensionService::SetExternalExtensionsPathForTesting(
286 const base::FilePath& path) {
287 g_external_extensions_path_for_testing_ = path;
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) {
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);
306 void XWalkExtensionService::OnRenderProcessHostClosed(
307 content::RenderProcessHost* host) {
308 RenderProcessToExtensionDataMap::iterator it =
309 extension_data_map_.find(host->GetID());
311 if (it == extension_data_map_.end())
314 XWalkExtensionData* data = it->second;
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);
323 message_filter->Invalidate();
325 // This will cause the filter to be deleted in the IO-thread.
326 host->GetChannel()->RemoveFilter(message_filter);
328 extension_data_map_.erase(it);
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 '"
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);
359 IPC::ChannelProxy* channel = host->GetChannel();
361 extension_thread_server->Initialize(channel);
362 ui_thread_server->Initialize(channel);
364 RegisterExtensionsIntoServer(extension_thread_extensions,
365 extension_thread_server.get());
366 RegisterExtensionsIntoServer(ui_thread_extensions, ui_thread_server.get());
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());
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());
380 ExtensionServerMessageFilter* message_filter =
381 new ExtensionServerMessageFilter(extension_thread_.message_loop_proxy(),
382 extension_thread_server.get(),
383 ui_thread_server.get());
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);
390 data->set_in_process_extension_thread_server(extension_thread_server.Pass());
391 data->set_in_process_ui_thread_server(ui_thread_server.Pass());
393 data->set_extension_thread(&extension_thread_);
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)));
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();
411 RenderProcessToExtensionDataMap::iterator it =
412 extension_data_map_.find(render_process_id);
414 if (it == extension_data_map_.end())
417 XWalkExtensionData* data = it->second;
419 XWalkExtensionProcessHost* stored_eph =
420 data->extension_process_host().release();
421 CHECK_EQ(stored_eph, eph);
423 content::RenderProcessHost* rph = data->render_process_host();
425 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
426 base::IgnoreResult(&content::RenderProcessHost::FastShutdownIfPossible),
427 base::Unretained(rph)));
430 extension_data_map_.erase(it);
434 void XWalkExtensionService::OnRenderProcessDied(
435 content::RenderProcessHost* host) {
436 RenderProcessToExtensionDataMap::iterator it =
437 extension_data_map_.find(host->GetID());
439 if (it == extension_data_map_.end())
442 XWalkExtensionData* data = it->second;
444 extension_data_map_.erase(it);
448 void XWalkExtensionService::OnCheckAPIAccessControl(
449 const std::string& extension_name,
450 const std::string& api_name,
451 const PermissionCallback& callback) {
453 delegate_->CheckAPIAccessControl(extension_name, api_name, callback);
456 bool XWalkExtensionService::OnRegisterPermissions(
457 const std::string& extension_name,
458 const std::string& perm_table) {
460 return delegate_->RegisterPermissions(extension_name, perm_table);
463 } // namespace extensions