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 bool XWalkExtensionService::Delegate::RegisterPermissions(
204 int render_process_id,
205 const std::string& extension_name,
206 const std::string& perm_table) {
210 XWalkExtensionService::XWalkExtensionService(Delegate* delegate)
211 : extension_thread_("XWalkExtensionThread"),
212 delegate_(delegate) {
213 if (!g_external_extensions_path_for_testing_.empty())
214 external_extensions_path_ = g_external_extensions_path_for_testing_;
215 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
216 content::NotificationService::AllBrowserContextsAndSources());
218 // IO main loop is needed by extensions watching file descriptors events.
219 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
220 extension_thread_.StartWithOptions(options);
223 XWalkExtensionService::~XWalkExtensionService() {
224 // This object should have been released and asked to be deleted in the
226 if (!extension_data_map_.empty())
227 VLOG(1) << "The ExtensionData map is not empty!";
230 void XWalkExtensionService::RegisterExternalExtensionsForPath(
231 const base::FilePath& path) {
232 external_extensions_path_ = path;
235 void XWalkExtensionService::OnRenderProcessHostCreatedInternal(
236 content::RenderProcessHost* host,
237 XWalkExtensionVector* ui_thread_extensions,
238 XWalkExtensionVector* extension_thread_extensions,
239 const base::ValueMap& runtime_variables) {
240 XWalkExtensionData* data = new XWalkExtensionData;
241 data->set_render_process_host(host);
243 CreateInProcessExtensionServers(host, data, ui_thread_extensions,
244 extension_thread_extensions);
246 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
247 if (!cmd_line->HasSwitch(switches::kXWalkDisableExtensionProcess))
248 CreateExtensionProcessHost(host, data, runtime_variables);
249 else if (!external_extensions_path_.empty()) {
250 RegisterExternalExtensionsInDirectory(
251 data->in_process_ui_thread_server(),
252 external_extensions_path_, runtime_variables);
255 extension_data_map_[host->GetID()] = data;
258 void XWalkExtensionService::OnRenderProcessWillLaunch(
259 content::RenderProcessHost* host,
260 XWalkExtensionVector* ui_thread_extensions,
261 XWalkExtensionVector* extension_thread_extensions,
262 const base::ValueMap& runtime_variables) {
265 if (!g_external_extensions_path_for_testing_.empty()) {
266 base::ValueMap test_variables;
267 test_variables["runtime_name"] = base::Value::CreateStringValue("xwalk");
268 OnRenderProcessHostCreatedInternal(host, ui_thread_extensions,
269 extension_thread_extensions, test_variables);
273 OnRenderProcessHostCreatedInternal(host, ui_thread_extensions,
274 extension_thread_extensions, runtime_variables);
279 XWalkExtensionService::SetCreateExtensionThreadExtensionsCallbackForTesting(
280 const CreateExtensionsCallback& callback) {
281 g_create_extension_thread_extensions_callback = callback;
286 XWalkExtensionService::SetCreateUIThreadExtensionsCallbackForTesting(
287 const CreateExtensionsCallback& callback) {
288 g_create_ui_thread_extensions_callback = callback;
292 void XWalkExtensionService::SetExternalExtensionsPathForTesting(
293 const base::FilePath& path) {
294 g_external_extensions_path_for_testing_ = path;
297 // We use this to keep track of the RenderProcess shutdown events.
298 // This is _very_ important so we can clean up all we need gracefully,
299 // avoiding invalid IPC steps after the IPC channel is gonne.
300 void XWalkExtensionService::Observe(int type,
301 const content::NotificationSource& source,
302 const content::NotificationDetails& details) {
304 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
305 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
306 content::RenderProcessHost* rph =
307 content::Source<content::RenderProcessHost>(source).ptr();
308 OnRenderProcessHostClosed(rph);
313 void XWalkExtensionService::OnRenderProcessHostClosed(
314 content::RenderProcessHost* host) {
315 RenderProcessToExtensionDataMap::iterator it =
316 extension_data_map_.find(host->GetID());
318 if (it == extension_data_map_.end())
321 XWalkExtensionData* data = it->second;
323 // Invalidate the objects in the different threads so they stop posting
324 // messages to each other. This is important because we'll schedule the
325 // deletion of both objects to their respective threads.
326 ExtensionServerMessageFilter* message_filter =
327 data->in_process_message_filter();
328 CHECK(message_filter);
330 message_filter->Invalidate();
332 // This will cause the filter to be deleted in the IO-thread.
333 host->GetChannel()->RemoveFilter(message_filter);
335 extension_data_map_.erase(it);
341 void RegisterExtensionsIntoServer(XWalkExtensionVector* extensions,
342 XWalkExtensionServer* server) {
343 XWalkExtensionVector::iterator it = extensions->begin();
344 for (; it != extensions->end(); ++it) {
345 std::string name = (*it)->name();
346 if (!server->RegisterExtension(scoped_ptr<XWalkExtension>(*it))) {
347 LOG(WARNING) << "Couldn't register extension with name '"
357 void XWalkExtensionService::CreateInProcessExtensionServers(
358 content::RenderProcessHost* host, XWalkExtensionData* data,
359 XWalkExtensionVector* ui_thread_extensions,
360 XWalkExtensionVector* extension_thread_extensions) {
361 scoped_ptr<XWalkExtensionServer> extension_thread_server(
362 new XWalkExtensionServer);
363 scoped_ptr<XWalkExtensionServer> ui_thread_server(
364 new XWalkExtensionServer);
366 IPC::ChannelProxy* channel = host->GetChannel();
368 extension_thread_server->Initialize(channel);
369 ui_thread_server->Initialize(channel);
371 RegisterExtensionsIntoServer(extension_thread_extensions,
372 extension_thread_server.get());
373 RegisterExtensionsIntoServer(ui_thread_extensions, ui_thread_server.get());
375 if (!g_create_ui_thread_extensions_callback.is_null()) {
376 XWalkExtensionVector extensions;
377 g_create_ui_thread_extensions_callback.Run(&extensions);
378 RegisterExtensionsIntoServer(&extensions, ui_thread_server.get());
381 if (!g_create_extension_thread_extensions_callback.is_null()) {
382 XWalkExtensionVector extensions;
383 g_create_extension_thread_extensions_callback.Run(&extensions);
384 RegisterExtensionsIntoServer(&extensions, extension_thread_server.get());
387 ExtensionServerMessageFilter* message_filter =
388 new ExtensionServerMessageFilter(extension_thread_.message_loop_proxy(),
389 extension_thread_server.get(),
390 ui_thread_server.get());
392 // The filter is owned by the IPC channel but we keep a reference to remove
393 // it from the Channel later during a RenderProcess shutdown.
394 data->set_in_process_message_filter(message_filter);
395 channel->AddFilter(message_filter);
397 data->set_in_process_extension_thread_server(extension_thread_server.Pass());
398 data->set_in_process_ui_thread_server(ui_thread_server.Pass());
400 data->set_extension_thread(&extension_thread_);
403 void XWalkExtensionService::CreateExtensionProcessHost(
404 content::RenderProcessHost* host, XWalkExtensionData* data,
405 const base::ValueMap& runtime_variables) {
406 data->set_extension_process_host(make_scoped_ptr(
407 new XWalkExtensionProcessHost(host, external_extensions_path_, this,
408 runtime_variables)));
411 void XWalkExtensionService::OnExtensionProcessDied(
412 XWalkExtensionProcessHost* eph, int render_process_id) {
413 // When this is called it means that XWalkExtensionProcessHost is about
414 // to be deleted. We should invalidate our reference to it so we avoid a
415 // segfault when trying to delete it within
416 // XWalkExtensionService::OnRenderProcessHostClosed();
418 RenderProcessToExtensionDataMap::iterator it =
419 extension_data_map_.find(render_process_id);
421 if (it == extension_data_map_.end())
424 XWalkExtensionData* data = it->second;
426 XWalkExtensionProcessHost* stored_eph =
427 data->extension_process_host().release();
428 CHECK_EQ(stored_eph, eph);
430 content::RenderProcessHost* rph = data->render_process_host();
432 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
433 base::IgnoreResult(&content::RenderProcessHost::FastShutdownIfPossible),
434 base::Unretained(rph)));
437 extension_data_map_.erase(it);
441 void XWalkExtensionService::OnRenderProcessDied(
442 content::RenderProcessHost* host) {
443 RenderProcessToExtensionDataMap::iterator it =
444 extension_data_map_.find(host->GetID());
446 if (it == extension_data_map_.end())
449 XWalkExtensionData* data = it->second;
451 extension_data_map_.erase(it);
455 void XWalkExtensionService::OnCheckAPIAccessControl(
456 int render_process_id,
457 const std::string& extension_name,
458 const std::string& api_name,
459 const PermissionCallback& callback) {
461 delegate_->CheckAPIAccessControl(render_process_id, extension_name,
465 bool XWalkExtensionService::OnRegisterPermissions(
466 int render_process_id,
467 const std::string& extension_name,
468 const std::string& perm_table) {
470 return delegate_->RegisterPermissions(render_process_id,
471 extension_name, perm_table);
474 } // namespace extensions