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/common/xwalk_extension_server.h"
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"
21 namespace extensions {
23 // Threshold to determine using shared memory or message
24 const size_t kInlineMessageMaxSize = 256 * 1024;
26 XWalkExtensionServer::XWalkExtensionServer()
28 renderer_process_handle_(base::kNullProcessHandle),
29 permissions_delegate_(NULL) {}
31 XWalkExtensionServer::~XWalkExtensionServer() {
33 STLDeleteValues(&extensions_);
36 bool XWalkExtensionServer::OnMessageReceived(const IPC::Message& message) {
38 IPC_BEGIN_MESSAGE_MAP(XWalkExtensionServer, message)
39 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_CreateInstance,
41 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_DestroyInstance,
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,
50 IPC_MESSAGE_UNHANDLED(handled = false)
56 void XWalkExtensionServer::OnChannelConnected(int32 peer_pid) {
57 CHECK(base::OpenProcessHandle(peer_pid, &renderer_process_handle_));
60 void XWalkExtensionServer::OnCreateInstance(int64_t instance_id,
62 ExtensionMap::const_iterator it = extensions_.find(name);
64 if (it == extensions_.end()) {
65 LOG(WARNING) << "Can't create instance of extension: " << name
66 << ". Extension is not registered.";
70 XWalkExtensionInstance* instance = it->second->CreateInstance();
72 LOG(WARNING) << "Can't create instance of extension: " << name
73 << ". CreateInstance() return invalid pointer.";
77 instance->SetPostMessageCallback(
78 base::Bind(&XWalkExtensionServer::PostMessageToJSCallback,
79 base::Unretained(this), instance_id));
81 instance->SetSendSyncReplyCallback(
82 base::Bind(&XWalkExtensionServer::SendSyncReplyToJSCallback,
83 base::Unretained(this), instance_id));
85 InstanceExecutionData data;
86 data.instance = instance;
87 data.pending_reply = NULL;
89 instances_[instance_id] = data;
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: "
101 const InstanceExecutionData& data = it->second;
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());
114 void XWalkExtensionServer::Initialize(IPC::Sender* sender) {
115 base::AutoLock l(sender_lock_);
120 bool XWalkExtensionServer::Send(IPC::Message* msg) {
121 base::AutoLock l(sender_lock_);
124 return sender_->Send(msg);
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) {
134 if (IsAsciiDigit(c)) {
135 if (!digit_or_underscore_allowed)
137 } else if (c == '_') {
138 if (!digit_or_underscore_allowed)
140 } else if (c == '.') {
144 digit_or_underscore_allowed = false;
145 } else if (IsAsciiAlpha(c)) {
147 digit_or_underscore_allowed = true;
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.
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();
168 if (ContainsKey(extension_symbols_, extension->name())) {
169 LOG(WARNING) << "Ignoring extension with name already registered: "
170 << extension->name();
174 if (!ValidateExtensionEntryPoints(extension->entry_points())) {
175 LOG(WARNING) << "Ignoring extension '" << extension->name()
176 << "' with invalid entry point.";
180 const base::ListValue& entry_points = extension->entry_points();
181 base::ListValue::const_iterator it = entry_points.begin();
183 for (; it != entry_points.end(); ++it) {
184 std::string entry_point;
185 (*it)->GetAsString(&entry_point);
186 extension_symbols_.insert(entry_point);
189 std::string name = extension->name();
190 extension_symbols_.insert(name);
191 extensions_[name] = extension.release();
195 bool XWalkExtensionServer::ContainsExtension(
196 const std::string& extension_name) const {
197 return ContainsKey(extensions_, extension_name);
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());
205 scoped_ptr<IPC::Message> message(
206 new XWalkExtensionClientMsg_PostMessageToJS(instance_id, wrapped_msg));
207 if (message->size() <= kInlineMessageMaxSize) {
208 Send(message.release());
212 base::SharedMemoryCreateOptions options;
213 options.size = message->size();
214 options.share_read_only = true;
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";
222 memcpy(shared_memory.memory(), message->data(), message->size());
224 base::SharedMemoryHandle handle;
225 shared_memory.GiveReadOnlyToProcess(renderer_process_handle_, &handle);
227 Send(new XWalkExtensionClientMsg_PostOutOfLineMessageToJS(handle,
231 void XWalkExtensionServer::SendSyncReplyToJSCallback(
232 int64_t instance_id, scoped_ptr<base::Value> reply) {
234 InstanceMap::iterator it = instances_.find(instance_id);
235 if (it == instances_.end()) {
236 LOG(WARNING) << "Can't SendSyncMessage to invalid Extension instance id: "
241 InstanceExecutionData& data = it->second;
242 if (!data.pending_reply) {
243 LOG(WARNING) << "There's no pending SyncMessage for instance id: "
248 base::ListValue wrapped_reply;
249 wrapped_reply.Append(reply.release());
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);
259 data.pending_reply = NULL;
262 void XWalkExtensionServer::DeleteInstanceMap() {
263 InstanceMap::iterator it = instances_.begin();
264 int pending_replies_left = 0;
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;
276 if (pending_replies_left > 0) {
277 LOG(WARNING) << pending_replies_left
278 << " pending replies left when destroying server.";
282 bool XWalkExtensionServer::ValidateExtensionEntryPoints(
283 const base::ListValue& entry_points) {
284 base::ListValue::const_iterator it = entry_points.begin();
286 for (; it != entry_points.end(); ++it) {
287 std::string entry_point;
289 (*it)->GetAsString(&entry_point);
291 if (!ValidateExtensionIdentifier(entry_point))
294 if (ContainsKey(extension_symbols_, entry_point)) {
295 LOG(WARNING) << "Entry point '" << entry_point
296 << "' clashes with another extension entry point.";
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: "
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;
320 data.pending_reply = ipc_reply;
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;
332 instance->HandleSyncMessage(value.Pass());
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;
342 InstanceExecutionData& data = it->second;
344 delete data.instance;
345 instances_.erase(it);
347 Send(new XWalkExtensionClientMsg_InstanceDestroyed(instance_id));
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;
357 extension_parameters.name = extension->name();
358 extension_parameters.js_api = extension->javascript_api();
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);
368 reply->push_back(extension_parameters);
372 void XWalkExtensionServer::Invalidate() {
373 base::AutoLock l(sender_lock_);
378 base::FilePath::StringType GetNativeLibraryPattern() {
379 const base::string16 library_pattern = base::GetNativeLibraryName(
380 base::UTF8ToUTF16("*"));
382 return library_pattern;
384 return base::UTF16ToUTF8(library_pattern);
389 std::vector<std::string> RegisterExternalExtensionsInDirectory(
390 XWalkExtensionServer* server, const base::FilePath& dir,
391 scoped_ptr<base::ValueMap> runtime_variables) {
394 std::vector<std::string> registered_extensions;
396 if (!base::DirectoryExists(dir)) {
397 LOG(WARNING) << "Couldn't load external extensions from non-existent"
398 << " directory " << dir.AsUTF8Unsafe();
399 return registered_extensions;
402 base::FileEnumerator libraries(
403 dir, false, base::FileEnumerator::FILES, GetNativeLibraryPattern());
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));
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());
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>());
423 LOG(WARNING) << "Failed to initialize extension: "
424 << extension_path.AsUTF8Unsafe();
428 return registered_extensions;
431 bool ValidateExtensionNameForTesting(const std::string& extension_name) {
432 return ValidateExtensionIdentifier(extension_name);
435 } // namespace extensions