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/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/stl_util.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "ipc/ipc_sender.h"
15 #include "xwalk/extensions/common/xwalk_extension_messages.h"
16 #include "xwalk/extensions/common/xwalk_external_extension.h"
19 namespace extensions {
21 XWalkExtensionServer::XWalkExtensionServer()
23 permissions_delegate_(NULL) {}
25 XWalkExtensionServer::~XWalkExtensionServer() {
27 STLDeleteValues(&extensions_);
30 bool XWalkExtensionServer::OnMessageReceived(const IPC::Message& message) {
32 IPC_BEGIN_MESSAGE_MAP(XWalkExtensionServer, message)
33 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_CreateInstance,
35 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_DestroyInstance,
37 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_PostMessageToNative,
38 OnPostMessageToNative)
39 IPC_MESSAGE_HANDLER_DELAY_REPLY(
40 XWalkExtensionServerMsg_SendSyncMessageToNative,
41 OnSendSyncMessageToNative)
42 IPC_MESSAGE_HANDLER(XWalkExtensionServerMsg_GetExtensions,
44 IPC_MESSAGE_UNHANDLED(handled = false)
50 void XWalkExtensionServer::OnCreateInstance(int64_t instance_id,
52 ExtensionMap::const_iterator it = extensions_.find(name);
54 if (it == extensions_.end()) {
55 LOG(WARNING) << "Can't create instance of extension: " << name
56 << ". Extension is not registered.";
60 XWalkExtensionInstance* instance = it->second->CreateInstance();
61 instance->SetPostMessageCallback(
62 base::Bind(&XWalkExtensionServer::PostMessageToJSCallback,
63 base::Unretained(this), instance_id));
65 instance->SetSendSyncReplyCallback(
66 base::Bind(&XWalkExtensionServer::SendSyncReplyToJSCallback,
67 base::Unretained(this), instance_id));
69 InstanceExecutionData data;
70 data.instance = instance;
71 data.pending_reply = NULL;
73 instances_[instance_id] = data;
76 void XWalkExtensionServer::OnPostMessageToNative(int64_t instance_id,
77 const base::ListValue& msg) {
78 InstanceMap::const_iterator it = instances_.find(instance_id);
79 if (it == instances_.end()) {
80 LOG(WARNING) << "Can't PostMessage to invalid Extension instance id: "
85 const InstanceExecutionData& data = it->second;
87 // The const_cast is needed to remove the only Value contained by the
88 // ListValue (which is solely used as wrapper, since Value doesn't
89 // have param traits for serialization) and we pass the ownership to to
90 // HandleMessage. It is safe to do this because the |msg| won't be used
91 // anywhere else when this function returns. Saves a DeepCopy(), which
92 // can be costly depending on the size of Value.
93 scoped_ptr<base::Value> value;
94 const_cast<base::ListValue*>(&msg)->Remove(0, &value);
95 data.instance->HandleMessage(value.Pass());
98 void XWalkExtensionServer::Initialize(IPC::Sender* sender) {
99 base::AutoLock l(sender_lock_);
104 bool XWalkExtensionServer::Send(IPC::Message* msg) {
105 base::AutoLock l(sender_lock_);
108 return sender_->Send(msg);
113 bool ValidateExtensionIdentifier(const std::string& name) {
114 bool dot_allowed = false;
115 bool digit_or_underscore_allowed = false;
116 for (size_t i = 0; i < name.size(); ++i) {
118 if (IsAsciiDigit(c)) {
119 if (!digit_or_underscore_allowed)
121 } else if (c == '_') {
122 if (!digit_or_underscore_allowed)
124 } else if (c == '.') {
128 digit_or_underscore_allowed = false;
129 } else if (IsAsciiAlpha(c)) {
131 digit_or_underscore_allowed = true;
137 // If after going through the entire name we finish with dot_allowed, it means
138 // the previous character is not a dot, so it's a valid name.
144 bool XWalkExtensionServer::RegisterExtension(
145 scoped_ptr<XWalkExtension> extension) {
146 if (!ValidateExtensionIdentifier(extension->name())) {
147 LOG(WARNING) << "Ignoring extension with invalid name: "
148 << extension->name();
152 if (ContainsKey(extension_symbols_, extension->name())) {
153 LOG(WARNING) << "Ignoring extension with name already registered: "
154 << extension->name();
158 if (!ValidateExtensionEntryPoints(extension->entry_points())) {
159 LOG(WARNING) << "Ignoring extension '" << extension->name()
160 << "' with invalid entry point.";
164 const base::ListValue& entry_points = extension->entry_points();
165 base::ListValue::const_iterator it = entry_points.begin();
167 for (; it != entry_points.end(); ++it) {
168 std::string entry_point;
169 (*it)->GetAsString(&entry_point);
170 extension_symbols_.insert(entry_point);
173 std::string name = extension->name();
174 extension_symbols_.insert(name);
175 extensions_[name] = extension.release();
179 bool XWalkExtensionServer::ContainsExtension(
180 const std::string& extension_name) const {
181 return ContainsKey(extensions_, extension_name);
184 void XWalkExtensionServer::PostMessageToJSCallback(
185 int64_t instance_id, scoped_ptr<base::Value> msg) {
186 base::ListValue wrapped_msg;
187 wrapped_msg.Append(msg.release());
188 Send(new XWalkExtensionClientMsg_PostMessageToJS(instance_id, wrapped_msg));
191 void XWalkExtensionServer::SendSyncReplyToJSCallback(
192 int64_t instance_id, scoped_ptr<base::Value> reply) {
194 InstanceMap::iterator it = instances_.find(instance_id);
195 if (it == instances_.end()) {
196 LOG(WARNING) << "Can't SendSyncMessage to invalid Extension instance id: "
201 InstanceExecutionData& data = it->second;
202 if (!data.pending_reply) {
203 LOG(WARNING) << "There's no pending SyncMessage for instance id: "
208 base::ListValue wrapped_reply;
209 wrapped_reply.Append(reply.release());
211 // TODO(cmarcelo): we need to inline WriteReplyParams here because it takes
212 // a copy of the parameter and ListValue is noncopyable. This may be
213 // improved in ipc_message_utils.h so we don't need to inline the code here.
214 XWalkExtensionServerMsg_SendSyncMessageToNative::ReplyParam
215 reply_param(wrapped_reply);
216 IPC::WriteParam(data.pending_reply, reply_param);
217 Send(data.pending_reply);
219 data.pending_reply = NULL;
222 void XWalkExtensionServer::DeleteInstanceMap() {
223 InstanceMap::iterator it = instances_.begin();
224 int pending_replies_left = 0;
226 for (; it != instances_.end(); ++it) {
227 delete it->second.instance;
228 if (it->second.pending_reply) {
229 pending_replies_left++;
230 delete it->second.pending_reply;
236 if (pending_replies_left > 0) {
237 LOG(WARNING) << pending_replies_left
238 << " pending replies left when destroying server.";
242 bool XWalkExtensionServer::ValidateExtensionEntryPoints(
243 const base::ListValue& entry_points) {
244 base::ListValue::const_iterator it = entry_points.begin();
246 for (; it != entry_points.end(); ++it) {
247 std::string entry_point;
249 (*it)->GetAsString(&entry_point);
251 if (!ValidateExtensionIdentifier(entry_point))
254 if (ContainsKey(extension_symbols_, entry_point)) {
255 LOG(WARNING) << "Entry point '" << entry_point
256 << "' clashes with another extension entry point.";
264 void XWalkExtensionServer::OnSendSyncMessageToNative(int64_t instance_id,
265 const base::ListValue& msg, IPC::Message* ipc_reply) {
266 InstanceMap::iterator it = instances_.find(instance_id);
267 if (it == instances_.end()) {
268 LOG(WARNING) << "Can't SendSyncMessage to invalid Extension instance id: "
273 InstanceExecutionData& data = it->second;
274 if (data.pending_reply) {
275 LOG(WARNING) << "There's already a pending Sync Message for "
276 << "Extension instance id: " << instance_id;
280 data.pending_reply = ipc_reply;
282 // The const_cast is needed to remove the only Value contained by the
283 // ListValue (which is solely used as wrapper, since Value doesn't
284 // have param traits for serialization) and we pass the ownership to to
285 // HandleMessage. It is safe to do this because the |msg| won't be used
286 // anywhere else when this function returns. Saves a DeepCopy(), which
287 // can be costly depending on the size of Value.
288 scoped_ptr<base::Value> value;
289 const_cast<base::ListValue*>(&msg)->Remove(0, &value);
290 XWalkExtensionInstance* instance = data.instance;
292 instance->HandleSyncMessage(value.Pass());
295 void XWalkExtensionServer::OnDestroyInstance(int64_t instance_id) {
296 InstanceMap::iterator it = instances_.find(instance_id);
297 if (it == instances_.end()) {
298 LOG(WARNING) << "Can't destroy inexistent instance:" << instance_id;
302 InstanceExecutionData& data = it->second;
304 delete data.instance;
305 instances_.erase(it);
307 Send(new XWalkExtensionClientMsg_InstanceDestroyed(instance_id));
310 void XWalkExtensionServer::OnGetExtensions(
311 std::vector<XWalkExtensionServerMsg_ExtensionRegisterParams>* reply) {
312 ExtensionMap::iterator it = extensions_.begin();
313 for (; it != extensions_.end(); ++it) {
314 XWalkExtensionServerMsg_ExtensionRegisterParams extension_parameters;
315 XWalkExtension* extension = it->second;
317 extension_parameters.name = extension->name();
318 extension_parameters.js_api = extension->javascript_api();
320 const base::ListValue& entry_points = extension->entry_points();
321 base::ListValue::const_iterator entry_it = entry_points.begin();
322 for (; entry_it != entry_points.end(); ++entry_it) {
323 std::string entry_point;
324 (*entry_it)->GetAsString(&entry_point);
325 extension_parameters.entry_points.push_back(entry_point);
328 reply->push_back(extension_parameters);
332 void XWalkExtensionServer::Invalidate() {
333 base::AutoLock l(sender_lock_);
338 base::FilePath::StringType GetNativeLibraryPattern() {
339 const base::string16 library_pattern = base::GetNativeLibraryName(
340 base::UTF8ToUTF16("*"));
342 return library_pattern;
344 return base::UTF16ToUTF8(library_pattern);
349 std::vector<std::string> RegisterExternalExtensionsInDirectory(
350 XWalkExtensionServer* server, const base::FilePath& dir,
351 const base::ValueMap& runtime_variables) {
354 std::vector<std::string> registered_extensions;
356 if (!base::DirectoryExists(dir)) {
357 LOG(WARNING) << "Couldn't load external extensions from non-existent"
358 << " directory " << dir.AsUTF8Unsafe();
359 return registered_extensions;
362 base::FileEnumerator libraries(
363 dir, false, base::FileEnumerator::FILES, GetNativeLibraryPattern());
365 for (base::FilePath extension_path = libraries.Next();
366 !extension_path.empty(); extension_path = libraries.Next()) {
367 scoped_ptr<XWalkExternalExtension> extension(
368 new XWalkExternalExtension(extension_path));
369 extension->set_runtime_variables(runtime_variables);
370 if (server->permissions_delegate())
371 extension->set_permissions_delegate(server->permissions_delegate());
372 if (extension->Initialize()) {
373 registered_extensions.push_back(extension->name());
374 server->RegisterExtension(extension.PassAs<XWalkExtension>());
376 LOG(WARNING) << "Failed to initialize extension: "
377 << extension_path.AsUTF8Unsafe();
381 return registered_extensions;
384 bool ValidateExtensionNameForTesting(const std::string& extension_name) {
385 return ValidateExtensionIdentifier(extension_name);
388 } // namespace extensions