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