Upstream version 8.36.169.0
[platform/framework/web/crosswalk.git] / src / xwalk / extensions / common / xwalk_extension_server.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/common/xwalk_extension_server.h"
6
7 #include "base/file_util.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/stl_util.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "ipc/ipc_message.h"
16 #include "ipc/ipc_sender.h"
17 #include "xwalk/extensions/common/xwalk_extension_messages.h"
18 #include "xwalk/extensions/common/xwalk_external_extension.h"
19
20 namespace xwalk {
21 namespace extensions {
22
23 // Threshold to determine using shared memory or message
24 const size_t kInlineMessageMaxSize = 256 * 1024;
25
26 XWalkExtensionServer::XWalkExtensionServer()
27     : sender_(NULL),
28       renderer_process_handle_(base::kNullProcessHandle),
29       permissions_delegate_(NULL) {}
30
31 XWalkExtensionServer::~XWalkExtensionServer() {
32   DeleteInstanceMap();
33   STLDeleteValues(&extensions_);
34 }
35
36 bool XWalkExtensionServer::OnMessageReceived(const IPC::Message& message) {
37   bool handled = true;
38   IPC_BEGIN_MESSAGE_MAP(XWalkExtensionServer, message)
39     IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_CreateInstance,
40         OnCreateInstance)
41     IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_DestroyInstance,
42         OnDestroyInstance)
43     IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_PostMessageToNative,
44         OnPostMessageToNative)
45     IPC_MESSAGE_HANDLER_DELAY_REPLY(
46         XWalkExtensionServerMsg_SendSyncMessageToNative,
47         OnSendSyncMessageToNative)
48     IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_GetExtensions,
49         OnGetExtensions)
50     IPC_MESSAGE_UNHANDLED(handled = false)
51   IPC_END_MESSAGE_MAP()
52
53   return handled;
54 }
55
56 void XWalkExtensionServer::OnChannelConnected(int32 peer_pid) {
57   CHECK(base::OpenProcessHandle(peer_pid, &renderer_process_handle_));
58 }
59
60 void XWalkExtensionServer::OnCreateInstance(int64_t instance_id,
61     std::string name) {
62   ExtensionMap::const_iterator it = extensions_.find(name);
63
64   if (it == extensions_.end()) {
65     LOG(WARNING) << "Can't create instance of extension: " << name
66         << ". Extension is not registered.";
67     return;
68   }
69
70   XWalkExtensionInstance* instance = it->second->CreateInstance();
71   instance->SetPostMessageCallback(
72       base::Bind(&XWalkExtensionServer::PostMessageToJSCallback,
73                  base::Unretained(this), instance_id));
74
75   instance->SetSendSyncReplyCallback(
76       base::Bind(&XWalkExtensionServer::SendSyncReplyToJSCallback,
77                  base::Unretained(this), instance_id));
78
79   InstanceExecutionData data;
80   data.instance = instance;
81   data.pending_reply = NULL;
82
83   instances_[instance_id] = data;
84 }
85
86 void XWalkExtensionServer::OnPostMessageToNative(int64_t instance_id,
87     const base::ListValue& msg) {
88   InstanceMap::const_iterator it = instances_.find(instance_id);
89   if (it == instances_.end()) {
90     LOG(WARNING) << "Can't PostMessage to invalid Extension instance id: "
91                  << instance_id;
92     return;
93   }
94
95   const InstanceExecutionData& data = it->second;
96
97   // The const_cast is needed to remove the only Value contained by the
98   // ListValue (which is solely used as wrapper, since Value doesn't
99   // have param traits for serialization) and we pass the ownership to to
100   // HandleMessage. It is safe to do this because the |msg| won't be used
101   // anywhere else when this function returns. Saves a DeepCopy(), which
102   // can be costly depending on the size of Value.
103   scoped_ptr<base::Value> value;
104   const_cast<base::ListValue*>(&msg)->Remove(0, &value);
105   data.instance->HandleMessage(value.Pass());
106 }
107
108 void XWalkExtensionServer::Initialize(IPC::Sender* sender) {
109   base::AutoLock l(sender_lock_);
110   DCHECK(!sender_);
111   sender_ = sender;
112 }
113
114 bool XWalkExtensionServer::Send(IPC::Message* msg) {
115   base::AutoLock l(sender_lock_);
116   if (!sender_)
117     return false;
118   return sender_->Send(msg);
119 }
120
121 namespace {
122
123 bool ValidateExtensionIdentifier(const std::string& name) {
124   bool dot_allowed = false;
125   bool digit_or_underscore_allowed = false;
126   for (size_t i = 0; i < name.size(); ++i) {
127     char c = name[i];
128     if (IsAsciiDigit(c)) {
129       if (!digit_or_underscore_allowed)
130         return false;
131     } else if (c == '_') {
132       if (!digit_or_underscore_allowed)
133         return false;
134     } else if (c == '.') {
135       if (!dot_allowed)
136         return false;
137       dot_allowed = false;
138       digit_or_underscore_allowed = false;
139     } else if (IsAsciiAlpha(c)) {
140       dot_allowed = true;
141       digit_or_underscore_allowed = true;
142     } else {
143       return false;
144     }
145   }
146
147   // If after going through the entire name we finish with dot_allowed, it means
148   // the previous character is not a dot, so it's a valid name.
149   return dot_allowed;
150 }
151
152 }  // namespace
153
154 bool XWalkExtensionServer::RegisterExtension(
155     scoped_ptr<XWalkExtension> extension) {
156   if (!ValidateExtensionIdentifier(extension->name())) {
157     LOG(WARNING) << "Ignoring extension with invalid name: "
158                  << extension->name();
159     return false;
160   }
161
162   if (ContainsKey(extension_symbols_, extension->name())) {
163     LOG(WARNING) << "Ignoring extension with name already registered: "
164                  << extension->name();
165     return false;
166   }
167
168   if (!ValidateExtensionEntryPoints(extension->entry_points())) {
169     LOG(WARNING) << "Ignoring extension '" << extension->name()
170                  << "' with invalid entry point.";
171     return false;
172   }
173
174   const base::ListValue& entry_points = extension->entry_points();
175   base::ListValue::const_iterator it = entry_points.begin();
176
177   for (; it != entry_points.end(); ++it) {
178     std::string entry_point;
179     (*it)->GetAsString(&entry_point);
180     extension_symbols_.insert(entry_point);
181   }
182
183   std::string name = extension->name();
184   extension_symbols_.insert(name);
185   extensions_[name] = extension.release();
186   return true;
187 }
188
189 bool XWalkExtensionServer::ContainsExtension(
190     const std::string& extension_name) const {
191   return ContainsKey(extensions_, extension_name);
192 }
193
194 void XWalkExtensionServer::PostMessageToJSCallback(
195     int64_t instance_id, scoped_ptr<base::Value> msg) {
196   base::ListValue wrapped_msg;
197   wrapped_msg.Append(msg.release());
198
199   scoped_ptr<IPC::Message> message(
200       new XWalkExtensionClientMsg_PostMessageToJS(instance_id, wrapped_msg));
201   if (message->size() <= kInlineMessageMaxSize) {
202     Send(message.release());
203     return;
204   }
205
206   base::SharedMemoryCreateOptions options;
207   options.size = message->size();
208   options.share_read_only = true;
209
210   base::SharedMemory shared_memory;
211   if (!shared_memory.Create(options) || !shared_memory.Map(message->size())) {
212     LOG(WARNING) << "Can't create shared memory to send out of line message";
213     return;
214   }
215
216   memcpy(shared_memory.memory(), message->data(), message->size());
217
218   base::SharedMemoryHandle handle;
219   shared_memory.GiveReadOnlyToProcess(renderer_process_handle_, &handle);
220
221   Send(new XWalkExtensionClientMsg_PostOutOfLineMessageToJS(handle,
222                                                             message->size()));
223 }
224
225 void XWalkExtensionServer::SendSyncReplyToJSCallback(
226     int64_t instance_id, scoped_ptr<base::Value> reply) {
227
228   InstanceMap::iterator it = instances_.find(instance_id);
229   if (it == instances_.end()) {
230     LOG(WARNING) << "Can't SendSyncMessage to invalid Extension instance id: "
231                  << instance_id;
232     return;
233   }
234
235   InstanceExecutionData& data = it->second;
236   if (!data.pending_reply) {
237     LOG(WARNING) << "There's no pending SyncMessage for instance id: "
238                  << instance_id;
239     return;
240   }
241
242   base::ListValue wrapped_reply;
243   wrapped_reply.Append(reply.release());
244
245   // TODO(cmarcelo): we need to inline WriteReplyParams here because it takes
246   // a copy of the parameter and ListValue is noncopyable. This may be
247   // improved in ipc_message_utils.h so we don't need to inline the code here.
248   XWalkExtensionServerMsg_SendSyncMessageToNative::ReplyParam
249       reply_param(wrapped_reply);
250   IPC::WriteParam(data.pending_reply, reply_param);
251   Send(data.pending_reply);
252
253   data.pending_reply = NULL;
254 }
255
256 void XWalkExtensionServer::DeleteInstanceMap() {
257   InstanceMap::iterator it = instances_.begin();
258   int pending_replies_left = 0;
259
260   for (; it != instances_.end(); ++it) {
261     delete it->second.instance;
262     if (it->second.pending_reply) {
263       pending_replies_left++;
264       delete it->second.pending_reply;
265     }
266   }
267
268   instances_.clear();
269
270   if (pending_replies_left > 0) {
271     LOG(WARNING) << pending_replies_left
272                  << " pending replies left when destroying server.";
273   }
274 }
275
276 bool XWalkExtensionServer::ValidateExtensionEntryPoints(
277     const base::ListValue& entry_points) {
278   base::ListValue::const_iterator it = entry_points.begin();
279
280   for (; it != entry_points.end(); ++it) {
281     std::string entry_point;
282
283     (*it)->GetAsString(&entry_point);
284
285     if (!ValidateExtensionIdentifier(entry_point))
286       return false;
287
288     if (ContainsKey(extension_symbols_, entry_point)) {
289       LOG(WARNING) << "Entry point '" << entry_point
290                    << "' clashes with another extension entry point.";
291       return false;
292     }
293   }
294
295   return true;
296 }
297
298 void XWalkExtensionServer::OnSendSyncMessageToNative(int64_t instance_id,
299     const base::ListValue& msg, IPC::Message* ipc_reply) {
300   InstanceMap::iterator it = instances_.find(instance_id);
301   if (it == instances_.end()) {
302     LOG(WARNING) << "Can't SendSyncMessage to invalid Extension instance id: "
303                  << instance_id;
304     return;
305   }
306
307   InstanceExecutionData& data = it->second;
308   if (data.pending_reply) {
309     LOG(WARNING) << "There's already a pending Sync Message for "
310                  << "Extension instance id: " << instance_id;
311     return;
312   }
313
314   data.pending_reply = ipc_reply;
315
316   // The const_cast is needed to remove the only Value contained by the
317   // ListValue (which is solely used as wrapper, since Value doesn't
318   // have param traits for serialization) and we pass the ownership to to
319   // HandleMessage. It is safe to do this because the |msg| won't be used
320   // anywhere else when this function returns. Saves a DeepCopy(), which
321   // can be costly depending on the size of Value.
322   scoped_ptr<base::Value> value;
323   const_cast<base::ListValue*>(&msg)->Remove(0, &value);
324   XWalkExtensionInstance* instance = data.instance;
325
326   instance->HandleSyncMessage(value.Pass());
327 }
328
329 void XWalkExtensionServer::OnDestroyInstance(int64_t instance_id) {
330   InstanceMap::iterator it = instances_.find(instance_id);
331   if (it == instances_.end()) {
332     LOG(WARNING) << "Can't destroy inexistent instance:" << instance_id;
333     return;
334   }
335
336   InstanceExecutionData& data = it->second;
337
338   delete data.instance;
339   instances_.erase(it);
340
341   Send(new XWalkExtensionClientMsg_InstanceDestroyed(instance_id));
342 }
343
344 void XWalkExtensionServer::OnGetExtensions(
345     std::vector<XWalkExtensionServerMsg_ExtensionRegisterParams>* reply) {
346   ExtensionMap::iterator it = extensions_.begin();
347   for (; it != extensions_.end(); ++it) {
348     XWalkExtensionServerMsg_ExtensionRegisterParams extension_parameters;
349     XWalkExtension* extension = it->second;
350
351     extension_parameters.name = extension->name();
352     extension_parameters.js_api = extension->javascript_api();
353
354     const base::ListValue& entry_points = extension->entry_points();
355     base::ListValue::const_iterator entry_it = entry_points.begin();
356     for (; entry_it != entry_points.end(); ++entry_it) {
357       std::string entry_point;
358       (*entry_it)->GetAsString(&entry_point);
359       extension_parameters.entry_points.push_back(entry_point);
360     }
361
362     reply->push_back(extension_parameters);
363   }
364 }
365
366 void XWalkExtensionServer::Invalidate() {
367   base::AutoLock l(sender_lock_);
368   sender_ = NULL;
369 }
370
371 namespace {
372 base::FilePath::StringType GetNativeLibraryPattern() {
373   const base::string16 library_pattern = base::GetNativeLibraryName(
374       base::UTF8ToUTF16("*"));
375 #if defined(OS_WIN)
376   return library_pattern;
377 #else
378   return base::UTF16ToUTF8(library_pattern);
379 #endif
380 }
381 }  // namespace
382
383 std::vector<std::string> RegisterExternalExtensionsInDirectory(
384     XWalkExtensionServer* server, const base::FilePath& dir,
385     scoped_ptr<base::ValueMap> runtime_variables) {
386   CHECK(server);
387
388   std::vector<std::string> registered_extensions;
389
390   if (!base::DirectoryExists(dir)) {
391     LOG(WARNING) << "Couldn't load external extensions from non-existent"
392                  << " directory " << dir.AsUTF8Unsafe();
393     return registered_extensions;
394   }
395
396   base::FileEnumerator libraries(
397       dir, false, base::FileEnumerator::FILES, GetNativeLibraryPattern());
398
399   for (base::FilePath extension_path = libraries.Next();
400         !extension_path.empty(); extension_path = libraries.Next()) {
401     scoped_ptr<XWalkExternalExtension> extension(
402         new XWalkExternalExtension(extension_path));
403
404     // Let the extension know about its own path, so it can be used
405     // as an identifier in case you have symlinks to extensions to force it
406     // load multiple times.
407     (*runtime_variables)["extension_path"] =
408         base::Value::CreateStringValue(extension_path.AsUTF8Unsafe());
409
410     extension->set_runtime_variables(*runtime_variables);
411     if (server->permissions_delegate())
412       extension->set_permissions_delegate(server->permissions_delegate());
413     if (extension->Initialize()) {
414       registered_extensions.push_back(extension->name());
415       server->RegisterExtension(extension.PassAs<XWalkExtension>());
416     } else {
417       LOG(WARNING) << "Failed to initialize extension: "
418                    << extension_path.AsUTF8Unsafe();
419     }
420   }
421
422   return registered_extensions;
423 }
424
425 bool ValidateExtensionNameForTesting(const std::string& extension_name) {
426   return ValidateExtensionIdentifier(extension_name);
427 }
428
429 }  // namespace extensions
430 }  // namespace xwalk
431